Passing a getter as a function argument

I have a custom data structure that I'd like to be able to use in a generic way. Specifically, I'd like to pass a getter as an argument, so I can run a particular function on a numeric property. This is the basic pattern I'm following (toy problem):


/* A getter for a numeric property of a generic type. */
func genericGet<E, T where T: NumberConvertible>(object: E, getter: E -> T) -> T { 
     return getter(object) 
} 

struct EV : Linkable, Equatable, Comparable { 
     var pitch: Int 
     var ts: Double 
     var dur: Double 
} 


let anEvent = EV(pitch: 61, ts: 2.55, dur: 1.0) 


func getSomethingFromSomething<E, T: NumberConvertible>(thing: E, getThing: E -> T) -> T {
     return myGet(thing, getter: getThing) 
} 

let somePitch = getSomethingFromSomething(anEvent, getThing: {$0.pitch}) print("pitch = \(somePitch)") // prints "61"


All good! However, when I try the same approach in my generic struct:


struct MultiLink<E where E: Linkable, E: Equatable, E: Comparable> {
     var item: E
     var linkedItems: [E]?


     func bestTransition<E, T: NumberConvertible>(getter: E -> T, scaling: Double) -> E? {

        if let items = linkedItems {
            let c = items.count
  
            for i in 0..<c {
                let linkedItem = items[i]
                let itemProp = genericGet(item, getter: getter)
                
               // yes, this is unfinished...
            }
        }
        return nil
    }

    init(item: E, linkedItems: [E]?) {
        self.item = item
        self.linkedItems = linkedItems
    }
}


...the compiler complains:


Playground execution failed: Spliqs.playground:70:57: error: cannot convert value of type 'E -> T' to expected argument type '_ -> _'
                let itemProp = genericGet(item, getter: getter)


Fair enough; maybe it's a bit silly passing the "getter" through to another "getter"... So I also tried:


let itemProp = getter(item)


...at line 12, which seems to make sense, since E -> T is a getter signature, but I got a bizarre error:


Playground execution failed: Spliqs.playground:70:32: error: cannot invoke 'getter' with an argument list of type '(E)'
                let itemProp = getter(item)
                               ^
Spliqs.playground:70:32: note: expected an argument list of type '(E)'
                let itemProp = getter(item)


...which is totally strange because in the actual error it tells me I can't use '(E)' as the argument, and in the note it tells me it expects '(E)' as an argument.

Wha?? 🙂


(And yes, I did post this on Stack Overflow, in case anybody saw it there.)

Answered by QuinceyMorris in 134560022

>> The pattern E -> T is from this answer (on SO) …


Ah, I'm beginning to understand. The point is that "E -> T" is a closure type. As such, it has nothing directly to do with getters. As well as code like this:


getSomethingFromSomething(anEvent, getThing: {$0.ts})


you can have code like this:


getSomethingFromSomething(anEvent, getThing: {25})


(The second will produce a compile error in this form, because it doesn't allow the compiler to infer that the closure should have one parameter. You have to write "{ _ in 25}" instead. But the principle is the same as before. It's just a closure with one parameter and a return type.)


You can also, certainly, have getSomethingFromSomething be generic, such as:


struct EV {
     var pitch: Int
     var ts: Double
     var dur: Double
}
let anEvent = EV(pitch: 61, ts: 2.55, dur: 1.0)
func getSomethingFromSomething<E,T>(thing: E, getThing: E -> T) -> T {
     return getThing(thing)
}
print("pitch = \(getSomethingFromSomething(anEvent, getThing: {$0.pitch}))")
print("ts = \(getSomethingFromSomething(anEvent, getThing: {$0.ts}))")


(Done in a playground, simplifying your code a bit.)


All that aside, I went back to your original piece of code, and here what's wrong:


struct MultiLink<E where E: Linkable, E: Equatable, E: Comparable> {
    …
     func bestTransition<E, T: NumberConvertible>(getter: E -> T, scaling: Double) -> E? {


You've re-declared the generic parameter "E" for the function, and this overrides the declaration on the struct. When the compiler told you that "(E)" wasn't "(E)", it wasn't lying — you have two separate "E"s!


You should write it like this:


struct MultiLink<E where E: Linkable, E: Equatable, E: Comparable> {
    …
     func bestTransition<T: NumberConvertible>(getter: E -> T, scaling: Double) -> E? {


Now there's only one E. 😉


P.S. Yes, you don't need all that genericGet/getThing stuff. When you have a closure parameter, you can simply apply it as if it was a function name, like using a function pointer in C.


P.P.S. Also coming in Swift 3: you'll no longer be able to omit the parentheses around a parameter list. Get used to typing "(E) -> T".

I'm having trouble following your example code, but I see some things that look wrong.


1. "since E -> T is a getter signature"?! I don't get what this means. A getter (considered as something implemented as a method or closure) has no input parameters, just a return type, so how does "E" fit into this?


2. When you write a property name (e.g. "item" towards the end), it refers to the property value, not the getter. There may not even be a getter, if the property is implemented as an instance variable, as seems likely in this case.


3. There are types for closures, and types for data values, but there isn't anything that's of property type, AFAIK. I think that's what you would need, to be able to pass a getter (as opposed to the property value) as a parameter.


4. Theoretically, you should be able to pass a selector for the property getter, just as you could pass a selector for a method. However, Swift 2.2 doesn't have syntax for this — it's coming in Swift 3, as (currently proposed to be) '#selector(getter:item)'. Now, a selector isn't the closure you're trying AFAICT to make use of, but it might be an alternative, later this year.


Or possibly I just haven't understood you intentions. Can you show a shorter code fragment, preferably not involving generics, that demonstrates what goes wrong?

First, thanks for your reply!


"1. "since E -> T is a getter signature"?! I don't get what this means. A getter (considered as something implemented as a method or closure) has no input parameters, just a return type, so how does "E" fit into this?"


The pattern E -> T is from this answer (on SO), and works as expected:

http://stackoverflow.com/questions/28944271/passing-property-type-as-parameter/28944642#28944642


Here's a version of the first code chunk, with generics removed. This can be pasted right into a playground, and should print "pitch = 61".


struct EV : Linkable, Equatable, Comparable {
    var pitch: Int
    var ts: Double
    var dur: Double
}
func myGet(object: EV, getter: EV -> Int) -> Int {
    return getter(object)
}
let anEvent = EV(pitch: 61, ts: 2.55, dur: 1.0)
func getSomethingFromSomething(thing: EV, getThing: EV -> Int) -> Int {
    return myGet(thing, getter: getThing)
}
let somePitch = getSomethingFromSomething(anEvent, getThing: {$0.pitch})
print("pitch = \(somePitch)")


The point is that getSomethingFromSomething() doesn't know either the object or the property I'm going to "get" from it (hence the use of generics in the full example, since that's really the point).


With the basic principle working, I just want to be able to apply the same principle to a more real-world case.


------

ps — I poked around with the possibility of passing a Selector, which would be kind of ideal. Looking forward to Swift 3 then!

A little progress. If I overload "myGet", and do a little casting in the getSomethingFromSomething(), then I can get closer to what I'm after:


struct EV : Linkable, Equatable, Comparable {
    var pitch: Int
    var ts: Double
    var dur: Double
}
func myGet(object: EV, getter: EV -> Double) -> Double {
    return getter(object)
}
func myGet(object: EV, getter: EV -> Int) -> Int {
    return getter(object)
}
let anEvent = EV(pitch: 61, ts: 2.55, dur: 1.0)
func getSomethingFromSomething(thing: EV, getThing: EV -> Double) -> Double {
    return myGet(thing, getter: getThing)
}
let somePitch = getSomethingFromSomething(anEvent, getThing: {Double($0.pitch)})
let someTS = getSomethingFromSomething(anEvent, getThing: {$0.ts})
print("pitch = \(somePitch), ts = \(someTS)")


And going back to using NumberConvertible (cast-free-arithmetic on GitHub) alleviates the need for the overload and the casts. Closer!

[Edit] But this is where things start to look weird, to me, since getSomethingFromSomething() is really the same as myGet...

Accepted Answer

>> The pattern E -> T is from this answer (on SO) …


Ah, I'm beginning to understand. The point is that "E -> T" is a closure type. As such, it has nothing directly to do with getters. As well as code like this:


getSomethingFromSomething(anEvent, getThing: {$0.ts})


you can have code like this:


getSomethingFromSomething(anEvent, getThing: {25})


(The second will produce a compile error in this form, because it doesn't allow the compiler to infer that the closure should have one parameter. You have to write "{ _ in 25}" instead. But the principle is the same as before. It's just a closure with one parameter and a return type.)


You can also, certainly, have getSomethingFromSomething be generic, such as:


struct EV {
     var pitch: Int
     var ts: Double
     var dur: Double
}
let anEvent = EV(pitch: 61, ts: 2.55, dur: 1.0)
func getSomethingFromSomething<E,T>(thing: E, getThing: E -> T) -> T {
     return getThing(thing)
}
print("pitch = \(getSomethingFromSomething(anEvent, getThing: {$0.pitch}))")
print("ts = \(getSomethingFromSomething(anEvent, getThing: {$0.ts}))")


(Done in a playground, simplifying your code a bit.)


All that aside, I went back to your original piece of code, and here what's wrong:


struct MultiLink<E where E: Linkable, E: Equatable, E: Comparable> {
    …
     func bestTransition<E, T: NumberConvertible>(getter: E -> T, scaling: Double) -> E? {


You've re-declared the generic parameter "E" for the function, and this overrides the declaration on the struct. When the compiler told you that "(E)" wasn't "(E)", it wasn't lying — you have two separate "E"s!


You should write it like this:


struct MultiLink<E where E: Linkable, E: Equatable, E: Comparable> {
    …
     func bestTransition<T: NumberConvertible>(getter: E -> T, scaling: Double) -> E? {


Now there's only one E. 😉


P.S. Yes, you don't need all that genericGet/getThing stuff. When you have a closure parameter, you can simply apply it as if it was a function name, like using a function pointer in C.


P.P.S. Also coming in Swift 3: you'll no longer be able to omit the parentheses around a parameter list. Get used to typing "(E) -> T".

Ah, of course!! Well spotted! Thanks so much for catching that - it was driving me slightly mad!


(ps - generics still make my head bend slightly... not even close to second-nature yet!)


J

Passing a getter as a function argument
 
 
Q