Any or a trouble with sequence of functions

Viewed 96

The problem I have faced is that I need to make a sequence of functions that can be invoked, but faced a problem that is, even though a function is a first-class member and do conform to protocol Any, the code bellow does not work.

struct FunctionSequence {

    var callbacks = [Any]() //how to restrict Any to only functions?

    init(with functions: Any...){
        self.callbacks = functions
    }

    func callAll(){
        for f in callbacks {
            f()
        }
    }
}

Compilation interrupts with:

error: cannot call value of non-function type 'Any'

So I ask people who's deep in the swift for some help. PS The result I need is as following:

var printer = FunctionSequence
    .init(with: {print("Hello,")}, {print("world!")})
printer.callbacks.insert({print("I hate you,")}, at: 1)
printer.callAll()
//outputs "Hello, I hate you, world!"
3 Answers

There is no general “function type” – functions with different arguments or different return types are different types.

In your case you apparently want an array of functions of type () -> Void, i.e. functions that take no arguments and do not return a value:

struct FunctionSequence {

    var callbacks = [() -> Void]()

    init(with functions: (() -> Void)...){
        self.callbacks = functions
    }

    // ...
}

Or with a type alias:

typealias SimpleFunction = () -> Void

struct FunctionSequence {

    var callbacks = [SimpleFunction]()

    init(with functions: SimpleFunction...){
        self.callbacks = functions
    }

    // ...
}

If callbacks is defined as an array of Any then you can put anything in it: functions taking no arguments, functions taking one integer, ..., integers, strings, anything.

It is then possible to check each array element for a specific signature, and call the functions accordingly. Example:

struct FunctionSequence {

    var callbacks = [Any]()

    init(with functions: Any...){
        self.callbacks = functions
    }

    func callAll() {
        for f in callbacks {
            switch f {
            case let f0 as () -> Void:    f0()
            case let f1 as (Int) -> Void: f1(17)
            default: break // Other function signature, or not a function at all.
            }
        }
    }
}

let printer = FunctionSequence(with:
        { print("Hello,") },
        { (i: Int) in print(i)} )
printer.callAll()

Use a function signature

struct FunctionSequence {
    var callbacks = [()->Void]() 

    init(with functions: ()->Void...){
        self.callbacks = functions
    }

    func callAll(){
        for f in callbacks {
            f()
        }
    }
}

or with a type alias

typealias VoidFunction = () -> Void
struct FunctionSequence {
    var callbacks = [VoidFunction]()

    init(with functions: VoidFunction...){
        self.callbacks = functions
    }

    func callAll(){
        for f in callbacks {
            f()
        }
    }
}

Beside the question but if you want the output on one line you probably want a function that returns a string and that callAll does the printing. For example

typealias StringFunction = () -> String
struct FunctionSequence {
    var callbacks = [StringFunction]()

    init(with functions: StringFunction...){
        self.callbacks = functions
    }

    func callAll(){
        print(callbacks.map {$0()}.joined(separator: " "))
    }
}

var printer = FunctionSequence
    .init(with: {"Hello,"}, {"world!"})
printer.callbacks.insert({"I hate you,"}, at: 1)
printer.callAll()

The problem I have faced is that I need to make a sequence of functions that can be invoked

That's just [() -> Void]. There's no need for another type.

var printer = [{print("Hello,", terminator: " ")}, {print("world!")}]
printer.insert({print("I hate you,", terminator: " ")}, at: 1)
for p in printer {p()}
//outputs "Hello, I hate you, world!"
Related