No problem Erica, I find this to be pretty subtle!
That's definitely how static versions of instance methods work today, and
it does it's job pretty well! My suggestion to flip the arguments is a bid
to increase the readability and re-usability of these methods without
changing any of the real semantics.
Here's how we can do it manually in order to see the benefits. The
following function:
func flip <A, B, C> (f: A -> B -> C) -> B -> A -> C {
return { b in { a in f(a)(b) } }
}
simply converts `A -> B -> C` to `B -> A -> C`, i.e. flips its arguments.
Let's use it make a version of `UIView.removeFromSuperview` that I think is
more understandable:
extension UIView {
@nonobjc static let _removeFromSuperview =
flip(UIView.removeFromSuperview)
}
(ignore that @nonobjc, it's only necessary cause Swift is trying to
generate a dynamic accessor)
With this method your code example becomes
v.subviews.forEach(UIView._removeFromSuperview())
To me this reads: for each subview of `v`, apply the action
`UIView._removeFromSuperview()`. Compare this to the equivalent with
today's static method:
v.subviews.map(UIView.removeFromSuperview).forEach{ $0() }
This is read as: map the subviews of `v` into an array of actions, and then
invoke those actions.
The benefits of this flip are easier to see with different examples,
because I agree with Stephen that it's hard to beat a simple
`v.subviews.forEach { $0.removeSuperView() }` in this particular example.
If static methods had their arguments flipped, we'd be able to easily
construct functions like `CGRect.insetBy(10.0, 10.0)` and
`CGRect.offsetBy(-20.0, 10.0)`. These are now free functions without any
mention to a specific rectangle. We could compose them to get a function
that simulataneously insets and translates, all without mentioning a
rectangle. With today's static methods we'd have to apply `flip` to each
one, i.e. `flip(CGRect.insetBy)(10.0, 10.0)`.
This becomes very powerful with constructing data pipelines that describe
how to transform data without ever actually mentioning the data. There are
quite a few examples of things like this in Cocoa. A nice one is Core
Image, in which you can build a pipeline of filters that you can feed
images into.
···
On Sun, Mar 13, 2016 at 3:45 PM Erica Sadun <erica@ericasadun.com> wrote:
I'm probably missing the point here, so apologize in advance. Instead of
reducing a function with an n-arity set of arguments to a partially applied
function with (m|m<n)-arity set of arguments, it's building a functional
application and applying that to an arbitrary receiver.
`UIView.removeFromSuperview(receiver) `
defines
`receiver.removeFromSuperview`
which can then be applied with whatever arguments, () in this case.
`UIView.removeFromSuperview(receiver)()` aka
`receiver.removeFromSuperview()`
With mapping, you can do:
let v = UIView()
(1...5).forEach{_ in v.addSubview(UIView())}
// You can apply each subview as a receiver, returning a function
let applied = v.subviews.map(UIView.removeFromSuperview)
// [(Function), (Function), (Function), (Function), (Function)]
// And then you an execute them in forEach
print(v.subviews.count) // 5
v.subviews.map(UIView.removeFromSuperview).forEach{$0()}
print(v.subviews.count) // 0
The whole map/forEach could be defined down to apply in a possible
language extension, as I mentioned before.
-- E, who apologizes for really not getting this
On Mar 13, 2016, at 1:21 PM, Brandon Williams <mbw234@gmail.com> wrote:
I think this highlights some of the confusion around the current curried
convention. Void methods are curried as A -> () -> (), which means you
would use it like:
UIView.removeFromSuperview # => UIView -> () -> ()
UIView.removeFromSuperview(view) # => () -> ()
UIView.removeFromSuperview(view)() #> ()
I find this confusing because removeFromSuperview reads as an action, yet
UIView.removeFromSuperview(view) does not perform the action but rather is
an action itself that requires a further invocation ().
With arguments flipped we would have:
UIView.removeFromSuperview # => () -> UIView -> ()
UIView.removeFromSuperview() # => UIView -> ()
UIView.removeFromSuperview()(view) #> ()
It now reads to me that UIView.removeFromSuperview() is the action that
will do the removing, and UIView.removeFromSuperview()(view) is applying
the action to a view.
I don’t advocate using removeFromSuperview in this manner, but if one were
to I believe the latter convention is easier to reason about without having
to look up types in a playground (as I had to do a few times to write this
:P)
On Sun, Mar 13, 2016 at 1:50 PM Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:
> On Mar 13, 2016, at 11:30 AM, Stephen Celis <stephen.celis@gmail.com> >> wrote:
>
>> On Mar 13, 2016, at 1:18 PM, Erica Sadun <erica@ericasadun.com> wrote:
>>
>> Since removeFromSuperview doesn't take a UIView argument, it sounds
like what you're looking for is
>> something that acts like "apply", to apply a
lambda/closure/selector/whatever to each member of a collection.
>>
>> view.subviews.apply(UIView.removeFromSuperview)
>>
>> -- E
>
> This is what `forEach` currently does with the existing curried static
syntax, right?
>
> I was more interested in the implications of an example brought up in
the OP:
>
> frames.map(CGRect.insetBy(-10, -10))
>
> - Stephen
forEach currently does f(x).
apply would do x.f()
-- E
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution