Anonymous callbacks

Guiserver, GTK-server, OpenGL, PostScript,
HTML 5, MIDI, IDE
Locked
Jeff
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Anonymous callbacks

Post by 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...

Artful code

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

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:

Code: Select all

(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:

Code: Select all

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
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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...

Artful code

Jeff
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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...

Artful code

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

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

Code: Select all

(gs:button 'TheButton (lambda (x) (println (upper-case x)) "press-me")
... passed into the new definition of gs:button.

Code: Select all

(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:

Code: Select all

; event received in gs:listen

event => (KGZvbyAoeCkgKHByaW50bG4gKHVwcGUtcmNhc2UgeCkpKQ== "TheButton")

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

Lutz

Jeff
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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...

Artful code

Jeff
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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...

Artful code

Jeff
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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...

Artful code

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

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

the classic gs:listen would do:

Code: Select all

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

Code: Select all

(set 'event (replace xxxxxx event yyyy 0))
(eval-string event)
All incoming events are of the form:

Code: Select all

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

Code: Select all

"(apply '<action> '(<p1> <p2> ...))"
I can put it on my list for later (still churning out basics as fast as I can ;-) )

Lutz

Locked