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.
Share this post
Twitter
Google+
Facebook
Reddit
LinkedIn
StumbleUpon
Email