[swift-evolution] Brainstorming: New operator type - chaining

Craig Cruden ccruden at novafore.com
Sat Jan 30 20:44:22 CST 2016


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 at 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 at swift.org <mailto:swift-evolution at 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 at swift.org
>>    https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
>> 
>> 
>> --
>>   -- Howard.
>> 
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
> 
> 
> -- 
> Human Friendly Ltd.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list