[parenscript-devel] Repost: Re: 'use strict' and 'with' in loop

Peter Wood p.r.wood at gmail.com
Sun Mar 24 13:52:50 UTC 2013


The reply bounced due to excessive message size, and I couldn't cancel, so
I'm resending anyway ...

Hi Daniel

Thanks for your reply.  Strict mode makes life a bit easier because I have
a lot of scripts which get loaded and unloaded and I want to be careful
about namespaces.  In strict mode, eval is not allowed to introduce new
variables into the surrounding namespace.  'Arguments' are also simpler and
cleaner.  More importantly, I _think_ it makes javascript slightly less
insecure: see

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode#.22Securing.22_JavaScript

Maybe it's like bolting your front door even though all your windows are
still open, but presumably better than nothing.  It is also claimed that
using strict mode enables the engine to make a number of optimizations,
which certainly won't hurt if it's true.  (To sum up, I think it's a better
language in strict mode.)

I have read through your discussion with Vladimir from last year, and I can
see that my offhand suggestion was terribly naive.  <cringe>.  Multiple
value return in js is obviously an "interesting" problem.  Certainly, none
of the ideas I have considered myself can solve all the problems.  Doing a
_completely_ CL-alike multiple value return scheme would seem bound to make
the compiler a great deal more complex for quite small returns (no pun
intended).  I'm not too keen on the global variable,but that's my problem.
Of course, it would have to be declared if it should work in strict mode.

At the risk of embarrassing myself again: what about having a values macro
return a closure which holds the multiple-value array and which pops a
value off it every time it gets called? If you only want to return 1 value
from values, or null, the macro could deal with that, and if you want to
return a single function, ie (values (lambda () ...), the macro could wrap
it in a lambda, to allow the pop() to return the desired anonymous
function. The multiple-value-bind receiving macro could bind the functions
appropriately.  Something like vlet* could nest calls to
multiple-value-bind if you want to mix local assignment of strings and
numbers and calls to multiple-value-returning functions but only use the
first value.  It will still break if you bind a var to the return value of
a non-values-lambda-returning-function, since vlet* assumes that function
values must be popped (ie, funcalled). Just in case this idea is worth
something, I am attaching these three macros ('fvals, 'fmvb and 'vlet*)
which seem to do at least some of what is wanted, with some test cases.

And yes, it would be great to have compilation to Lua! Lua is very nice.

Regards,
Peter
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/parenscript-devel/attachments/20130324/6c09d738/attachment.html>
-------------- next part --------------
(ps:defpsmacro fvals (&rest vals)
  (cond ((zerop (length vals))
         `(progn null))
        ((= (length vals) 1)
         `(if (eql (typeof , at vals) :function)
              (lambda () , at vals)
              , at vals))
        (t `(let ((avals (ps:array ,@(reverse vals))))
              (lambda ()
                (pop avals))))))

(ps:defpsmacro fmvb (vars valf &body body)
  (if (= (length vars) 1)
      `(let ((, at vars ,valf))
         ,(if (or (numberp valf)
                  (stringp valf))
              `(progn , at body)
              `(progn (when (eql (typeof , at vars) :function)
                        (setf , at vars (funcall , at vars)))
                      , at body)))
      (let* ((fnname (gensym "fn"))
             (bindings (mapcar (lambda (v)
                                 `(,v (funcall ,fnname))) vars)))
        `(let* ((,fnname ,valf)
                , at bindings)
           , at body))))

(ps:defpsmacro vlet* (var-valfs &body body)
  (labels ((bind-vvfs (vvfs form)
             (if (null vvfs) `(progn , at form)
                 (destructuring-bind (v vf)
                     (car vvfs)
                   `(fmvb (,v)
                          ,vf
                          ,(bind-vvfs (cdr vvfs) form))))))
    (bind-vvfs var-valfs body)))                   

(defun foo ()
  (fvals 1 2))

#|
function foo() {
    var avals = [2, 1];
    return function () {
        return avals.pop();
    };
}; 
==> function()
|#

(defun bar ()
  (fvals 1))

#|
function bar() {
    return typeof(1) === "function" ? function () {
        return 1;
    } : 1;
};
==> 1
|#

(defun rab ()
  (fmvb (x) (bar) x))

#|
function rab() {
    var x = bar();
    if (typeof(x) === "function") {
        x = x();
    };
    return x;
};
==> 1
|#

(defun oof ()
  (fmvb (x y)
        (foo)
        (+ x y)))

#|
function oof() {
    var FN1633 = foo();
    var x = FN1633();
    var y = FN1633();
    return x + y;
};
==> 3
|#

(defun empty ()
  (fvals))

#|
function empty() {
    return null;
};
==> null
|#

(defun return-single-fn ()
  (fvals (lambda () :fine)))

#|
function returnSingleFn() {
    return typeof(function () {
        return "fine";
    }) === "function" ? function () {
        return function () {
            return "fine";
        };
    } : function () {
        return "fine";
    };
};
==> function()
|#

(defun return-multiple-fns ()
  (fvals (lambda () :fine)
         (lambda () :and)
         (lambda () :dandy)))

#|
function returnMultipleFns() {
    var avals = [function () {
        return "dandy";
    }, function () {
        return "and";
    }, function () {
        return "fine";
    }];
    return function () {
        return avals.pop();
    };
};
==> function()
|#

(defun getsinglefn (callme)
  (fmvb (fn)
        (funcall callme)
        fn))

#|
function getsinglefn(callme) {
    var fn = callme();
    if (typeof(fn) === "function") {
        fn = fn();
    };
    return fn;
};

getsinglefn(returnSingleFn)() ==> "fine"
getsinglefn(returnMultipleFns)() ==> "fine"

|#

(defun get-2-fns ()
  (fmvb (f1 f2)
        (returnmultiplefns)
        (ps:array f1 f2)))

#|
function get2Fns() {
    var FN3795 = returnMultipleFns();
    var f1 = FN3795();
    var f2 = FN3795();
    return [f1, f2];
};
==> [ function(), function() ]
get2Fns()[0]() ==> "fine"
get2Fns()[1]() ==> "and"
|#

(defun get-3-fns ()
  (fmvb (f1 f2 f3)
        (returnmultiplefns)
        (strcat (funcall f1) " "
                (funcall f2) " "
                (funcall f3))))

#|
function get3Fns() {
    var FN3796 = returnMultipleFns();
    var f1 = FN3796();
    var f2 = FN3796();
    var f3 = FN3796();
    return f1() + " " + f2() + " " + f3();
};
==> "fine and dandy"
|#

(defun tvlet ()
          (vlet* ((x 1)
                  (y "foo ")
                  (a (foo))
                  (b (bar))
                  (c 0))
                 (if (= (- a b) c)
                     (strcat "Yay, " y x)
                     :fail!)))
#|
function tvlet() {
    var x = 1;
    var y = "foo ";
    var a = foo();
    if (typeof(a) === "function") {
        a = a();
    };
    var b = bar();
    if (typeof(b) === "function") {
        b = b();
    };
    var c = 0;
    return a - b === c ? "Yay, " + y + x : "fail!";
};
==> "Yay, foo 1"
|#


(defun foo-key (a &key (b 0))
  (fvals (strcat "strings: a,  " a " and b, " b)
         (* a b)))
#|
function fooKey(a) {
    var _js1096 = arguments.length;
    for (var n1095 = 1; n1095 < _js1096; n1095 += 2) {
        switch (arguments[n1095]) {
        case "b":
            b = arguments[n1095 + 1];
        };
    };
    var b = "undefined" === typeof b ? 0 : b;
    var avals = [a * b, "strings: a,  " + a + " and b, " + b];
    return function () {
        return avals.pop();
    };
};
==> function()
|#


(defun use-foo-key1 (x y)
  (vlet* ((dftstr (fookey x)))
         (fmvb (ndftstr nought)
               (fookey x :b y))
         (ps:array dftstr ndftstr nought)))

#|
function useFooKey1(x, y) {
    var dftstr = fooKey(x);
    if (typeof(dftstr) === "function") {
        dftstr = dftstr();
    };
    var FN5919 = fooKey(x, "b", y);
    var ndftstr = FN5919();
    var nought = FN5919();
    return [dftstr, ndftstr, nought];
};
==> [ "strings: a,  1 and b, 0", "strings: a,  1 and b, 2", 2 ]
|#


(defun use-foo-key2 (x y)
  (fmvb (ndftstr nought)
        (fookey x :b y))
  (ps:array ndftstr nought))

#|
function useFooKey2(x, y) {
    var FN5959 = fooKey(x, "b", y);
    var ndftstr = FN5959();
    var nought = FN5959();
    return [ndftstr, nought];
};
==> [ "strings: a,  1 and b, 2", 2 ]
|#



More information about the parenscript-devel mailing list