[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