[swift-users] How to store ref to any kind of function, and call with it argument list?

C. Keith Ray keithray at mac.com
Sun Nov 5 12:26:54 CST 2017


Thanks, that's working for me, though I have to specify the return type explicitly to avoid compile error.

typealias TwoArgsFunction    = (Any,Any)->Any

func intToInt(_ i: Int, d: Double) -> Int {
    print("intToInt \(i) \(d)")
    return 4
}

func typeEraseTwo<Arg1T,Arg2T,R>(r: R.Type,
        fn: @escaping (Arg1T,Arg2T)->R) -> TwoArgsFunction {
    return {
        guard let arg1 = $0 as? Arg1T else {
                fatalError("\(Arg1T.self) wrong type \(type(of:$0)) \($0) ")
        }
        guard let arg2 = $1 as? Arg2T else {
                fatalError("\(Arg2T.self) wrong type \(type(of:$1)) \($1) ")
        }
        return fn(arg1, arg2)
    }
}

var function : TwoArgsFunction = typeEraseTwo(r: Int.self, fn: intToInt)
let r = function(12, 5.5)
print(r)
// prints "intToInt 12 5.5" and "4"

...and Xcode just crashed while I was typing in a playground.

--
C. Keith Ray
Senior Software Engineer / Trainer / Agile Coach
* http://www.thirdfoundationsw.com/keith_ray_resume_2014_long.pdf



> On Nov 3, 2017, at 10:31 PM, Slava Pestov <spestov at apple.com> wrote:
> 
> (Int) -> Int is not a subtype of (Any) -> Any, because a value of the latter type can be called with an argument type that is not an Int, for example a String:
> 
> let fn: (Int) -> Int = …
> let fn2 = (Any) -> Any = fn // pretend this works
> 
> fn2(“hi”) // what does this do?
> 
> I think you’ll need to do something with generics where you wrap the original function value in a thunk that tests the argument type first, eg
> 
> func erase<T, U>(fn: (T) -> U) -> (Any) -> Any {
>   return { any in fn(arg as! T) }
> }
> 
> I haven’t thought this through properly since it’s late, but it might be a good starting point.
> 
> Slava
> 
>> On Nov 3, 2017, at 7:01 PM, C. Keith Ray <keithray at mac.com <mailto:keithray at mac.com>> wrote:
>> 
>> um... how can I cast functions taking one argument to a "generic function pointer" type?
>> 
>> typealias OneArgsFunction = (Any)->Any
>> 
>> func intToInt(_ i: Int) -> Int { return 4 }
>> func floatToFloat(_ f: Float) -> Float { return 0.4 }
>> 
>> var function : OneArgsFunction = intToInt
>> function = floatToFloat
>> 
>> error: f.playground:4:34: error: cannot convert value of type '(Int) -> Int' to specified type 'OneArgsFunction' (aka '(Any) -> Any')
>> var function : OneArgsFunction = intToInt
>>                                  ^~~~~~~~
>> 
>> error: f.playground:5:12: error: cannot assign value of type '(Float) -> Float' to type 'OneArgsFunction' (aka '(Any) -> Any')
>> function = floatToFloat
>>            ^~~~~~~~~~~~
>> 
>> 
>> --
>> C. Keith Ray
>> Senior Software Engineer / Trainer / Agile Coach
>> * http://www.thirdfoundationsw.com/keith_ray_resume_2014_long.pdf <http://www.thirdfoundationsw.com/keith_ray_resume_2014_long.pdf>
>> 
>> 
>> 
>>> On Nov 3, 2017, at 2:24 PM, Slava Pestov <spestov at apple.com <mailto:spestov at apple.com>> wrote:
>>> 
>>> Unfortunately we don’t have a way to invoke a function with a runtime argument list because that would require runtime code generation in the most general case.
>>> 
>>> I would hack around it by handling the common cases of no arguments, 1 argument, 2 arguments, etc up to some fixed number of arguments that you consider “enough”. Use a switch on the type of the function.
>>> 
>>> Slava
>>> 
>>>> On Nov 3, 2017, at 2:21 PM, C. Keith Ray via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>>>> 
>>>> In the code below, I'm trying to store a reference to a function with any number of arguments, with any return type. And I want to be able to invoke it. 
>>>> 
>>>> Help?
>>>> 
>>>> See the comments with "*******"
>>>> 
>>>> typealias Void = ()
>>>> 
>>>> func equalTypes(_ me: [Any.Type], _ other: [Any.Type]) -> Bool {
>>>>     guard me.count == other.count else { return false }
>>>>     
>>>>     for i in 0 ..< me.count {
>>>>         if me[i] != other[i] { return false }
>>>>     }
>>>>     return true
>>>> }
>>>> 
>>>> struct Function {
>>>>     var returnType: Any.Type
>>>>     var argTypes: [Any.Type]
>>>>     var function: Any // ******* any function *******
>>>>     
>>>>     func perform(_ args: [Any]) -> Any {
>>>>         // ******* call function() with args, return result *******
>>>>         return 0
>>>>     }
>>>>     
>>>>     func isCompatible(_ other: Function) -> Bool {
>>>>         return self.returnType == other.returnType &&
>>>>             equalTypes(argTypes, other.argTypes)
>>>>     }
>>>> }
>>>> 
>>>> func vToV() {
>>>>     print("vToV")
>>>> }
>>>> 
>>>> func intToInt(_ i: Int) -> Int {
>>>>     print("intToInt")
>>>>     return 4
>>>> }
>>>> 
>>>> let f = Function(returnType: Void.self,
>>>>                  argTypes: [],
>>>>                  function: vToV)
>>>> let r = f.perform([])
>>>> print(r)
>>>> 
>>>> let f2 = Function(returnType: Int.self,
>>>>                  argTypes: [Int.self],
>>>>                  function: intToInt)
>>>> let r2 = f2.perform([12])
>>>> print(r2)
>>>> 
>>>> assert(f.isCompatible(f))
>>>> assert(!f.isCompatible(f2))
>>>> 
>>>> 
>>>> --
>>>> C. Keith Ray
>>>> Senior Software Engineer / Trainer / Agile Coach
>>>> * http://www.thirdfoundationsw.com/keith_ray_resume_2014_long.pdf <http://www.thirdfoundationsw.com/keith_ray_resume_2014_long.pdf>
>>>> 
>>>> 
>>>> 
>>>> _______________________________________________
>>>> swift-users mailing list
>>>> swift-users at swift.org <mailto:swift-users at swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
>>> 
>> 
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20171105/37e43e14/attachment.html>


More information about the swift-users mailing list