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.

Why are you focusing so much on the fact that functions stored in a dictionary are called/executed after the code is compiled (ie runtime)?

I mean, of course they are. Just like any other function.


func foo() { print("foo") } // foo is this function.
foo()       // Calls foo.
let x = foo // The value of x is now a reference to the function foo.
x()         // Calls foo.
let myDictionary: [String : () -> ()] = ["foosKey" : foo] // This dictionary now has a value that is a reference to the function foo.
myDictionary["foosKey"]?() // Calls foo.


You certainly can put a (reference to a) function in a dictionary, just like you can in an array, in a variable, in a struct, in a class, whatever.

I can't see what the big deal/difference is.


Perhaps what you mean to point out is that you can't send a dictionary with function(reference)s in it over a network and expect to transmit the actual (code for those) functions? (And the same goes for eg archiving/unarchiving.)

I don't think anyone here have such expectations.

(Functions are reference types, and all that gets stored in the dictionary are references to them.)

You're missing the point entirely. Nothing stored in a dictionary is executable code. It's strings or numbers or possibly

even image data but not code.


Your example, adds fooskey to the dictionary, not the function foo().

I'm not sure who is missing what point really.


Of course it is not code, who has said that it should be code? By that logic you can not store instances of classes in Dictionaries either. No one would say that you can not put instances of some class in a dictionary. You can do that, most people would say, assuming that we all know that it is only references to the class instances that is stored in the dictionary, since classes are reference types.

Read my previous post again. It clearly says that it is references to functions that gets stored in the values of the key-value pairs of the dictionary. I even wrote that part starting with "Perhaps what you mean to point out ..." which it doesn't seem like you have read. Have you copy pasted and run my code example? It prints three "foo", each one of them is the result of a call to the function foo. Also, the OP is happy with the accepted answer (which clearly works, even though you say it won't work).


Here's my dictionary again:

let myDictionary: [String : () -> ()] = ["foosKey" : foo]

That is exactly the same as eg:

let myDictionary: Dictionary<String, (() -> Void)> = ["foosKey" : foo]

All Dictionaries has an Element type which is (Key, Value), and in this particular case:

Key is the type String

Value is the type () -> Void

So this Dictionary contains Strings (for keys) and references to functions of the type (() -> Void) as values.

I don't understand what you mean when you say that my dictionary only has a key and no value in it.

A Dictionary can't have just a key in it ...

This Dictionary clearly has one key-value pair in it.

The key is "foosKey" and the value is a reference to the function foo.

How can you not agree with this?


Furthermore, the title of this thread/question is:

- Can you put functions in Dictionaries?


My short answer to that would be:

- You can put (references to) functions in a Dictionary.

(Assuming we all know that functions are reference types, I think it's ok to not complicate the question, and answer with the word reference in parens.)


Two other very similar questions and answers:


- Can you put functions in variables?

- You can put (references to) functions in variables (and consts, and eg arrays and dictionaries for that matter).


- Can you put class instances in variables?

- You can put (references to) class instances in variables (and consts, and eg arrays and dictionaries for that matter).

I tried that example, and also with a class, and it gave the results you said: the closures from the struct stores the values it needs and forget the struct. The closures from the class instance refer back to the class. I'll have to watch out for that.

The description I wrote is based on the observation for the current Swift behavior, and I don't think it as it should be.

Because the behavior of capturing is inconsistent with the description in Capturing Values of The Swift Programming Language (Swift 2) .

And, if method closure of struct really holds a copy of self, mutating methods also should be available as closure...


I'm not sure Swift team takes this as a bug, but I think it's worth sending a Bug Report about this behavior.

A simplified way of looking at it is you're really putting a function pointer into the dictionary value. It's always worked in ObjC and Swift would be seriously messed up if it didn't work there as well.

I just tried against with a struct, but I change the order of things in the init():


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


By putting the initialization of the dictionary before the assignment of the function, I got the right results.


I'm starting to grasp it: it seems that the function captures the state of the struct; each function reference copies the state of the struct it is derived from. If the function reference is taken from a class instance, it only hold a reference to that instance.

Exactly, when used as closure, struct methods keeps its own copy of self, in your words, the state of the struct.

So, you'll get different result if you moved one lline to another place.

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

Now you know what happens on btn1 and btn2.

FWIW:


This also works:


struct Form {
    var commands = [String: Proc]()
    let btn1 = Button(caption: sh)
    let btn2 = Button(caption: gv)
    var stdBtnClick:Event!
    init() {
        stdBtnClick =  { sender in
            guard let button = sender as? Button else { return }
            print("\nThe caption is", button.caption)
            if let proc = self.commands[button.caption] {
                proc()
            } else {
                print("Can't find proc for", button.caption)
            }
            print("The proc was just called\n")
        }

        btn1.onClick = stdBtnClick
        btn2.onClick = stdBtnClick

        commands.updateValue(sayHello, forKey: sh)
        commands.updateValue( { greet("Veronica") }, forKey: gv)

        print("Commands =", commands)
    }
}


In this case the order doesn't matter.


But if you do this:


struct Form {
    var commands = [String: Proc]()
    let btn1 = Button(caption: sh)
    let btn2 = Button(caption: gv)
    let stdBtnClick:Event = { sender in
        guard let button = sender as? Button else { return }
        print("\nThe caption is", button.caption)
        if let proc = self.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)
    }
}


Will not work because "self" in this case does not exist yet, we are on the class context right now, and if you change this:


if let proc = self.commands[button.caption] {


to this:


if let proc = commands[button.caption] {


the obvious message will appear::

//instance member 'commands' cannot be used on type 'Form'


And this also will not work:


struct Form {
    var commands = [String: Proc]()
    let btn1 = Button(caption: sh)
    let btn2 = Button(caption: gv)
    let stdBtnClick:Event;
    init() {
        stdBtnClick = { sender in
            guard let button = sender as? Button else { return }
            print("\nThe caption is", button.caption)
            if let proc = self.commands[button.caption] {
                proc()
            } else {
                print("Can't find proc for", button.caption)
            }
            print("The proc was just called\n")
        }
   
        btn1.onClick = stdBtnClick
        btn2.onClick = stdBtnClick
   
        commands.updateValue(sayHello, forKey: sh)
        commands.updateValue( { greet("Veronica") }, forKey: gv)
   
        print("Commands =", commands)
    }
}
//error: variable 'self.stdBtnClick' captured by a closure before being initialized stdBtnClick = { sender in



And this is exacly the problem of the first case, and may also why we can consider this a bug:


Because struct are only referenced strongly like other value types, using closure like in this way in not possible, we cant capture self before at end of initialization.


Using function and not closures appear to break this rule, and worst, appear to make a copy of self inside of initialization. The "self" inside of the function is, in the some way, a promise of post initialization, but when you reference this function inside of the init method on structs, some "not yet" fully initialized self appear to be "captured" in each assign. Using a a implicitly unwrapped optional closure appear to works like expected, reference the "same" self as used to initialize the struct, and not a strange copy.

Thanks. This is getting tricky. In some cases we may need to restort to calling an extra initialization routine after the one used to create the variable at first. (A bit like viewDidLoad. That seems unnecessary when we first start designing our algorithms.


I think it would have been simpler if veriables in structs and classes worked like variables in functions: if they have been declared they can be used.


---

Mark

Can you put functions in Dictionaries?
 
 
Q