Changing the curried static methods


(Brandon Williams) #1

Right now instance methods on a type get a curried, static method for free,
e.g. CGRect.insetBy is a curried function CGRect -> (CGFloat, CGFloat) ->
CGRect. This is super cool and great for code reusability.

Unfortunately, I think the order of the curry makes it difficult to use
most of the time. With the above example you would use it as such:

CGRect.insetBy(rect)(10.0, 10.0)

That doesn’t read very nicely, and it’s more likely that you know (10.0,
10.0) before you know rect, hence you would want to call it as:

CGRect.insetBy(10.0, 10.0)(rect)

In general, I would expect a method:

struct A {
  func method <B, C> (b: B) -> C
}

to have a static method of the form

A.method: B -> A -> C

Does this make sense? Is there a reason for the current design that I’m not
seeing?


(Robert Widmann) #2

Hm, I always justified the existing behavior to myself because it reminds me of how Objective-C passes parameters in method calls (i.e. you get self, [SEL], params).

~Robert Widmann

2016/03/12 14:46、Brandon Williams via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

Right now instance methods on a type get a curried, static method for free, e.g. CGRect.insetBy is a curried function CGRect -> (CGFloat, CGFloat) -> CGRect. This is super cool and great for code reusability.

Unfortunately, I think the order of the curry makes it difficult to use most of the time. With the above example you would use it as such:

CGRect.insetBy(rect)(10.0, 10.0)

That doesn’t read very nicely, and it’s more likely that you know (10.0, 10.0) before you know rect, hence you would want to call it as:

CGRect.insetBy(10.0, 10.0)(rect)

In general, I would expect a method:

struct A {
  func method <B, C> (b: B) -> C
}

to have a static method of the form

A.method: B -> A -> C

Does this make sense? Is there a reason for the current design that I’m not seeing?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Tino) #3

A relegated issue is that it's complicated to use curried static methods with map & foreach
(for example, this
view.subviews.forEach(UIView.removeFromSuperview)
doesn't work )


(Joe Groff) #4

Note that there's a proposal open to flatten away the currying altogether:

https://github.com/apple/swift-evolution/blob/master/proposals/0042-flatten-method-types.md

I agree that it would often be more useful to bind the non-self arguments first. Maybe we could provide a different shorthand for that, though; a number of people for instance have suggested `.insetBy(10.0, 10.0)` as a possibility.

-Joe

···

On Mar 12, 2016, at 1:46 PM, Brandon Williams via swift-evolution <swift-evolution@swift.org> wrote:

Right now instance methods on a type get a curried, static method for free, e.g. CGRect.insetBy is a curried function CGRect -> (CGFloat, CGFloat) -> CGRect. This is super cool and great for code reusability.

Unfortunately, I think the order of the curry makes it difficult to use most of the time. With the above example you would use it as such:

CGRect.insetBy(rect)(10.0, 10.0)

That doesn’t read very nicely, and it’s more likely that you know (10.0, 10.0) before you know rect, hence you would want to call it as:

CGRect.insetBy(10.0, 10.0)(rect)

In general, I would expect a method:

struct A {
  func method <B, C> (b: B) -> C
}

to have a static method of the form

A.method: B -> A -> C

Does this make sense? Is there a reason for the current design that I’m not seeing?


(Erica Sadun) #5

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

···

On Mar 13, 2016, at 11:09 AM, Stephen Celis via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 13, 2016, at 9:17 AM, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

A relegated issue is that it's complicated to use curried static methods with map & foreach
(for example, this
view.subviews.forEach(UIView.removeFromSuperview)
doesn't work )

With a final Void return value, wouldn't that just become the following?

   view.subviews.forEach(UIView.removeFromSuperview())

I have to wonder how common this pattern is, anyway, when the following is as short and arguably easier to read:

   view.subviews.forEach { $0.removeFromSuperview() }

Does the current convention give us anything else?

It's easy enough to define a `flip` library function that converts `A -> B -> C` to `B -> A -> C`, but I do see the latter being a more powerful default than the former, even if it's unconventional.


(Erica Sadun) #6

forEach currently does f(x).
apply would do x.f()

-- E

···

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


#7

With a final Void return value, wouldn't that just become the following?

    view.subviews.forEach(UIView.removeFromSuperview())

I have to wonder how common this pattern is, anyway, when the following is as short and arguably easier to read:

    view.subviews.forEach { $0.removeFromSuperview() }

Does the current convention give us anything else?

It's easy enough to define a `flip` library function that converts `A -> B -> C` to `B -> A -> C`, but I do see the latter being a more powerful default than the former, even if it's unconventional.

Stephen

···

On Mar 13, 2016, at 9:17 AM, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

A relegated issue is that it's complicated to use curried static methods with map & foreach
(for example, this
view.subviews.forEach(UIView.removeFromSuperview)
doesn't work )


#8

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

···

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


(Brandon Williams) #9

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


(Brandon Williams) #10

Ah, thanks for the reference. I wasn’t aware of that proposal.

I wish currying wasn’t going away, but aside from that I think there’s
still a strong case for flipping the arguments since it simply reads better
for the arguments to follow the name of the method.

···

On Mon, Mar 14, 2016 at 1:09 PM Joe Groff <jgroff@apple.com> wrote:

On Mar 12, 2016, at 1:46 PM, Brandon Williams via swift-evolution < > swift-evolution@swift.org> wrote:

Right now instance methods on a type get a curried, static method for
free, e.g. CGRect.insetBy is a curried function CGRect -> (CGFloat,
CGFloat) -> CGRect. This is super cool and great for code reusability.

Unfortunately, I think the order of the curry makes it difficult to use
most of the time. With the above example you would use it as such:

CGRect.insetBy(rect)(10.0, 10.0)

That doesn’t read very nicely, and it’s more likely that you know (10.0,
10.0) before you know rect, hence you would want to call it as:

CGRect.insetBy(10.0, 10.0)(rect)

In general, I would expect a method:

struct A {
  func method <B, C> (b: B) -> C
}

to have a static method of the form

A.method: B -> A -> C

Does this make sense? Is there a reason for the current design that I’m
not seeing?

Note that there's a proposal open to flatten away the currying altogether:

https://github.com/apple/swift-evolution/blob/master/proposals/0042-flatten-method-types.md

I agree that it would often be more useful to bind the non-self arguments
first. Maybe we could provide a different shorthand for that, though; a
number of people for instance have suggested `.insetBy(10.0, 10.0)` as a
possibility.

-Joe


#11

Interesting proposal! Is there a migration path for code that takes advantage of the current currying? The following is a convoluted example, but I've appreciated the flexibility in actual code:

  func flip<A, B, C>(input: A -> B -> C) -> B -> A -> C {
    return { b in { a in input(a)(b) } }
  }
  let insetBy = flip(CGRect.insetBy)
  let grow = { insetBy((-$0, -$0)) }
  grow(50)(.zero)

- Stephen

···

On Mar 14, 2016, at 1:09 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

Note that there's a proposal open to flatten away the currying altogether:

https://github.com/apple/swift-evolution/blob/master/proposals/0042-flatten-method-types.md

I agree that it would often be more useful to bind the non-self arguments first. Maybe we could provide a different shorthand for that, though; a number of people for instance have suggested `.insetBy(10.0, 10.0)` as a possibility.


(Erica Sadun) #12

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 <mailto:swift-evolution@swift.org>> wrote:

> On Mar 13, 2016, at 11:30 AM, Stephen Celis <stephen.celis@gmail.com <mailto:stephen.celis@gmail.com>> wrote:
>
>> On Mar 13, 2016, at 1:18 PM, Erica Sadun <erica@ericasadun.com <mailto: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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Brandon Williams) #13

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


(Joe Groff) #14

Well, you could still write a `curry` transform to turn (T, U) -> V into T -> U -> V. IMO, it's much clearer to express these kinds of transforms directly as closure literals than in terms of abstract dataflow transforms like `flip`. { $0.insetBy(-$1, -$1) } is more readily understandable than the above.

-Joe

···

On Mar 15, 2016, at 6:03 PM, Stephen Celis <stephen.celis@gmail.com> wrote:

On Mar 14, 2016, at 1:09 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

Note that there's a proposal open to flatten away the currying altogether:

https://github.com/apple/swift-evolution/blob/master/proposals/0042-flatten-method-types.md

I agree that it would often be more useful to bind the non-self arguments first. Maybe we could provide a different shorthand for that, though; a number of people for instance have suggested `.insetBy(10.0, 10.0)` as a possibility.

Interesting proposal! Is there a migration path for code that takes advantage of the current currying? The following is a convoluted example, but I've appreciated the flexibility in actual code:

func flip<A, B, C>(input: A -> B -> C) -> B -> A -> C {
   return { b in { a in input(a)(b) } }
}
let insetBy = flip(CGRect.insetBy)
let grow = { insetBy((-$0, -$0)) }
grow(50)(.zero)


(Erica Sadun) #15

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.

By your description, it sort of sounds like you could want to create a method cascade:

myRect
   ..insetBy(args)
   ..offsetBy(args)

In this, the instance passes through each of the function calls, joining them into a fluent declaration
with an implicit receiver.

But it also sounds like you want to use functional chaining. Core Image can chain because it always has
inputImage and outputImage and set defaults:

myRect = myRect.insetBy(args).offsetBy(args)

Alternative, you could be doing some kind of weird chaining thing where so long as the output and inputs match
up they can be composed together:

f = (x: insetByAble).insetBy(args)->(T:offsetByAble).offsetBy(args)->U
myRect = f(myRect)

But in the end, it kind of almost really sounds like you just want to write a function

f(x: CGRect) -> CGRect {
   x.insetBy(10.0, 10.0)
   x.offsetBy(-20.0, 10.0)
   return x
}

Which of these are you aiming for?
            
-- E


(Brandon Williams) #16

Sorry for the confusion, I’m not looking for help to model something in
particular. These are examples to show the difference between the current
curried static convention and the one I think is more expressive.

To iterate, right now CGRect.insetBy(rect)(insets) just reads incorrectly
to me. Considering that Swift aims to be expressive through method names
and argument labels, I would expect this to be CGRect.insetBy(insets)(rect).

···

On Sun, Mar 13, 2016 at 10:54 PM Erica Sadun <erica@ericasadun.com> wrote:

> 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.
>

By your description, it sort of sounds like you could want to create a
method cascade:

myRect
   ..insetBy(args)
   ..offsetBy(args)

In this, the instance passes through each of the function calls, joining
them into a fluent declaration
with an implicit receiver.

But it also sounds like you want to use functional chaining. Core Image
can chain because it always has
inputImage and outputImage and set defaults:

myRect = myRect.insetBy(args).offsetBy(args)

Alternative, you could be doing some kind of weird chaining thing where so
long as the output and inputs match
up they can be composed together:

f = (x: insetByAble).insetBy(args)->(T:offsetByAble).offsetBy(args)->U
myRect = f(myRect)

But in the end, it kind of almost really sounds like you just want to
write a function

f(x: CGRect) -> CGRect {
   x.insetBy(10.0, 10.0)
   x.offsetBy(-20.0, 10.0)
   return x
}

Which of these are you aiming for?

-- E


(Erica Sadun) #17

Ah. I think I finally understand what you're getting at.

You'd like to be able to create a version of the call *with arguments* before applying the call to a receiver. Right?

But isn't currying in that form dead? Or is the Class.member(instance) currying going to persist into Swift 3?

-- E

···

On Mar 14, 2016, at 8:39 AM, Brandon Williams <mbw234@gmail.com> wrote:

Sorry for the confusion, I’m not looking for help to model something in particular. These are examples to show the difference between the current curried static convention and the one I think is more expressive.

To iterate, right now CGRect.insetBy(rect)(insets) just reads incorrectly to me. Considering that Swift aims to be expressive through method names and argument labels, I would expect this to be CGRect.insetBy(insets)(rect).