Anonymous callbacks

Started by Jeff, June 25, 2007, 05:09:25 AM

Previous topic - Next topic

Jeff

Is there a way to pass a lambda as a callback to a guiserver element without assigning it to a symbol?
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Lutz

#1
Quick answer: No.



But perhaps you can play some tricks understanding the following:



You can pass either a symbol or a string, but the string must not contain any spaces. So guiserver parses it one as one token (see the definition of gs:button in guiserver.lsp. So this would both work:


(gs:button "TheButton" 'myaction "press me")

(gs:button "TheButton "MAIN:myaction"  "press me")


guiserver parses out the the action token with a simple Java SringTokenizer. So it will break up the string along spaces. This is what guiserver does when formatting the event:


String action = params.nextToken();
...
guiserver.out.println("("+ action + " "" + id +  "")");


Then gs:listen in guiserver.lsp just das a eval-string on the string received.



But most important, what are your trying to do? Why not assgining the lambda function to a symbol?



Lutz

Jeff

#2
There are always other ways to accomplish it.  But one of the most useful, er, uses of an anonymous function is to define simple callbacks.



That's what makes Javascript so usable as a gui framework, and practically the entire basis of libs like Prototype.



I suppose I could create a wrapper macro that would handle things for me, assigning incremental symbols, but that would be a work-around (because it would not be an anonymous function anymore).



By allowing anonymous functions as callbacks, you free up the library considerably.  You can store an entire menu system in a table that can be quickly and easily edited.  It would allow a handy recursive macro that could create the menu system from the table at runtime.



That would allow programs that are easily extensible by the user; provide an interface to the table and we have a program that is extensible in newLisp a la emacs.



I really think this is something that is worth considering as a change to the way the guiserver works, although I certainly understand the practical constraints of writing a parser in Java (blech).
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Jeff

#3
Also, a note on how lambdas make interface programming more elegant.  You do not clutter up the name space and it is much easier to follow a chain of functions when they are defined within their parents.  It also makes large chains of callbacks easier, and is more idiomatic of a lisp.



Here is an example:  button A creates dialog B with button C on it.  When button A is pressed, it calls a function that generates dialog B with button C.  Where is the action of button C defined?  If it is unique, there is no reason not to use an anonymous function.  



In addition, isn't it more elegant for button A to actually create dialog B and button C, rather than just making those elements visible?
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Lutz

#4
It is only the communications link needing the action as a compact token. Yoy could easilyt reqite the gs:button and gs:listen functions.


(gs:button 'TheButton (lambda (x) (println (upper-case x)) "press-me")

... passed into the new definition of gs:button.


(define (button id action text)
(net-send out (string "button " " id " " (base64-enc (string action)) " " (base64 text) "n")))


On the gs:listen side you get back a base64 encode sitting right after the parenthesis containing the text of the lambda expression. Use a regex replace on the event string to transform:



; event received in gs:listen

event => (KGZvbyAoeCkgKHByaW50bG4gKHVwcGUtcmNhc2UgeCkpKQ== "TheButton")

; into
event => ((foo (x) (println (upper-case x))) "TheButton")




Lutz

Jeff

#5
That may be exactly what I'm looking for.  And if not, it's certainly the basis for a few macros :).  Thanks!
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Jeff

#6
Thinking about that... that would mean redefining the creator function for every element type I wish to create.  I do not think that would be worth the work :/
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Jeff

#7
Lutz,



Is it possible that the way events are passed back could be changed from a direct function call to an apply directive?  That would give us greater flexibility in hooking into the process.  It might even be an easy way to allow anonymous callbacks without much change...
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Lutz

#8
One could add a second gs:listen-apply and gs:check-event-apply in the file guiserver.lsp.



the classic gs:listen would do:
(eval-string event)

the gs:listen-apply would do:
(set 'event (replace xxxxxx event yyyy 0))
(eval-string event)


All incoming events are of the form:
"(<action> <p1> <p2> ....)"

so the regular expression would change this to:
"(apply '<action> '(<p1> <p2> ...))"

I can put it on my list for later (still churning out basics as fast as I can ;-) )



Lutz