I am trying to implement function composition. At first I defined a function that is named compose
.
func compose<A,B,C>(f:(B -> C))(g: (A -> B)) -> A -> C {
return { f(g($0)) }
}
This works great. For example if I have not
and isEven
functions like
func not(value: Bool) -> Bool {
return !value
}
func even(value: Int) -> Bool {
return value % 2 == 0
}
The odd
function can be defined in terms of not
and even
like this:
func odd(value: Int) -> Bool {
return compose(not)(isEven)(value)
}
Then I decided to use a custom operator instead of compose
function. The custom operator is ..
. At first I just copied compose
function and changed it name to ..
. Here is how it looks like:
infix operator .. { associativity left }
func ..<A,B,C>(f:(B -> C))(g: (A -> B)) -> A -> C {
return { f(g($0)) }
}
Here Xcode gives the error: "Unary operator implementation must have a 'prefix' or 'postfix' modifier"
After that I changed the operator to this:
infix operator .. { associativity left }
func ..<A,B,C>(f: (B -> C), g: (A -> B)) -> A -> C {
return { f(g($0)) }
}
And odd
function to this:
func odd(value: Int) -> Bool {
return (not..even)(value)
}
Or as a closure:
let odd = not..even
And this code worked. Now I know maybe there is no benefit here to make ..
operator curried here but I wonder why curried operators is not allowed? For example if +
operator were defined as curried function we would make something like this:
let array = [1,2,3,4]
array.map((+1))
You're asking for something known as operator sections, or the ability to partially apply binary operators on the left or the right. Unfortunately, Swift allows only fully uncurried operators, which means you have to get a little creative. If we regard an operator as a binary function op : (A, A) -> A
, then its curried form, curry-op : A -> A -> A
, is simply a unary function returning a unary function. We can fake this with custom prefix and postfix operators that simulate right and left sectioning respectively.
Here's prefix and postfix +
prefix func +<A : protocol<IntegerLiteralConvertible, IntegerArithmeticType>>(r : A) -> (A -> A) {
return { l in l + r }
}
postfix func +<A : protocol<IntegerLiteralConvertible, IntegerArithmeticType>>(l : A) -> (A -> A) {
return { r in l + r }
}
This allows you to write code such as
let l = [Int](1...10) /// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l.map(+5) /// [6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
instead of the old
l.map({ x in x + 5 })
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With