Modular State Management: View State

Episode #73 • Sep 23, 2019 • Subscriber-Only

While we’ve seen that each reducer we’ve written is super modular, and we were easily able to extract each one into a separate framework, our views are still far from modular. This week we address this by considering: what does it mean to transform the state a view has access to?

View State
Introduction
00:06
Modularizing our views
00:44
Transforming a store's value
04:06
A familiar-looking function
09:23
What's in a name?
14:17
Propagating global changes locally
17:56
Focusing on view state
22:38
Till next time
27:09

Unlock This Episode

Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.

Introduction

The ease at which we modularized these reducers speaks to just how modular this architecture is by default. We didn’t have to make any striking refactors or changes to logic because the boundaries are already very clearly defined. The worst things got was the need to introduce a little extra boilerplate for a component that had more complex state.

This episode is for subscribers only.

Subscribe to Point-Free

Access this episode, plus all past and future episodes when you become a subscriber.

See plans and pricing

Already a subscriber? Log in

Exercises

  1. Another way to isolate code is to move it to its own file and mark part of it private or fileprivate. How does this kind of modularization differ from using an actual Swift module?

  2. In this episode we discussed how it was not appropriate to give the name map to the transformation we defined on Store due to the trickiness of reference types. Let’s explore defining map on another type with reference semantics.

    Previously on Point-Free, we defined a Lazy type as a struct around a function that returns a value:

    struct Lazy<A> {
      let run: () -> A
    }
    

    Upgrade this struct to a class so that we can introduce memoization. A call to run should perform the given closure and cache the return value so that any repeat calls to run can immediately return this cached value. It should behave as follows:

    import Foundation
    
    let slow = Lazy<Int> {
      sleep(1)
      return 1
    }
    slow.run() // Returns `1` after a second
    slow.run() // Returns `1` immediately
    

    From here, define map on Lazy:

    extension Lazy {
      func map<B>(_ f: @escaping (A) -> B) -> Lazy<B> {
        fatalError("Unimplemented")
      }
    }
    

    Given our discussion around map on the Store type, is it appropriate to call this function map?

  3. Sometimes it can be useful to view into a store so that it removes all access to the underlying state of the store. For example, a “debug” screen for your app could have a UI for listing out every single action in your application as buttons, and tapping the button will send the action to the store. Such a screen doesn’t need any access to the app state.

    Try building such a screen, and provide it view of the store that removes all access to the underlying app state.

  4. Write a function that transforms a Store<GlobalValue, GlobalAction> into a Store<GlobalValue, LocalAction>. That is, a function of the following signature:

    extension Store {
      func view<LocalAction>(
        /* what arguments are needed? */
        ) -> Store<Value, LocalAction> {
    
        fatalError("Unimplemented")
      }
    }
    

    What kind of data does the function need to be supplied with in addition to a store? Is this kind of transformation familiar? Does it have a name we’ve used before on Point-Free?

References

The Many Faces of Map

Brandon Williams & Stephen Celis • Monday Apr 23, 2018

Why does the map function appear in every programming language supporting “functional” concepts? And why does Swift have two map functions? We will answer these questions and show that map has many universal properties, and is in some sense unique.

Contravariance

Brandon Williams & Stephen Celis • Monday Apr 30, 2018

We first explored the concept of “positive” and “negative” position of function arguments in our contravariance episode. In this episode we describe a very simple process to determine when it is possible to define a map or pullback transformation on any function signature.

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.

Why Functional Programming Matters

John Hughes • Saturday Apr 1, 1989

A classic paper exploring what makes functional programming special. It focuses on two positive aspects that set it apart from the rest: laziness and modularity.

Access Control

Apple

This chapter of the Swift Programming Language book explains access control in depth and how it affects module imports.

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”.