Skip to content

["Request"] New syntax for partial application #652

Open
@ferranpujolcamins

Description

@ferranpujolcamins

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 |>.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions