From: Ricardo Wurmus <rekado@elephly.net>
To: Emacs developers <emacs-devel@gnu.org>
Subject: interactive closure — variables not bound
Date: Wed, 28 Sep 2016 08:21:10 +0200 [thread overview]
Message-ID: <87y42ch7e1.fsf@elephly.net> (raw)
Hi emacs-devel,
I’m currently attempting to rewrite some of the procedures and commands
in xwidget.el to do without the “title hack”. Currently, the return
value of any JavaScript snippet that is run in WebKit is stored in the
title and then read out by running JavaScript to retrieve the title.
With the WebKit2 API all JavaScript is run asynchronously. I’ve changed
“xwidget-webkit-execute-script” such that it takes an optional third
argument holding a Lisp callback, which is invoked with the return value
from JavaScript using “call1”. This works fine for the most part,
although using callbacks is a little less convenient than doing things
synchronously (which is not supported by WebKit2).
Now there is at least one instance where things aren’t as smooth. Take
“xwidget-webkit-insert-string” for example. It is a command using
either “read-string” or “read-passwd” dependent on the input field
type. The input field type is retrieved using JavaScript. Using the
title hack this can be implemented in a faux synchronous fashion:
(defun xwidget-webkit-insert-string (xw str)
…
(interactive
(let* ((xww …)
(field-value …)
(field-type (xwidget-webkit-execute-script-rv
xww
"findactiveelement(document).type;")))
(list xww
(cond ((equal "text" field-type)
(read-string "Text: " field-value))
((equal "password" field-type)
(read-passwd "Password: " nil field-value))
((equal "textarea" field-type)
…)))))
… ; do things with “xw” and “str”
)
Using callbacks instead we have a problem, because the return value of
the JavaScript is only available in the callback procedure, not in the
“interactive” form. To go around this problem I’m now trying to split
up “xwidget-webkit-insert-string” in two parts:
* “xwidget-webkit-insert-string”, which remains the interactive command
exposed to the user. It runs the JavaScript expression and in the
callback passes the return value to …
* … a second interactive command, which takes care of prompting the user
for a string to input.
This looks something like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(defun xwidget-webkit-insert-string ()
(interactive)
(let ((xww (xwidget-webkit-current-session)))
(xwidget-webkit-execute-script
xww
(concat xwidget-webkit-activeelement-js
"var res = findactiveelement(document); [res.value, res.type];")
;; This is called with “call1” from within “xwidget.c” when the
;; JavaScript return value is available
(lambda (field)
(let* ((field-value (car field))
(field-type (cadr field)))
(call-interactively
(lambda (str)
(interactive
(list (cond ((equal "text" field-type)
(read-string "Text: " field-value))
((equal "password" field-type)
(read-passwd "Password: " nil field-value))
((equal "textarea" field-type)
(xwidget-webkit-begin-edit-textarea xww field-value)))))
(xwidget-webkit-execute-script
xww
(format "findactiveelement(document).value='%s'" str)))))))))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The problem with this definition is that it doesn’t work (the other
problem is that I’m replacing one ugly hack with another). At runtime
Emacs says that “field-type” is undefined. At compile time Emacs says
that in the callback “xww”, “field-value”, and “field-type” are
references to free variables.
But why? Isn’t this a closure? “field-value” and “field-type” are
let-bound inside of the callback, so they should be available in the
scope of the interactive lambda’s “cond”.
I’ve also tried to do without “interactive” and just use “read-string”
directly, but while this gives me a prompt at runtime it also freezes
Emacs and eats up all my memory (which might be normal when using
“read-string” in a lambda that’s called from C with “call1”, dunno).
~~ Ricardo
PS: I’ve sent an email to assign@gnu.org a week ago with a completed
request form template, but haven’t received any response yet.
next reply other threads:[~2016-09-28 6:21 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-09-28 6:21 Ricardo Wurmus [this message]
2016-09-28 12:41 ` interactive closure — variables not bound Stefan Monnier
2016-09-28 21:12 ` Ricardo Wurmus
2016-09-29 0:07 ` Stefan Monnier
2016-09-29 9:09 ` Ricardo Wurmus
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87y42ch7e1.fsf@elephly.net \
--to=rekado@elephly.net \
--cc=emacs-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).