Brainstorming: New operator type - chaining


(Joseph Lord) #1

Currently there are three types of operator: prefix, infix and postfix and most of the Swift operations can be defined using these and implemented for additional types and they cover most use cases but there are features in the Swift language which cannot be replicated and customised with these.

In particular there is the optional chaining use of `?` for which I can see no way of producing customised versions of or adapting to types other than optionals (e.g. custom Either types or other monadic types).

I’m not sure about the feasibility of this change or what knockon effects might be. This is meant as exploratory suggestion that may not reach proposal stage.

Would be interested to know:
1) If this is feasible.
2) If it is interesting to people.
3) What if anything should be possible for l-values (optional chaining works on them but what should be possible.
4) Any other good alternatives.

I picture that the chaining operator function would be called with the left hand side value and could be required to return a value of a type such as this ChainValueResult:

enum ChainValueResult<A,C,D> {
   case continue(A, C->D),
   case stop(D)
}

The logic then applied to this enum would be in case of continue to apply the C->D function to the result of continuing the chain which is applied to the A value. In the case of stop it is to simply return the value of type D.

I have used this enum in the draft code below. Please note that the code below doesn't compile and that if really being used the inference system would need to account for the return type of the ongoing chain.

Use case 1 - Custom optional chaining behaviour

The particular thing that I would like use this for is to make a custom asserting variant of the optional chaining operator. I already do this with nil coalescing (it is currently the only custom operator in my code) but a shorthand for this would be good:

assert(foo != nil)
foo?.bar()

I would only want to do:

foo±.bar()

func ± <A, C>(lhs: A?)->ChainValueResult<A, C->C? ,C?> {
  if let lhs = lhs {
    return .continue(lhs, { $0 })
    } else {
    assertionFailure()
          return .stop(nil)
  }
}

Use case 2 - Chaining on custom types

And this especially may not be feasible and I may be out of my depth describing the requirement but it feels like a more general case so at least worth airing):

It would be good for code like the following to be possible (hopefully with a little more type inference).

let c:Either<Int, Error> = Either<String, Error>(.Left(“boo"))^^.count

Now the user’s definition of the operator would probably have to look something like this:

func ^^<A,B, C>(lhs: Either<A,B>)->(ChainValueResult<A, C, Either<C,B>> {
     switch lhs {
     case .Left(let a):
         return .continue(a, { Either<C,B>(.Left($0) })
     case .Right(let b):
         return .stop( Either<C, B>(.Right(b))
  }
}

I couldn't find any significant discussion of optional chaining in the mailing list so far.

Joseph


(Chris Lattner) #2

Use case 2 - Chaining on custom types

And this especially may not be feasible and I may be out of my depth describing the requirement but it feels like a more general case so at least worth airing):

I’m not certain I understand the problem you’re looking to solve here, but I think it would be great (though probably very low priority) to open up the functionality of the postfix optional chaining operator to other types. This could allow its monadic binding (aka railroad short circuiting) behavior to be extended to other types like Result, a left-biased-either, or an arbitrary type defined by the user. A natural approach would be to capture this behavior in an “chainable” protocol of some sort.

This is low priority because I don’t know a lot of killer applications of it, I’m motivated by my ivory tower desire to push compiler magic into the standard library. It bugs me that optional has sugar and other behavior that other types can’t participate in.

-Chris

···

On Jan 30, 2016, at 3:06 PM, Joseph Lord via swift-evolution <swift-evolution@swift.org> wrote:

It would be good for code like the following to be possible (hopefully with a little more type inference).

let c:Either<Int, Error> = Either<String, Error>(.Left(“boo"))^^.count

Now the user’s definition of the operator would probably have to look something like this:

func ^^<A,B, C>(lhs: Either<A,B>)->(ChainValueResult<A, C, Either<C,B>> {
   switch lhs {
   case .Left(let a):
       return .continue(a, { Either<C,B>(.Left($0) })
   case .Right(let b):
       return .stop( Either<C, B>(.Right(b))
  }
}

I couldn't find any significant discussion of optional chaining in the mailing list so far.

Joseph
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Howard Lovatt) #3

What is wrong with flatMap returning an Optional? Like Scala does.

···

On Sunday, 31 January 2016, Joseph Lord via swift-evolution < swift-evolution@swift.org> wrote:

Currently there are three types of operator: prefix, infix and postfix and
most of the Swift operations can be defined using these and implemented for
additional types and they cover most use cases but there are features in
the Swift language which cannot be replicated and customised with these.

In particular there is the optional chaining use of `?` for which I can
see no way of producing customised versions of or adapting to types other
than optionals (e.g. custom Either types or other monadic types).

I’m not sure about the feasibility of this change or what knockon effects
might be. This is meant as exploratory suggestion that may not reach
proposal stage.

Would be interested to know:
1) If this is feasible.
2) If it is interesting to people.
3) What if anything should be possible for l-values (optional chaining
works on them but what should be possible.
4) Any other good alternatives.

I picture that the chaining operator function would be called with the
left hand side value and could be required to return a value of a type such
as this ChainValueResult:

enum ChainValueResult<A,C,D> {
  case continue(A, C->D),
  case stop(D)
}

The logic then applied to this enum would be in case of continue to apply
the C->D function to the result of continuing the chain which is applied to
the A value. In the case of stop it is to simply return the value of type D.

I have used this enum in the draft code below. Please note that the code
below doesn't compile and that if really being used the inference system
would need to account for the return type of the ongoing chain.

Use case 1 - Custom optional chaining behaviour

The particular thing that I would like use this for is to make a custom
asserting variant of the optional chaining operator. I already do this with
nil coalescing (it is currently the only custom operator in my code) but a
shorthand for this would be good:

assert(foo != nil)
foo?.bar()

I would only want to do:

foo±.bar()

func ± <A, C>(lhs: A?)->ChainValueResult<A, C->C? ,C?> {
        if let lhs = lhs {
                return .continue(lhs, { $0 })
        } else {
                assertionFailure()
                return .stop(nil)
        }
}

Use case 2 - Chaining on custom types

And this especially may not be feasible and I may be out of my depth
describing the requirement but it feels like a more general case so at
least worth airing):

It would be good for code like the following to be possible (hopefully
with a little more type inference).

let c:Either<Int, Error> = Either<String, Error>(.Left(“boo"))^^.count

Now the user’s definition of the operator would probably have to look
something like this:

func ^^<A,B, C>(lhs: Either<A,B>)->(ChainValueResult<A, C, Either<C,B>> {
        switch lhs {
        case .Left(let a):
                return .continue(a, { Either<C,B>(.Left($0) })
         case .Right(let b):
                return .stop( Either<C, B>(.Right(b))
        }
}

I couldn't find any significant discussion of optional chaining in the
mailing list so far.

Joseph
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
  -- Howard.


(Jonathan Tang) #4

> Use case 2 - Chaining on custom types
>
> And this especially may not be feasible and I may be out of my depth
describing the requirement but it feels like a more general case so at
least worth airing):

I’m not certain I understand the problem you’re looking to solve here, but
I think it would be great (though probably very low priority) to open up
the functionality of the postfix optional chaining operator to other
types. This could allow its monadic binding (aka railroad short
circuiting) behavior to be extended to other types like Result, a
left-biased-either, or an arbitrary type defined by the user. A natural
approach would be to capture this behavior in an “chainable” protocol of
some sort.

BTW, chainable monadic behavior is already available via .method() calls on
an object that returns Self and maintains some internal state. This is
widely used across a number of other languages, eg.

1. JQuery, where the state is a selection of DOM elements and each method
call is an operation on the DOM.
2. Builder pattern in Java, where the Builder holds the internal state of
the object and each method call validates that it's a legal transformation.
3. SwiftyJSON, where the state is whether every key along the chain exists
and has the proper type, and then .stringValue/.intValue/.boolValue returns
the final result or nil if there was an error at any point.
4. Django's ORM, where the state is the current clauses of the SQL query,
each method adds a clause, and then subscripting/iterating/etc. force
query-building and execution of the query.
5. BeautifulSoup, where the state is your current position in the DOM tree
and selection of nodes.

Swift is already a huge leap forward from Objective-C in this regard,
because the latter's syntax didn't really allow method chaining.

What optional chaining gives us is the ability to invoke an arbitrary
method of the "contained" object, without needing to know what type it is.
So for example, foo?.bar()?.baz() will invoke .bar() on Optional<Foo>
without Optional having to explicitly say that it supports a method bar().
It just forwards all operations on, providing nil if the Optional contained
nil. Sorta like a type-safe version of #doesNotUnderstand in SmallTalk.

I see this as being much more related to generics than protocols, though I
guess you'd need a protocol to specify the behavior of ? in terms of the
container type. Use-cases I can think of:

1. Implicit mapping over collections, like JQuery. myArray?.foo() would
invoke foo() on every object contained in myArray.
2. RPCs. You could imagine RPCStub<RemoteObject> be an object that wraps
RemoteObject, and then stub?.foo(1, "two") marshals the parameters, passes
them along to the remove server, invokes them, and returns them, maybe
wrapped in a Promise. Assuming the remote object is written in Swift too,
you'd get a lot more type-safety than stub.call("foo", 1, "two").
3. Logging/tracing/performance monitoring, where you might want to record
the fact that the call was made without worrying about what call it is.
4. Type erasure. Right now, if Foo is a protocol with an associated type,
you can't have an array of Foo where each element might be a different
concrete type adopting Foo. The typical workaround is a struct AnyFoo
which wraps the concrete instance, adopts Foo, and forwards all calls on to
the wrapped instance. A chaining operator would let you avoid having to
explicitly adopt the protocol and write all the forwarding message calls.
5. Anything that you'd use ES6 proxy objects for. I had a use-case for
these a couple months ago that involved tracking everything that was stored
in a container so the container could be automatically persisted.
6. CoreData could possibly benefit from this - imagine being able to define
CoreData objects as ordinary Swift structs, intercept all property setting,
and use this to build the appropriate ManagedObjectModels. A lot of the
current model-editing GUI in XCode might be able to be replaced.

Now that I've listed them out, it's basically aspect-oriented programming,
which means it'd have the same use-cases as AOP and also the same pitfalls.

···

On Sat, Jan 30, 2016 at 9:20 PM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 30, 2016, at 3:06 PM, Joseph Lord via swift-evolution < > swift-evolution@swift.org> wrote:

This is low priority because I don’t know a lot of killer applications of
it, I’m motivated by my ivory tower desire to push compiler magic into the
standard library. It bugs me that optional has sugar and other behavior
that other types can’t participate in.

-Chris

>
> It would be good for code like the following to be possible (hopefully
with a little more type inference).
>
> let c:Either<Int, Error> = Either<String, Error>(.Left(“boo"))^^.count
>
> Now the user’s definition of the operator would probably have to look
something like this:
>
> func ^^<A,B, C>(lhs: Either<A,B>)->(ChainValueResult<A, C, Either<C,B>>
{
> switch lhs {
> case .Left(let a):
> return .continue(a, { Either<C,B>(.Left($0) })
> case .Right(let b):
> return .stop( Either<C, B>(.Right(b))
> }
> }
>
> I couldn't find any significant discussion of optional chaining in the
mailing list so far.
>
> Joseph
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Tyler Cloutier) #5

I’m admittedly a novice in functional programming, but couldn’t this be accomplished with Higher Kinded Types? Forgive me if I misunderstand, but it strikes me that Optional Chaining raises the chained operation into the optional context, such that “failed" operations propagate the current .None optional context. Isn’t the purpose of Applicative Functors to define this type of behavior for all such types that have this property?

Tyler

···

On Jan 30, 2016, at 9:20 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 30, 2016, at 3:06 PM, Joseph Lord via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Use case 2 - Chaining on custom types

And this especially may not be feasible and I may be out of my depth describing the requirement but it feels like a more general case so at least worth airing):

I’m not certain I understand the problem you’re looking to solve here, but I think it would be great (though probably very low priority) to open up the functionality of the postfix optional chaining operator to other types. This could allow its monadic binding (aka railroad short circuiting) behavior to be extended to other types like Result, a left-biased-either, or an arbitrary type defined by the user. A natural approach would be to capture this behavior in an “chainable” protocol of some sort.

This is low priority because I don’t know a lot of killer applications of it, I’m motivated by my ivory tower desire to push compiler magic into the standard library. It bugs me that optional has sugar and other behavior that other types can’t participate in.


(Joseph Lord) #6

Thanks,

I can understand that it is low priority and know that you have a huge amount on your plate before 3.0. I don't really have a killer application because I think it is really syntactic sugar for a map like function with the benefit of removing the explicit closure. If I'm right and it is just syntactic sugar then there is no urgency but I did want to mention the idea at this stage in case there were any potential impacts on the ABI so that at least it could be considered and allowed for if necessary.

Especially if the academic issue of the special treatment of optionals bothers you as it does me a little I'm hopeful that this will be addressed at some point (4.0 or 5.0 maybe) but don't want to add additional pressure to the Core Team.

The personal use case is the first one I mentioned in the original message, wanting an asserting variant of optional chaining. That it is not currently possible led me to consider a more general possible solution and what else could be done with it. The Either use case was a bigger stretch for me as I don't usually use that style but it felt like the thing missing to enable alternatives to optionals being used as first class citizens with the powers of optionals in projects that choose to use them.

To elaborate on the relationship between optional chaining and map which reassures me it is syntactic sugar and is unlikely to be an ABI issue I looked at how to do the equivalent operations (I'm sure this is obvious to Chris and many others of you but I thought it worth sharing as it helped me think about things)

The current optional chaining is equivalent to this on an rvalue:

foo?.bar()

foo.map { $0.bar() } // except this warns about unused results

And this on a lvalue version:

foo?.bar = 1

foo.map { $0.bar = 1 }

As always in these most simple examples the sugar doesn't feel that worthwhile but the benefits are shown in more complex or repeated use. An example of an assignment to a property that is accessed through two levels of optional would look quite ugly as I think it would need a double map but an `if let` with two binds would be the better option without optional chaining.

Joseph

···

On 31/01/2016 05:20, Chris Lattner via swift-evolution wrote:
On Jan 30, 2016, at 3:06 PM, Joseph Lord via swift-evolution <swift-evolution@swift.org> wrote:
Use case 2 - Chaining on custom types

And this especially may not be feasible and I may be out of my depth describing the requirement but it feels like a more general case so at least worth airing):

I’m not certain I understand the problem you’re looking to solve here, but I think it would be great (though probably very low priority) to open up the functionality of the postfix optional chaining operator to other types. This could allow its monadic binding (aka railroad short circuiting) behavior to be extended to other types like Result, a left-biased-either, or an arbitrary type defined by the user. A natural approach would be to capture this behavior in an “chainable” protocol of some sort.

This is low priority because I don’t know a lot of killer applications of it, I’m motivated by my ivory tower desire to push compiler magic into the standard library. It bugs me that optional has sugar and other behavior that other types can’t participate in.

-Chris


(Joseph Lord) #7

What is wrong with flatMap returning an Optional? Like Scala does.

>

Using map or a function taking a closure is less clear to read than an chaining mechanism.

For the first usecase (which is really the most trivial case of optional chaining:

> assert(foo != nil)
> foo?.bar()
>
> I would only want to do:
>
> foo±.bar()
>

_ = foo.aMap { $0.bar() }

If you can image a multiple step chain it could clearly get more complicated.

I don't think that there is a way to avoid the closures within current Swift and any map/flatMap based approach.

I'm not familiar with Scala so I may have misunderstood exactly what you meant.

Joseph

···

On 31/01/2016 01:14, Howard Lovatt via swift-evolution wrote:

On Sunday, 31 January 2016, Joseph Lord via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    Currently there are three types of operator: prefix, infix and
    postfix and most of the Swift operations can be defined using these
    and implemented for additional types and they cover most use cases
    but there are features in the Swift language which cannot be
    replicated and customised with these.

    In particular there is the optional chaining use of `?` for which I
    can see no way of producing customised versions of or adapting to
    types other than optionals (e.g. custom Either types or other
    monadic types).

    I’m not sure about the feasibility of this change or what knockon
    effects might be. This is meant as exploratory suggestion that may
    not reach proposal stage.

    Would be interested to know:
    1) If this is feasible.
    2) If it is interesting to people.
    3) What if anything should be possible for l-values (optional
    chaining works on them but what should be possible.
    4) Any other good alternatives.

    I picture that the chaining operator function would be called with
    the left hand side value and could be required to return a value of
    a type such as this ChainValueResult:

    enum ChainValueResult<A,C,D> {
       case continue(A, C->D),
       case stop(D)
    }

    The logic then applied to this enum would be in case of continue to
    apply the C->D function to the result of continuing the chain which
    is applied to the A value. In the case of stop it is to simply
    return the value of type D.

    I have used this enum in the draft code below. Please note that the
    code below doesn't compile and that if really being used the
    inference system would need to account for the return type of the
    ongoing chain.

    Use case 1 - Custom optional chaining behaviour

    The particular thing that I would like use this for is to make a
    custom asserting variant of the optional chaining operator. I
    already do this with nil coalescing (it is currently the only custom
    operator in my code) but a shorthand for this would be good:

    assert(foo != nil)
    foo?.bar()

    I would only want to do:

    foo±.bar()

    func ± <A, C>(lhs: A?)->ChainValueResult<A, C->C? ,C?> {
             if let lhs = lhs {
                     return .continue(lhs, { $0 })
             } else {
                     assertionFailure()
                     return .stop(nil)
             }
    }

    Use case 2 - Chaining on custom types

    And this especially may not be feasible and I may be out of my depth
    describing the requirement but it feels like a more general case so
    at least worth airing):

    It would be good for code like the following to be possible
    (hopefully with a little more type inference).

    let c:Either<Int, Error> = Either<String, Error>(.Left(“boo"))^^.count

    Now the user’s definition of the operator would probably have to
    look something like this:

    func ^^<A,B, C>(lhs: Either<A,B>)->(ChainValueResult<A, C,
    Either<C,B>> {
             switch lhs {
             case .Left(let a):
                     return .continue(a, { Either<C,B>(.Left($0) })
              case .Right(let b):
                     return .stop( Either<C, B>(.Right(b))
             }
    }

    I couldn't find any significant discussion of optional chaining in
    the mailing list so far.

    Joseph
    _______________________________________________
    swift-evolution mailing list
    swift-evolution@swift.org
    https://lists.swift.org/mailman/listinfo/swift-evolution

--
   -- Howard.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Human Friendly Ltd.


(Craig Cruden) #8

My interest is in expressions more than functions (i.e. n? + 5)…. but

IF... Swift implemented a protocol of maybe MonadOps that indicated that a function/option supported
monadic-like operations (instead of ad-hoc separate implementations) which would then be used
to implement syntactic for-comprehension sugar for maps and flatMaps it would make the chaining
of options much more readable without getting overly fancy.

i.e.

= for {
   o1 <- optional1
   o2 <- optional2
   o3 <- optional3
   …
} yield { foo.bar(o1, o2, o3) }

···

On 2016-01-31, at 9:23:36, Joseph Lord via swift-evolution <swift-evolution@swift.org> wrote:

On 31/01/2016 01:14, Howard Lovatt via swift-evolution wrote:
> What is wrong with flatMap returning an Optional? Like Scala does.
>

Using map or a function taking a closure is less clear to read than an chaining mechanism.

For the first usecase (which is really the most trivial case of optional chaining:

> assert(foo != nil)
> foo?.bar()
>
> I would only want to do:
>
> foo±.bar()
>

_ = foo.aMap { $0.bar() }

If you can image a multiple step chain it could clearly get more complicated.

I don't think that there is a way to avoid the closures within current Swift and any map/flatMap based approach.

I'm not familiar with Scala so I may have misunderstood exactly what you meant.

Joseph

On Sunday, 31 January 2016, Joseph Lord via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

   Currently there are three types of operator: prefix, infix and
   postfix and most of the Swift operations can be defined using these
   and implemented for additional types and they cover most use cases
   but there are features in the Swift language which cannot be
   replicated and customised with these.

   In particular there is the optional chaining use of `?` for which I
   can see no way of producing customised versions of or adapting to
   types other than optionals (e.g. custom Either types or other
   monadic types).

   I’m not sure about the feasibility of this change or what knockon
   effects might be. This is meant as exploratory suggestion that may
   not reach proposal stage.

   Would be interested to know:
   1) If this is feasible.
   2) If it is interesting to people.
   3) What if anything should be possible for l-values (optional
   chaining works on them but what should be possible.
   4) Any other good alternatives.

   I picture that the chaining operator function would be called with
   the left hand side value and could be required to return a value of
   a type such as this ChainValueResult:

   enum ChainValueResult<A,C,D> {
      case continue(A, C->D),
      case stop(D)
   }

   The logic then applied to this enum would be in case of continue to
   apply the C->D function to the result of continuing the chain which
   is applied to the A value. In the case of stop it is to simply
   return the value of type D.

   I have used this enum in the draft code below. Please note that the
   code below doesn't compile and that if really being used the
   inference system would need to account for the return type of the
   ongoing chain.

   Use case 1 - Custom optional chaining behaviour

   The particular thing that I would like use this for is to make a
   custom asserting variant of the optional chaining operator. I
   already do this with nil coalescing (it is currently the only custom
   operator in my code) but a shorthand for this would be good:

   assert(foo != nil)
   foo?.bar()

   I would only want to do:

   foo±.bar()

   func ± <A, C>(lhs: A?)->ChainValueResult<A, C->C? ,C?> {
            if let lhs = lhs {
                    return .continue(lhs, { $0 })
            } else {
                    assertionFailure()
                    return .stop(nil)
            }
   }

   Use case 2 - Chaining on custom types

   And this especially may not be feasible and I may be out of my depth
   describing the requirement but it feels like a more general case so
   at least worth airing):

   It would be good for code like the following to be possible
   (hopefully with a little more type inference).

   let c:Either<Int, Error> = Either<String, Error>(.Left(“boo"))^^.count

   Now the user’s definition of the operator would probably have to
   look something like this:

   func ^^<A,B, C>(lhs: Either<A,B>)->(ChainValueResult<A, C,
   Either<C,B>> {
            switch lhs {
            case .Left(let a):
                    return .continue(a, { Either<C,B>(.Left($0) })
             case .Right(let b):
                    return .stop( Either<C, B>(.Right(b))
            }
   }

   I couldn't find any significant discussion of optional chaining in
   the mailing list so far.

   Joseph
   _______________________________________________
   swift-evolution mailing list
   swift-evolution@swift.org
   https://lists.swift.org/mailman/listinfo/swift-evolution

--
  -- Howard.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Human Friendly Ltd.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Howard Lovatt) #9

I am probably completely mis-understanding what you want. Your 1st example,
isn't it equivalent to:

    foo!.bar()

···

On Sunday, 31 January 2016, Joseph Lord via swift-evolution < swift-evolution@swift.org> wrote:

On 31/01/2016 01:14, Howard Lovatt via swift-evolution wrote:
> What is wrong with flatMap returning an Optional? Like Scala does.
>

Using map or a function taking a closure is less clear to read than an
chaining mechanism.

For the first usecase (which is really the most trivial case of optional
chaining:

> assert(foo != nil)
> foo?.bar()
>
> I would only want to do:
>
> foo±.bar()
>

_ = foo.aMap { $0.bar() }

If you can image a multiple step chain it could clearly get more
complicated.

I don't think that there is a way to avoid the closures within current
Swift and any map/flatMap based approach.

I'm not familiar with Scala so I may have misunderstood exactly what you
meant.

Joseph

On Sunday, 31 January 2016, Joseph Lord via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    Currently there are three types of operator: prefix, infix and
    postfix and most of the Swift operations can be defined using these
    and implemented for additional types and they cover most use cases
    but there are features in the Swift language which cannot be
    replicated and customised with these.

    In particular there is the optional chaining use of `?` for which I
    can see no way of producing customised versions of or adapting to
    types other than optionals (e.g. custom Either types or other
    monadic types).

    I’m not sure about the feasibility of this change or what knockon
    effects might be. This is meant as exploratory suggestion that may
    not reach proposal stage.

    Would be interested to know:
    1) If this is feasible.
    2) If it is interesting to people.
    3) What if anything should be possible for l-values (optional
    chaining works on them but what should be possible.
    4) Any other good alternatives.

    I picture that the chaining operator function would be called with
    the left hand side value and could be required to return a value of
    a type such as this ChainValueResult:

    enum ChainValueResult<A,C,D> {
       case continue(A, C->D),
       case stop(D)
    }

    The logic then applied to this enum would be in case of continue to
    apply the C->D function to the result of continuing the chain which
    is applied to the A value. In the case of stop it is to simply
    return the value of type D.

    I have used this enum in the draft code below. Please note that the
    code below doesn't compile and that if really being used the
    inference system would need to account for the return type of the
    ongoing chain.

    Use case 1 - Custom optional chaining behaviour

    The particular thing that I would like use this for is to make a
    custom asserting variant of the optional chaining operator. I
    already do this with nil coalescing (it is currently the only custom
    operator in my code) but a shorthand for this would be good:

    assert(foo != nil)
    foo?.bar()

    I would only want to do:

    foo±.bar()

    func ± <A, C>(lhs: A?)->ChainValueResult<A, C->C? ,C?> {
             if let lhs = lhs {
                     return .continue(lhs, { $0 })
             } else {
                     assertionFailure()
                     return .stop(nil)
             }
    }

    Use case 2 - Chaining on custom types

    And this especially may not be feasible and I may be out of my depth
    describing the requirement but it feels like a more general case so
    at least worth airing):

    It would be good for code like the following to be possible
    (hopefully with a little more type inference).

    let c:Either<Int, Error> = Either<String, Error>(.Left(“boo"))^^.count

    Now the user’s definition of the operator would probably have to
    look something like this:

    func ^^<A,B, C>(lhs: Either<A,B>)->(ChainValueResult<A, C,
    Either<C,B>> {
             switch lhs {
             case .Left(let a):
                     return .continue(a, { Either<C,B>(.Left($0) })
              case .Right(let b):
                     return .stop( Either<C, B>(.Right(b))
             }
    }

    I couldn't find any significant discussion of optional chaining in
    the mailing list so far.

    Joseph
    _______________________________________________
    swift-evolution mailing list
    swift-evolution@swift.org
    https://lists.swift.org/mailman/listinfo/swift-evolution

--
   -- Howard.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Human Friendly Ltd.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
  -- Howard.


(Tyler Cloutier) #10

And by Applicative Functor, I believe I actually intend Monad.

Tyler

···

On Jan 31, 2016, at 5:07 PM, Tyler Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 30, 2016, at 9:20 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 30, 2016, at 3:06 PM, Joseph Lord via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Use case 2 - Chaining on custom types

And this especially may not be feasible and I may be out of my depth describing the requirement but it feels like a more general case so at least worth airing):

I’m not certain I understand the problem you’re looking to solve here, but I think it would be great (though probably very low priority) to open up the functionality of the postfix optional chaining operator to other types. This could allow its monadic binding (aka railroad short circuiting) behavior to be extended to other types like Result, a left-biased-either, or an arbitrary type defined by the user. A natural approach would be to capture this behavior in an “chainable” protocol of some sort.

This is low priority because I don’t know a lot of killer applications of it, I’m motivated by my ivory tower desire to push compiler magic into the standard library. It bugs me that optional has sugar and other behavior that other types can’t participate in.

I’m admittedly a novice in functional programming, but couldn’t this be accomplished with Higher Kinded Types? Forgive me if I misunderstand, but it strikes me that Optional Chaining raises the chained operation into the optional context, such that “failed" operations propagate the current .None optional context. Isn’t the purpose of Applicative Functors to define this type of behavior for all such types that have this property?

Tyler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joseph Lord) #11

I probably wasn't very clear. The 1st example is equivalent to the force unwrap but only in debug builds. In release builds it becomes equivalent to:

foo?.bar()

This gives the strict checking in development and allows you to check that assumptions of non-nilness are correct but still provides compiler proven nil safe handling in production.

I already use a similar approach for nil-coalescing in places where that is a suitable option: http://blog.human-friendly.com/theanswer-equals-maybeanswer-or-a-good-alternative

Joseph

···

On 31/01/2016 02:51, Howard Lovatt via swift-evolution wrote:

I am probably completely mis-understanding what you want. Your 1st
example, isn't it equivalent to:

     foo!.bar()


(Austin Zheng) #12

I think HKTs would be required to allow a generic implementation to serve all monadic instances, but even today there's no way AFAIK to replicate the "?." style chaining operator for any type of arguments in user code.

I would be +1 for the original proposal. Just as we have "if let actualThing = thing" syntax for the common case of optional unwrapping, and "if case let .Foo(x) = y" syntax that is more generally applicable, it would be great to have "?." used to chain optionals and a similar syntax allowing for other types (like a Result<T, ErrorType> or a Promise) to be chained. The chaining method calls on optionals make using them a lot less onerous than if only (e.g.) nested explicit if-let unwrapping were available.

Best,
Austin

···

On Jan 31, 2016, at 5:14 PM, Tyler Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

And by Applicative Functor, I believe I actually intend Monad.

Tyler

On Jan 31, 2016, at 5:07 PM, Tyler Cloutier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 30, 2016, at 9:20 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 30, 2016, at 3:06 PM, Joseph Lord via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Use case 2 - Chaining on custom types

And this especially may not be feasible and I may be out of my depth describing the requirement but it feels like a more general case so at least worth airing):

I’m not certain I understand the problem you’re looking to solve here, but I think it would be great (though probably very low priority) to open up the functionality of the postfix optional chaining operator to other types. This could allow its monadic binding (aka railroad short circuiting) behavior to be extended to other types like Result, a left-biased-either, or an arbitrary type defined by the user. A natural approach would be to capture this behavior in an “chainable” protocol of some sort.

This is low priority because I don’t know a lot of killer applications of it, I’m motivated by my ivory tower desire to push compiler magic into the standard library. It bugs me that optional has sugar and other behavior that other types can’t participate in.

I’m admittedly a novice in functional programming, but couldn’t this be accomplished with Higher Kinded Types? Forgive me if I misunderstand, but it strikes me that Optional Chaining raises the chained operation into the optional context, such that “failed" operations propagate the current .None optional context. Isn’t the purpose of Applicative Functors to define this type of behavior for all such types that have this property?

Tyler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution