Sent from my iPad

On Dec 13, 2017, at 11:13 PM, Stephen Celis <> wrote:

>> On Dec 13, 2017, at 9:53 PM, Erica Sadun <> wrote:
>> Chris L had a beautiful solution for an "Unwrappable" protocol that allowed 
>> all of the optional sugar to be extended to any type that had a biased 
>> `Wrapped` item, allowing it to be used with `Either`, `Wrapped`, etc as well 
>> as form the basis for `Optional` itself.
>> protocol Unwrappable {
>>  associatedtype Element
>>  func unwrap() -> Element?
>> }
> It would definitely be nice to make "Optional" sugar work for non-"Optional" 
> types! (I think the goal for "async"/"await" is to make it work for any 
> continuation, so it's basically Haskell "do" notation for Swift!)
> I'm not sure the "Unwrappable" protocol can handle many of the examples I 
> mentioned (accumulative errors, parallelism), though I wonder if it could be 
> designed differently to do so. The applicative structure requires just 2 
> functions[1]:
>    protocol Applicative<A>: Functor<A> {
>      static func pure(_ value: A) -> Self
>      static func <*> <B>(lhs: Self<(A) -> B>, rhs: Self) -> Self<B> 
>    }
> Such a protocol isn't possible in Swift (yet),

Thanks for jumping in and elaborating on a more general approach!  I don’t want 
to sidetrack the thread, but it actually is possible to encode higher-kindred 
types and protocols requiring them in Swift today.  It’s a bit clunky and 
requires some boilerplate but the technique is worth knowing.

Some Kotlin folks have created a pretty robust FP library using the same 

> but it could unlock this kind of sugar for everyone. Here's "Optional" 
> conformance:
>    extension Optional: Applicative<Wrapped> {
>      static func pure(_ value: Wrapped) -> Wrapped? {
>        return value // promoted to Optional.some
>      }
>      static func <*> <B>(lhs: Optional<(Wrapped) -> B>, rhs: Optional) -> B? {
>        guard let lhs = lhs, rhs = rhs else { return nil }
>        return lhs(rhs)
>      }
>    }
> We can't conform to such an "Applicative" protocol today, but we _can_ still 
> write and use these functions in a concrete manner! (Delete the conformance 
> and see!)
> The original post in this thread had this example:
>    getPostageEstimate(source: String, destination: String, weight: Double)
> If we were dealing with this function in the world of optional arguments, 
> here's how we could use our abstraction:
>    Optional.pure(curry(getPostageEstimate)) <*> john.address <*> 
> alice.address <*> pure(2.0)
> It's not _too_ bad, though it's kinda noisy, we have to maintain a bunch of 
> custom code, we have to curry "getPostageEstimate" before passing it through, 
> we lose our argument labels, and we're living in custom operator world, which 
> can be disconcerting at first.
> If Swift provided sugar over this structure, we'd merely need to do this:
>    getPostageEstimate(|source: john.address, destination: alice.address, 
> weight: 2.0|)
> What's even neater is we can use this same format for types that wrap values 
> in other ways! Let's say the addresses are coming from untrusted sources and 
> need to be validated:
>    getPostageEstimate(|source: try validateAddr(john), destination: try 
> validateAddr(alice), weight: 2.0|)
> If both addresses are invalid and "throw", we could get both errors and 
> render them at once to our end user.
> Another example: what if we want to get the postage estimate for two 
> addresses that we need to fetch asynchronously:
>    getPostageEstimate(|source: await myAddress(), destination: await 
> theirAddress(), weight: 2.0|)
> Such a syntax allows those requests to run in parallel and not block :)
> In these examples, "Optional" promotion becomes applicative promotion ("2.0" 
> is getting wrapped automatically), which might open a can of worms but it's a 
> fun can to think about!
> --
> [1]: Well, technically it also requires "map", since any applicative is a 
> functor, and it also requires that it follows some math laws.
> Stephen
swift-evolution mailing list

Reply via email to