Description
Description
It's annoying when I can't write a function point-free style just because I cannot partially apply the second argument of a 2-ary function (or more generally, the ith parameter of an n-ary). For example, we need the lift
function in Functor
just because we can't partially apply the function in map
in a convenient and easy way.
I propose a new syntax for partial application that extends the current one to allow partial application of any parameters in a function.
Sample usage
I propose to add a new operator <|
, that takes a function as first argument and a tuple with values and placeholders as second argument. The placeholders (a placeholder is two underscores) help determine what parameter a value is being applied to.
For partial application of the first parameter, we can also keep the current simplified syntax.
let f: (String, Int) -> Int = { s, i in i }
// Partially apply first argument
let p1CurrentSyntax /* : (Int) -> Int */ = f <| "a"
let p1NewSyntax /* : (Int) -> Int */ = f <| ("a", __)
// Partially apply second argument
let p2 /* : (String) -> Int */ = f <| (__, 1)
Note how I've written the resulting function type in comments, meaning that type inference works well with the syntax.
I chose to reverse the direction and parameter position of the current |>
because now the position of the function and the tuple of arguments resembles the order in which you normally write function application.
Potential implementation
The trick is to define a special PlaceHolder
type just so we can write .__
on our tuples.
If we also define __
as a global value we can avoid the leading dot. This is not needed (type inference also works if we write .__
). It makes the syntax nicer, but it pollutes the global namespace. What do you think?
public enum PlaceHolder {
case __
}
public let __ = PlaceHolder.__
infix operator <| : AdditionPrecedence
// Support for simplified syntax for partial application of the first parameter
public func <|<A, B, C>(_ f: @escaping (A, B) -> C, _ value: A) -> (B) -> C {
{ b in f(value, b)}
}
public func <|<A, B, C>(_ f: @escaping (A, B) -> C, _ values: (PlaceHolder, B)) -> (A) -> C {
{ a in f(a, values.1)}
}
public func <|<A, B, C>(_ f: @escaping (A, B) -> C, _ values: (A, PlaceHolder)) -> (B) -> C {
{ b in f(values.0, b)}
}
// ... implementations for higher arities
Modules
Bow
Breaking changes
None, unless we remove |>
.