Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retrieve a function from a series of functions and call it

Tags:

rebol

rebol3

I'm trying to create a dispatcher of functions in Rebol 3, so that for each string the program receives there's an associated function to be called.

For example:

handlers: make map! [
  "foo" foo-func
  "bar" bar-func
]

where foo-func and bar-func are functions:

foo-func: func [ a b ] [ print "foo" ]
bar-func: func [ a b ] [ print "bar" ]

The idea is to select the function starting from the string, so:

f: select handlers "foo"

so that executing f is the same as executing foo-func and then call f with some arguments:

f param1 param2

I tried quoting the words in the map!, or using get-words but without success.

Using a get-word! at the console, without passing through a map! it works:

>> a: func [] [ print "Hello world!" ]
>> a
Hello world!
>> b: :a
>> b
Hello world!

Any help appreciated.

like image 705
kronwiz Avatar asked Mar 25 '14 13:03

kronwiz


4 Answers

select handlers "foo" only get the word foo-func:

f: select handlers "foo"
probe f   ;will get: foo-func

You need to get its content:

f: get f
f 1 2    ;will print "foo"

Or more compact:

f: get select handlers "foo"
like image 82
Wayne Cui Avatar answered Nov 08 '22 23:11

Wayne Cui


It's better to actually have the reference to the function in the map, rather than a word that refers to the function. If you store a word then you have to make sure the word is bound to an object which has a reference to that function, like this:

handlers: object [
    foo-func: func [ a b ] [ print "foo" ]
    bar-func: func [ a b ] [ print "bar" ]
]

handler-names: map [
    "foo" foo-func
    "bar" bar-func
]

apply get in handlers select handler-names name args

But if you just have a reference to the function in your map, you don't have to do the double indirect, and your code looks like this:

handlers: map reduce [
    "foo" func [ a b ] [ print "foo" ]
    "bar" func [ a b ] [ print "bar" ]
]

apply select handlers name args

Cleaner code, and more efficient too. Or if you're careful enough, like this:

handlers/(name) a b

The path method above will also work if you want the code to do nothing if there is no handler - common in cases where you have optional handlers, such as in GUIs.

You can even have more than one reference to the same function with different key names. You don't have to assign functions to words, they're just values. You can also use the path method to collect the handlers in the first place, saving a reduce.

handlers: make map! 10  ; preallocate as many entries as you expect
handlers/("foo"): func [ a b ] [ print "foo" ]
handlers/("bar"): func [ a b ] [ print "bar" ]
handlers/("baz"): select handlers "bar"  ; multiple references

That path syntax is just another way to call poke, but some prefer it. We have to put the string values in parens because of a (hopefully temporary) syntax conflict, but within those parens the string keys work. It's a faster alternative to do select or poke.

like image 21
BrianH Avatar answered Nov 09 '22 00:11

BrianH


foo-func in your map is just an unevaluated word

>> type? select handlers "foo"
== word!

You should first create your functions and then reduce the block, you use for creating your handler map so

handlers: make map! reduce [
   "foo" :foo-func
   "bar" :bar-func
]

then you have functions inside your map

>> type? select handlers "foo"
== function!
like image 4
sqlab Avatar answered Nov 09 '22 01:11

sqlab


Try: .... f: do select handlers "foo" ....

like image 2
giuliolunati Avatar answered Nov 08 '22 23:11

giuliolunati