Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Curried infix operators in swift. Is it possible?

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))
like image 206
mustafa Avatar asked Feb 12 '23 13:02

mustafa


1 Answers

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 })
like image 72
CodaFi Avatar answered Apr 07 '23 06:04

CodaFi