A video series exploring functional programming and Swift.
#71 • Monday Aug 26, 2019 • Subscriber-only

Composable State Management: Higher-Order Reducers

We will explore a form of reducer composition that will take our applications to the next level. Higher-order reducers will allow us to implement broad, cross-cutting functionality on top of our applications with very little work, and without littering our application code with unnecessary logic. And, we’ll finally answer “what’s the point?!”

This episode builds on concepts introduced previously:

#71 • Monday Aug 26, 2019 • Subscriber-only

Composable State Management: Higher-Order Reducers

We will explore a form of reducer composition that will take our applications to the next level. Higher-order reducers will allow us to implement broad, cross-cutting functionality on top of our applications with very little work, and without littering our application code with unnecessary logic. And, we’ll finally answer “what’s the point?!”

This episode builds on concepts introduced previously:


Subscribe to Point‑Free

This episode is for subscribers only. To access it, and all past and future episodes, become a subscriber today!

See subscription optionsorLog in

Sign up for our weekly newsletter to be notified of new episodes, and unlock access to any subscriber-only episode of your choosing!

Sign up for free episode

Introduction

Now that we have the basics of our architecture in place we can start to explore things to do with it that unlock capabilities that were not even possible in the old way of making the app. There is a concept that we have discussed a number of times on Point-Free known as “higher-order constructions.” This is where you take some construction that you have been studying and lift it to a higher-order by considering functions that take that object as input and return that object as output. The canonical example is “higher-order functions”, which are functions that take functions as input and return functions as output.

But on Point-Free we’ve also considered “higher-order random number generators”, which were functions that took our Gen type as input and returned the Gen type as output. And we’ve considered “higher-order parsers”, which are functions that take parsers as input and return parsers as output. Each time you form one of these higher-order constructions you gain the ability to unlock something new that the vanilla constructions could not do alone.

Subscribe to Point-Free

👋 Hey there! Does this episode sound interesting? Well, then you may want to subscribe so that you get access to this episodes and more!


Exercises

  1. Create a higher-order reducer with the following signature:

    func filterActions(_ predicate: @escaping (Action) -> Bool)
      -> (@escaping (inout Value, Action) -> Void)
      -> (inout Value, Action) -> Void {
      fatalError("Unimplemented")
    }
    

    This allows you to transform any reducer into one that only listens to certain actions.

  2. Create a higher-order reducer that adds the functionality of undo to any reducer. You can start by providing new types to augument the existing state and actions of a reducer:

    struct UndoState<Value> {
      var value: Value
      var history: [Value]
      var canUndo: Bool { !self.history.isEmpty }
    }
    
    enum UndoAction<Action> {
      case action(Action)
      case undo
    }
    

    And then implement the following function to implement the functionality:

    func undo<Value, Action>(
      _ reducer: @escaping (inout Value, Action) -> Void
    ) -> (inout UndoState<Value>, UndoAction<Action>) -> Void {
      fatalError("Unimplemented")
    }
    
  3. Enhance the undo higher-order reducer so that it limits the size of the undo history.

  4. Enhance the undo higher-order reducer to also allow redoing.

  5. Add undo and redo buttons to the CounterView, and make them undo and redo only the counter actions on that screen.


References

  • Contravariance

    Brandon Williams & Stephen Celis • Monday Apr 30, 2018

    We first explored the concept of the pullback in our episode on “contravariance”, although back then we used a different name for the operation. The pullback is an instrumental form of composition that arises in certain situations, and can often be counter-intuitive at first sight.

    Let’s explore a type of composition that defies our intuitions. It appears to go in the opposite direction than we are used to. We’ll show that this composition is completely natural, hiding right in plain sight, and in fact related to the Liskov Substitution Principle.

  • Elm: A delightful language for reliable webapps

    Elm is both a pure functional language and framework for creating web applications in a declarative fashion. It was instrumental in pushing functional programming ideas into the mainstream, and demonstrating how an application could be represented by a simple pure function from state and actions to state.

  • Redux: A predictable state container for JavaScript apps.

    The idea of modeling an application’s architecture on simple reducer functions was popularized by Redux, a state management library for React, which in turn took a lot of inspiration from Elm.

  • Composable Reducers

    Brandon Williams • Tuesday Oct 10, 2017

    A talk that Brandon gave at the 2017 Functional Swift conference in Berlin. The talk contains a brief account of many of the ideas covered in our series of episodes on “Composable State Management”.

Chapters
Introduction
00:06
What’s a higher-order reducer?
01:56
Higher-order activity feeds
06:32
Higher-order logging
12:25
What’s the point?
20:09