Add function application to Swift's standard library

Let's consider a couple of assumptions:

  1. we don't want the possibility of extending every type with a new member;
  2. we prefer using methods instead of operators;

I'm personally neutral on both of these, but I've seem mostly these sentiments over the years within the Swift community.

Anyway, by those assumptions there is no way to add a new .apply (whatever the name, I'd probably call it .to) member to every type without compiler support.

Let's try and lose the second assumption. The |> operator is widely used and recognized as the "standard" operator for function application when the function is on the right hand side: languages like F# and Elixir basically consider this operator as their ..

Can an operator work? Not by itself, because the "member call" operator . has precedence over everything else, so |> cannot be chained with .:

let x = foo
  .bar
  |> baz
  .fiz /// this is called on `baz`, not on the whole previous chain

Incidentally, this is a problem in general in Swift (for example, using ?? in chains is cumbersome). So, |> wouldn't solve the "chainability" problem without compiler support. We would also need a ?> operator for optionals, probably in 2 forms (also with functions that return an optional) to avoid nested optionals.

Two options, both require compiler support: I'm not sure which would be harder to implement, but assuming that the impact is comparable, I'd go with .apply, that also has no problems with optional chaining.

The "anonymous member" .(f)/.{f($0)} seems a nice idea to me, but has a few more problems compared to the previous solutions:

  • it still requires compiler support, and probably more than the others;
  • it looks overkill if compared to just add an .apply member to everything;
  • could produce code that's harder to read.

Is there an option that doesn't require compiler support? Not from a Jedi.

Another thing I observed in the Swift community (on average) is the preference towards adding members to types via explicit protocol conformance, in order to avoid "polluting" a namespace (I don't particularly endorse this).

For example, a type can be extended with a Convertible protocol:

protocol Convertible {}

extension Convertible {
  func apply<A>(_ f: (Self) -> A) -> A {
    f(self)
  }
}

This can be done right now, but requires declaring explicit conformance for any type involved: a solution like this can be good if the community agrees on the fact that always declaring the explicit conformance is the way to go.

Another thing that can be done right now is just add |> to the standard library, but together with a bunch of other operators that make sense. If we only used operators in a chain, instead of methods, everything would be simpler, but |> is not enough, as shown by the need for something like ?>. We could take inspiration from Haskell operators, or Elixir's witchcraft and add at least the essential operators that represent operations like the following (I'm using §§ as placeholder operator):

A §§ (A) -> B /// function application, usually |>
A? §§ (A) -> B /// map
A? §§ ((A) -> B)? /// applicative
A? §§ (A) -> B? /// flatMap, in haskell is >>=

Once we have these in the standard library (also defined for Result, at least) we could use them in functions defined for other types.

4 Likes