I follow the instruction in this talk at 26:35, and try to implement and run the following code:
Result { (42, [User?.some(user)]) }
|> (fmap <<< first)(add(1))
|> (fmap <<< second <<< fmap <<< fmap <<< prop(\.favoriteFood.name)) { $0.uppercased() }
It shows an error: "the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions."
Adding types for Result
doesn't help:
let result: Result<(Int, [User?]), Error> = Result { (42, [User?.some(user)]) }
result
|> (fmap <<< first)(add(1))
|> (fmap <<< second <<< fmap <<< fmap <<< prop(\.favoriteFood.name)) { $0.uppercased() }
// still can't be compiled
It can be compiled and run if deleting one of the pipeline:
// It can be compiled if ignoring the first pipeline
Result { (42, [User?.some(user)]) }
|> (fmap <<< second <<< fmap <<< fmap <<< prop(\.favoriteFood.name)) { $0.uppercased() }
// It can be compiled if ignoring the second pipeline
Result { (42, [User?.some(user)]) }
|> (fmap <<< first)(add(1))
Questions:
- Why can the speaker Stephen Celis in the talk compile and run his code, but I can't with almost identical code?
- How could I make the above code compile without losing the flow/fluent style in functional programming way with piping and composing?
To make the above code run, you need the following implementation:
// Operators
precedencegroup Pipe {
associativity: left
higherThan: AssignmentPrecedence
}
infix operator |>: Pipe
@inlinable public func |> <A, B> (_ lhs: A, _ rhs: (A) -> B) -> B {
rhs(lhs)
}
precedencegroup Compose {
associativity: right
higherThan: Pipe
}
infix operator <<<: Compose
@inlinable public func <<< <A, B, C> (_ lhs: @escaping (B) -> C, _ rhs: @escaping (A) -> B) -> (A) -> C {
{ x in lhs(rhs(x)) }
}
// Higher order functions for composing
func fmap<A, B, E>(_ f: @escaping (A) -> B) -> (Result<A, E>) -> Result<B, E> {
{ r in r.map(f) }
}
func fmap<A, B>(_ f: @escaping (A) -> B) -> ([A]) -> [B] {
{ r in r.map(f) }
}
func fmap<A, B>(_ f: @escaping (A) -> B) -> (A?) -> B? {
{ r in r.map(f) }
}
func first<A, B, C>(_ f: @escaping (A) -> C) -> ((A, B)) -> (C, B) {
{ t2 in (f(t2.0), t2.1) }
}
func second<A, B, C>(_ f: @escaping (B) -> C) -> ((A, B)) -> (A, C) {
{ t2 in (t2.0, f(t2.1)) }
}
public func prop<Root, Value>(_ keyPath: WritableKeyPath<Root, Value>)
-> (@escaping (Value) -> Value)
-> (Root)
-> Root {
{ update in
{ root in
var copy = root
copy[keyPath: keyPath] = update(copy[keyPath: keyPath])
return copy
}
}
}
// Operation
let add: (Int) -> (Int) -> Int = { (x) in { (y) in y + x } }
and the models are:
struct Food {
var name: String
}
struct User {
var name: String
var favoriteFood: Food
}
let user = User(name: "Stephen", favoriteFood: Food(name: "Hamburgers"))