letn semantics for default vals and set: stable feature?

Started by hartrock, September 26, 2015, 04:55:10 AM

Previous topic - Next topic

hartrock

It could be helpful to mention in the manual (haven't found it), that setting symbols by 'Default variable values' (http://www.newlisp.org/CodePatterns.html#toc-4">//http://www.newlisp.org/CodePatterns.html#toc-4) or set goes from left to right, honoring the values of symbols already set: this is letn semantics (in contrast to let  'using symbol bindings as before the let statement').

Examples:

>
(define (foo (a1 1) (a2 (+ a1 1)) (a3 (+ a2 1)))
  (println "a1: " a1 ", a2: " a2 ", a3: " a3))
(foo)

(lambda ((a1 1) (a2 (+ a1 1)) (a3 (+ a2 1))) (println "a1: "
  a1 ", a2: " a2 ", a3: " a3))
a1: 1, a2: 2, a3: 3
3
>
(set 'a1 1 'a2 (+ a1 1) 'a3 (+ a2 1))
(println "a1: " a1 ", a2: " a2 ", a3: " a3)

3
a1: 1, a2: 2, a3: 3
3
>
If this is not a stable feature, this would be interesting to know, too.

Lutz

Thanks Hartrock. For the define function, this is already shown in one of the examples. For set an example will be added in the future manual.

hartrock

It's good to know for sure, that things like:

(define (do str , (len (length str)))
  ...)
(set 'str "foo"
     'len (length str))
are working reliably.

For bind with evaluation it is left to right, too:

>
(define-macro (foo)
  (local (a1 a2 a3)
    (bind (args) true)
    (println "a1: " a1 ", a2: " a2 ", a3: " a3)))

(lambda-macro ()
 (local (a1 a2 a3)
  (bind (args) true)
  (println "a1: " a1 ", a2: " a2 ", a3: " a3)))
> (foo (a1 1) (a2 (+ a1 1)) (a3 (+ a2 1)))
a1: 1, a2: 2, a3: 3
3
>

Is it possible to say - as a rule of thumb for easy memorizing (good not only for beginners) -, that let and letex are the exception of a general left to right evaluation for setting symbols? Or is it more complicated?

Lutz

define, letn, let , letex, all have to deal with two variable environments: the calling environment and the their own environment. Only define and letn will assign from the changed current own environment. let, letex will consistently refer to the calling environment:



> (define (foo (a 1) (b (+ 1 a))) (list a b))
(lambda ((a 1) (b (+ 1 a))) (list a b))
> (set 'a 123)
123
> (foo)
(1 2)
> (letn (a 1  b (+ a 1)) (list a b))
(1 2)

> (let (a 1  b (+ a 1)) (list a b))
(1 124)
> (letex (a 1  b (+ a 1)) '(a b))
(1 124)
>


If you name the local and variable assigned from the same, there is no difference:



> (define (foo (a a)) a)
(lambda ((a a)) a)
> (foo)
123
> (letn (a a) a)
123
> (letex (a a) a)
123
> (let (a a) a)
123




The functions set and bind only deal with one environment which gets updated incrementally.

hartrock

Quote from: "Lutz"

> (define (foo (a a)) a)
(lambda ((a a)) a)
> (foo)
123
> (letn (a a) a)
123
> (letex (a a) a)
123
> (let (a a) a)
123

Thanks for the example, where symbol a refers to different environments: inner new environment and outer one, where it has 123 value (inner val gets outer val assigned here).



I already have known all of these semantics, but I haven't been sure for 100%, that e.g.:

(set 'foo 1
     'bar (+ foo 1))
is and stays the same as:

(set 'foo 1)
(set 'bar (+ foo 1))
At a first look same semantics for both variants may seem to be 'natural'.

But it had been possible to keep a door open for some kind of optimization: to only use second variant for enforcing sequential set'ing of syms, and reserving first one for allowing set'ing them in parallel by using their values before set expression (which - if used (!) - would make a difference for bar, giving it the value of foo before set'ing this to 1 incremented by 1).

This door is not open, but IMO it had been a possibility to want to keep it open.

Lutz

Evaluation order in general is always depth first from left to right.



See also here: https://en.wikipedia.org/wiki/Tree_traversal">https://en.wikipedia.org/wiki/Tree_traversal



If you want to change that order you would write a fexpr using the define-macro function, which does not evaluate arguments on function entry. There also some - the more complex - built-in functions, which don't follow that order, like for and other flow control flow control functions. Lisp calls these special forms.

hartrock

Quote from: "Lutz"Evaluation order in general is always depth first from left to right.



See also here: https://en.wikipedia.org/wiki/Tree_traversal">https://en.wikipedia.org/wiki/Tree_traversal



If you want to change that order you would write a fexpr using the define-macro function, which does not evaluate arguments on function entry.

Thanks for clarification.


Quote from: "Lutz" There also some - the more complex - built-in functions, which don't follow that order, like for and other flow control flow control functions. Lisp calls these special forms.

At least for Scheme set! (haven't seen 'set' without '!' there) is mentioned as special form; see e.g. http://www.cs.bham.ac.uk/research/projects/poplog/paradigms_lectures/lecture11.html#setbang">//http://www.cs.bham.ac.uk/research/projects/poplog/paradigms_lectures/lecture11.html#setbang (to backup my remark regarding newLISP set).

abaddon1234

that let and letex are the exception of a general left to right evaluation for setting symbols? Or is it more complicated?

http://gclub.cawblog.com/2015/09/13/play-gclub-on-website/">เล่นจีคลับผ่านเว็บ

hartrock

Quote from: "abaddon1234"that let and letex are the exception of a general left to right evaluation for setting symbols? Or is it more complicated?

http://gclub.cawblog.com/2015/09/13/play-gclub-on-website/">เล่นจีคลับผ่านเว็บ

let and letex are using the outer binding of given symbols for initialization; e.g.]
sr@free:~$ newlisp
newLISP v.10.6.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

> (letex (x 1 y 2 z 3) '(x y z))
(1 2 3)
> ; but:
> (letex (x 1 y (+ x 1) z (+ y 1)) '(x y z))

ERR: value expected in function + : nil
> ; but:
> (set 'x 10 'y 11)
11
> (letex (x 1 y (+ x 1) z (+ y 1)) '(x y z))
(1 11 12)
>
[/code]
My point regarding set - denoted as a special form in other Lisps - has been, that it should stated somewhere in the manual, that it follows the general rule of left-to-right evaluation using formerly set bindings at the same level (there is no new level of bindings):

sr@free:~$ newlisp
newLISP v.10.6.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

> (set 'x 1 'y (+ x 1) 'z (+ y 1)) (list x y z)
3
(1 2 3)
>
That there are no new bindings introduced is clear, but set - as well as let - guaranteed to follow the general left-to-right evaluation rule has not been self-evident to me. Think of parallelization opportunities, if this would not been the case; on the other side parallelization could be dangerous in case of side-effects.

Possibly some thoughts along 'pure functional programming is good for parallelization, because it has no side effects' has made my thinking more complicated than needed here.



It is probably correct to say for newLISP, that the general rule of left-to-right evaluation:

[*] also applies for expressions like set, let - even if denoted as special forms in other Lisps -;
  • [*] does not apply only, if explicitely stated so.
  • [/list]

    Taking the definition of special forms from the newLISP manual (different from other Lisps):
    Quote from: "manual"
    Lisp functions that do not evaluate all or some of their arguments are called special forms.
    ; denoting something as special form there only implies, that the general rule of left-to-right evaluation does not apply to all arguments.




    Quote from: "Lutz"
    Evaluation order in general is always depth first from left to right.

    Where

    [*] arguments are evaluated - like for function calls and also for expressions like let -, and
  • [*] nothing is said about a special evaluation order,
  • [/list]
    left-to-right evaluation applies.



    Note: It is possible, of course, to write some macro for changing evaluation order by rearranging to be evaluated macro arguments.