vanvoorden
(Rick van Voorden)
1
Hi! I've been reading through the (excellent) posts on Doug's Compiler Corner from @Douglas_Gregor . I learn new things! I did not have much experience with "curried instance methods":
struct Point {
var x: Int
var y: Int
func flippedOverXAxis() -> Point { ... }
func flippedOverYAxis() -> Point { ... }
}
let memberFunction = Point.flippedOverXAxis
var p = Point(x: 1, y: 2)
p = memberFunction(p)()
I have a situation with a function that accepts a closure that operates on a Point instance:
func f(_: (Point) -> Point) { }
f({ point in Point.flippedOverXAxis(point)() })
This works… but I am wondering if there is some way to pass that curried instance method directly without the ceremony of wrapping it in a closure and passing a point instance:
f(Point.flippedOverXAxis)
This fails:
18 | f({ point in Point.flippedOverXAxis(point)() })
19 |
20 | f(Point.flippedOverXAxis)
| `- error: cannot convert value of type '@Sendable () -> Point' to expected argument type '(Point) -> Point'
| `- error: instance member 'flippedOverXAxis' cannot be used on type 'Point'; did you mean to use a value of this type instead?
21 |
Is there any way to pass that curried method directly to a closure that expects an "instance" method? I am unblocked (because I can always wrap the curried method in a closure)… but it would be interesting if there was a shortcut there that worked. Thanks!
You forgot to include the parameters in the instance method signature.
func f(_: (Point) -> () -> Point) { }
The generic form looks like this:
func f<Instance, each Parameter, Output>(
_: (Instance) -> (repeat each Parameter) -> Output
) { }
f(Point.flippedOverXAxis)
3 Likes
jrose
(Jordan Rose)
3
Forever unhappy that we didn't get time to implement SE-0042.
5 Likes
ibex10
5
@Slava_Pestov, @Quedlinbug: I am trying out @Quedlinbug's generic function above.
But the following code fails to compile (on Xcode 15.4).
f (Foo (), p: 1, 2, 3, 4, 5, o: Foo.bar)
struct Foo {
func bar <each U>(_ u: repeat each U) -> Foo {
let v = (repeat each u)
print (#function, v)
return self
}
}
func f <Instance, each Parameter, Output> (
_ i:Instance,
p: repeat each Parameter,
o v: (Instance) -> (repeat each Parameter) -> Output
) {
print (#function)
_ = v (i) (repeat each p)
}
Compilation Errors
Command SwiftCompile failed with a nonzero exit code
error: compile command failed due to signal 11 (use -v to see invocation)
Jon_Shier
(Jon Shier)
6
Crashes in Xcode 16b3 as well.
vanvoorden
(Rick van Voorden)
7
Sure… but if we are changing the function signature then that's not exactly my original question. If we were stuck with the original function (because it came from a library or other source we don't control)… then we still need some kind of ceremony to forward that to the original function:
func f(_ body: (Point) -> Point) { }
func g(_ body: (Point) -> () -> Point) {
f({ point in body(point)() })
}
g(Point.flippedOverXAxis)
Unless I am missing something… there's no shortcut to get directly to the compact version without the SE-0042 changes (as referenced by @jrose)
rayx
(Huan Xiong)
8
If I change Foo.bar to a regular method (I think this is what the orginal discussion was about), it works fine.
I suspect the crash is because variadic generic function can't be used as closure. Below is a simpler example to reproduce the crash:
struct Foo {
func bar <each U>(_ u: repeat each U) -> Foo {
let v = (repeat each u)
print (#function, v)
return self
}
}
func f(_ i: Foo, p1: Int, p2: Int, c: (Foo) -> (Int, Int) -> Foo) {
c(i)(p1, p2)
}
f(Foo(), p1: 1, p2: 2, c: Foo.bar)
1 Like
What you are describing is converting a method to a property*.
func convertMethodToProperty<Instance, Property>(
_ method: @escaping (Instance) -> () -> Property
) -> (Instance) -> Property {
{ method($0)() }
}
Even disregarding the need for @escaping, is that an ergonomic improvement?
f { $0.flippedOverXAxis() }
f(convertMethodToProperty(Point.flippedOverXAxis))
* While it wouldn't work for the article, flippedOverXAxis should be a property. Change it to
var flippedOverXAxis: Point
and then you have what you want:
f(\.flippedOverXAxis)
That would not do it. You want (Point) -> Point, but that would provide (Point, Void) -> Point.