Guowei Lv

2 minute read

The title of this episode is called “Side effects”, but in my opinion, it’s all about how to move side-effects out of the function and make the function composable.

Side effects in the body of the function

If there is some side-effects in the body of the function, we can move the side-effect into the return of the function, and let the caller deal with it.

func computeWithEffect(_ x: Int) -> Int {
    let computation = x * x + 1
    print("Computed \(computation)")
    return computation
}

We can move the stuff to print into the return type.

func computeAndPrint(_ x: Int) -> (Int, [String]) {
    let computation = x * x + 1
    return (computation, ["Computed \(computation)"])
}

This way we can do:

let (computation, logs) = computeAndPrint(2)
logs.forEach { print($0) }

But there is one problem, we cannot compose computeAndPrint with itself, because of the extra String array in the return type.

To solve this problem, we can invent a new operator >=>.

precedencegroup EffectfulComposition {
    associativity: left
    higherThan: ForwardApplication
    lowerThan: ForwardComposition
}

infix operator >=>: EffectfulComposition

func >=> <A, B, C>(
    _ f: @escaping (A) -> (B, [String]),
    _ g: @escaping (B) -> (C, [String])
) -> ((A) -> (C, [String])) {

    return { a in
        let (b, logs) = f(a)
        let (c, moreLogs) = g(b)
        return (c, logs + moreLogs)
    }
}

Well, this operator is implemented here just to solve the [String] in the return type, but it can be a general operator too.

E.g. we can do:

func >=> <A, B, C>(
    _ f: @escaping (A) -> B?,
    _ g: @escaping (B) -> C?
) -> ((A) -> C?) {

    return { a in
        guard let b = f(a) else { return nil }
        return g(b)
    }
}

So I would say the fish operator >=> can be used whenever the functions cannot be directly composed together because of their return types. It has not really much to do with Side-effects per se, but more of a tool to solve function composition problems.

comments powered by Disqus