Can you put functions in Dictionaries?

Hi,


I have been trying to prove a concept in Swift that I implemented in another language. I thought it would be easy is Swift, but it doesn't seem to work.


My playground code is below. When I call the 2 btn.click() functions at the end it get


The caption is Say hello

Can't find proc for Say hello

The proc was just called

The caption is Greet Veronica

Can't find proc for Greet Veronica

The proc was just called


I was hoping that sayHello would be called and then the closure to greet Veronica.


Can anyone see what coud be going wrong here?


TIA

Mark


import UIKit
print("\nTesting assignment of functions\n")

typealias Proc = () -> ()
typealias Event = AnyObject -> ()

class Button {

    var caption: String
    var onClick: Event?

    init(caption: String, onClick: Event? = nil) {
        self.caption = caption
        self.onClick = onClick
    }

    func click() {
        onClick?(self)
    }

}

func sayHello() { print("Hello") }
func greet(name: String) { print("Hello", name) }

let sh = "Say hello"
let gv = "Greet Veronica"

struct Form {

    var commands = [String: Proc]()


    let btn1 = Button(caption: sh)
    let btn2 = Button(caption: gv)
    func stdBtnClick(sender: AnyObject) {
        guard let button = sender as? Button else { return }
        print("\nThe caption is", button.caption)
        if let proc = commands[button.caption] {
            proc()
        } else {
            print("Can't find proc for", button.caption)
        }
        print("The proc was just called\n")
    }


    init() {
        btn1.onClick = stdBtnClick
        btn2.onClick = stdBtnClick
     
        commands.updateValue(sayHello, forKey: sh)
        commands.updateValue( { greet("Veronica") }, forKey: gv)
     
        print("Commands =", commands)
    }

}

let form = Form()
form.btn1.click()
form.btn2.click()
Answered by jsslai in 59137022

Change Form to class instead of struct. I guess now the Form gets copied and commands property is empty.

Short answer, no, you cannot put functions in dictionaries. Dictionaries contain data which is unprocessed.

That data is not compiled into executable code.

But I keep hearing that in Swift functions are first class objects. This would seem to suggest otherwise.

Functions are, dictionaries are not.

Accepted Answer

Change Form to class instead of struct. I guess now the Form gets copied and commands property is empty.

Try changing `struct Form {` to `class Form {`, and consider how value type semantics work.


EDIT: It seems you have noticed yourself jsslai have noticed it before I post this. (Sorry, I first missed your name, jsslai.)

That's it. Method closure holds `self` implicitly, and the held `self` behaves according to the value type semantics when it is a struct.

Code cannot be executed from a dictionary. This is the strength of iOS. And to some extent OSX.

This is a good thing. It makes no difference what you define a dictionary item as. Dictionaries are

just data. They are NOT compiled code.

Fortunately it turns out you can store functions in dictionaries. The issue was that I was not grasping the interplay between value and reference uses of the variables. The case I was coming from, in Delphi, was all in classes, not the equivalent of struct (record).


I can't see any reason to treat data stored in a dictionary any differently from the way it is treated in a simple variable. The dictionary is just a way of getting at the data.

Thanks for the answers, guys. I must admit, I can't see why it didn't work with struct. I was not copying the form as far as I could see. The buttons remained the same, with the same properties, but the dictionary broke down for some reason.

Won't work, it's post compile.

Like OOPer said, struct has value semantics. It gets copied automatically instead of a class which has reference semantics.

You can find this copying behavior in a more simplified struct.

struct MyStruct {
    var value: Int = 0
    func getValue() -> Int {
        return value;
    }
}
var x = MyStruct()
let f1 = x.getValue
x.value = 2
let f2 = x.getValue
print(f1()) //->0
print(f2()) //->2

Both f1 and f2 have their own copy of x, try everything to confirm this.

Put those in a dictionary. Now try to execute the commands you retrieve. It WILL NOT happen.

Are you saying something like this?

typealias ProcInt = ()->Int
var dict: [String: ProcInt] = [:]
dict["F1"] = f1
dict["F2"] = f2
x.value = 5
dict["F3"] = x.getValue
print(dict["F1"]!()) //->0
print(dict["F2"]!()) //->2
x.value = 10
print(dict["F3"]!()) //->5

That happens AFTER the code is compiled. What's so hard about that to understand?

It is so hard to understand what is not clearly explained.

Would you please explain by example what you want to say?

Can you put functions in Dictionaries?
 
 
Q