[swift-evolution] [Proposal] Automating Partial Application via Wildcards

Erica Sadun erica at ericasadun.com
Tue Feb 2 12:19:10 CST 2016


I would not want to have to deal with inout, as "bad things could happen"™ And I'm imagining arbitrary params, not just rightmost,
although I would defer to implementation realities (such as not using _) as needed.

-- E

> On Feb 2, 2016, at 11:14 AM, Austin Zheng <austinzheng at gmail.com> wrote:
> 
> I like this ad-hoc currying proposal.
> 
> Questions:
> - How does this handle inout params? Are they just not allowed, to keep things simple? Or are they allowed as long as they aren't the parameter being curried?
> - Would you be able to curry arbitrary params in the function decl, or only the n rightmost params?
> 
> Best,
> Austin
> 
> 
>> On Feb 2, 2016, at 10:10 AM, Michael Ilseman via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> 
>>> On Feb 2, 2016, at 9:33 AM, Erica Sadun via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> First draft. Let's go Scala!
>>> 
>>> -- E
>>> 
>>> Automating Partial Application via Wildcards
>>> 
>>> Proposal: TBD
>>> Author(s): Erica Sadun <http://github.com/erica>, Davide De Franceschi <http://github.com/DeFrenZ>
>>> Status: TBD
>>> Review manager: TBD
>>>  <https://gist.github.com/erica/6327981d42eb9be6b4d2#introduction>Introduction
>>> 
>>> SE-0002 <https://github.com/apple/swift-evolution/blob/master/proposals/0002-remove-currying.md> has been accepted for Swift 3.0. That proposal removes currying func declaration syntax from Swift. It reasons that currying introduces unnecessary language and implementation complexity and is easily replaced with chained function return types. 
>>> 
>>> Because of SE-0002, this curried example:
>>> 
>>> public func projectFunctionToCoordinateSystem(function f: FunctionType)(p0: CGPoint, p1: CGPoint)(x: CGFloat) -> CGPoint
>>> becomes
>>> 
>>> public func projectFunctionToCoordinateSystem(function f: FunctionType) -> (p0: CGPoint, p1: CGPoint) -> (x: CGFloat) -> CGPoint 
>>> in Swift 3.
>>> 
>>> It's mechanically simple to re-introduce partial application but the current solution adds unnecessary nesting and complicated closure declarations, as you see in the following Swift 3 version of this projection function.
>>> 
>>> public func projectFunctionToCoordinateSystem(function f: FunctionType) -> (p0: CGPoint, p1: CGPoint) -> (x: CGFloat) -> CGPoint {
>>>   return { p0, p1 in
>>>     return { x in
>>>       let (dx, dy) = (p1.x - p0.x, p1.y - p0.y)
>>>       let (magnitude, theta) = (hypot(dy, dx), atan2(dy, dx)) // Thanks loooop
>>>       var outPoint = CGPoint(x: x * magnitude, y: f(x) * magnitude)
>>>       outPoint = CGPointApplyAffineTransform(outPoint, CGAffineTransformMakeRotation(theta))
>>>       outPoint = CGPointApplyAffineTransform(outPoint, CGAffineTransformMakeTranslation(p0.x, p0.y))
>>>       return CGPoint(x: outPoint.x, y: outPoint.y)
>>>     }
>>>   }
>>> }
>>> SE-0002 mentions the possibility of introducing Scala-style free-form partial implementation as a future step. This proposal requests that a Scala-style wildcard feature be adopted into Swift by introducing a form of automatic partial application.
>>> 
>>>  <https://gist.github.com/erica/6327981d42eb9be6b4d2#detail-design>Detail Design
>>> 
>>> The proposed design replaces a Swift 3 curried signature like this: 
>>> 
>>> public func projectFunctionToCoordinateSystem(function f: FunctionType) -> (p0: CGPoint, p1: CGPoint) -> (x: CGFloat) -> CGPoint
>>> with a non-curried, fully qualified call like this:
>>> 
>>> public func projectFunctionToCoordinateSystem(function f: FunctionType, p0: CGPoint, p1: CGPoint, x: CGFloat) -> CGPoint
>>> When called with wildcard tokens, the function is partially applied using the supplied arguments. For example:
>>> 
>>> let partial1 = projectFunctionToCoordinateSystem(function: mySinFunction, p0: p0, p1: p1, x: _)
>>> // partial1(x: xValue)
>>> 
>>> let partial2 = projectFunctionToCoordinateSystem(function: mySinFunction, p0: .zero, p1: _, x: _)
>>> // partial2(p1: p1Value, x: xValue) 
>>> // or
>>> // let partial3 = partial2(p1: p1Value, x: _); partial3(x: 0.25)
>>> This returns a curried form. Labels are retained and used, and the compiler should throw an error for any ambiguity that arises as a side effect.
>>> 
>>> 
>> 
>> To someone more familiar with the implementation of type checking than myself: how much would this complicate overload resolution, as there are no constraints or contextual typing information present in “_”? Would this, in practice, be any worse than something like:
>> 
>> func foo(a : Int, b b: Int?) -> Int {
>>     if let b = b {
>>         return a + b
>>     }
>>     return a
>> }
>> func foo(a : Int, b b: Float?) -> Float {
>>     if let b = b {
>>         return Float(a) + b
>>     }
>>     return Float(a)
>> }
>> 
>> let a = foo(1, b: 2) // Int: 3
>> let b = foo(1, b: 2.0) // Float: 3
>> let c = foo(1, b: nil) // Error: ambiguous use of 'foo(_:b:)'
>> 
>> 
>>> The function implementation is fully configured as if all parameters are specified, losing all its nested params in/params in/params in... overhead:
>>> 
>>> public func projectFunctionToCoordinateSystem(function f: FunctionType, p0: CGPoint, p1: CGPoint, x: CGFloat) -> CGPoint {
>>>   let (dx, dy) = (p1.x - p0.x, p1.y - p0.y)
>>>   let (magnitude, theta) = (hypot(dy, dx), atan2(dy, dx)) // Thanks loooop
>>>   var outPoint = CGPoint(x: x * magnitude, y: f(x) * magnitude)
>>>   outPoint = CGPointApplyAffineTransform(outPoint, CGAffineTransformMakeRotation(theta))
>>>   outPoint = CGPointApplyAffineTransform(outPoint, CGAffineTransformMakeTranslation(p0.x, p0.y))
>>>   return CGPoint(x: outPoint.x, y: outPoint.y)
>>> }
>>> The result is simple, readable, and written independently of how the currying is to be applied. as under this design, any parameter can be curried.
>>> 
>>>  <https://gist.github.com/erica/6327981d42eb9be6b4d2#alternatives-considered>Alternatives Considered
>>> 
>>> Although our natural inclination is to use standard currying and partial application, we also considered defaulting arguments. In this scenario, wildcards return a version of the function with defaulted arguments for all non-wildcard values. In such a design, 
>>> 
>>> let defaultedVersion = projectFunctionToCoordinateSystem(function: mySinFunction, p0: .zero, p1: _, x: _)
>>> could be called with a p0 parameter even though that same parameter was already specified in the assignment as in the following example. So this:
>>> 
>>> defaultedVersion(p0: myNewOrigin, p1: myPoint, x: 0.5)
>>> expands to:
>>> 
>>> defaultedVersionOfProjectFunctionToCoordinateSystem(function: mySinFunction, p0: myNewOrigin, p1: myPoint, x: 0.5)
>>> where the new version of p0 overrides the defaulted version created in the initial wildcard assignment.
>>> 
>>> The implementation details for this alternative approach would differ but it might be easier to implement. As you'd expect, any function called with fully qualified arguments would be executed rather than returning a defaulted version (or a partially applied version for the non-alternative implementation).
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160202/3e4c54a7/attachment.html>


More information about the swift-evolution mailing list