newLisp without collateral effects

Started by ale870, December 16, 2010, 01:46:06 AM

Previous topic - Next topic

ale870

#15
Ciao @cormullion, I tried it but does not solve side-effects problems.

Maybe you need to use (copy) but assigning the result to a new local variable:


(map
      (fn (x)
         (setq myCounter (inc (copy counter)) ) ; @@@@@@@@@@@@@@@@@@@
         (bad-boy)
         (println $idx " - " "Value: " myCounter " --> " (upper-case x))  )
      '("a" "b" "c") )


But this approach is similar to (let) usage I tried before.
--

Lutz

#16
(define (counter:counter x)
  (println $idx "-->" (upper-case x)) (inc counter:cnt)
  (bad-boy))

(define (bad-boy)
  (println "count: " counter:cnt))

(map counter '("a" "b" "c"))


I also added two subchapters to: http://www.newlisp.org/downloads/CodePatterns.html#toc-9">http://www.newlisp.org/downloads/CodePa ... html#toc-9">http://www.newlisp.org/downloads/CodePatterns.html#toc-9 showing "Functions with memory" and "Functions using self modifying code". Both are applicable to your problem.

ale870

#17
Nice info @Lutz, thank you! I will check them immediately!
--

ale870

#18
@Lutz can you explain me this one:

(define (sum (x 0)) (inc 0 x))


OK I tried it and works, but I don't understand HOW it works!
--

johu

#19
> (define (sum (x 0)) (inc 0 x))
(lambda ((x 0)) (inc 0 x))
> (sum 1)
1
> sum
(lambda ((x 0)) (inc 1 x))
> (sum 3)
4
> sum
(lambda ((x 0)) (inc 4 x))
>

Note that the lambda expression has been changed after executing sum.

The built-in function inc is destructive.

But this way might not be used in map.



> (define (myFunc x) (println "Value " (inc 0 1) " --> " x))
(lambda (x) (println "Value " (inc 0 1) " --> " x))
> (map myFunc '("A" "B" "C"))
Value 1 --> A
Value 1 --> B
Value 1 --> C
("A" "B" "C")
> (dolist (x '("A" "B" "C")) (myFunc x))
Value 1 --> A
Value 2 --> B
Value 3 --> C
"C"
> (let (res) (dolist (x '("A" "B" "C")) (push (myFunc x) res -1)))
Value 4 --> A
Value 5 --> B
Value 6 --> C
("A" "B" "C")
>

Kazimir Majorinc

#20
I think that appropriate approach in this situation would be:


(local(counter)
  (map (lambda(x)(inc counter)(print counter "-->" x ";  "))
       '(A B C)))
(print counter)

1-->A;  2-->B;  3-->C;  nil



because side effects are really needed here, just you do not need it for long. Without some side effects, there would be 1-->A;  1-->B;  1-->C;  nil. If you want to protect yourself against accidental name clashes, it is more general problem. One really must care about unique names. For example, if you want that in



(define 'bad-boy (lambda()(setq counter 2000)))



some specific counter is used, not counter that can be supplied from environment that calls bad-boy, you must give it specific name. In Common Lisp, there is convention to use globals on this way:



(define 'bad-boy (lambda()(setq *counter* 2000)))



but you can use your own notation. For example, I wrote two helper functions set-protected1 and set-protected2. http://kazimirmajorinc.blogspot.com/2009/12/symbols-as-sexprs.html">http://kazimirmajorinc.blogspot.com/200 ... exprs.html">http://kazimirmajorinc.blogspot.com/2009/12/symbols-as-sexprs.html



(set-protected1 'bad-boy (lambda()(setq counter 2000)) '(counter))



==>



(lambda () (setq .<.bad-boy____counter.>. 2000))



So, variable counter is renamed to the form that cannot be accidentally rewritten. It actually works:


(load "http://instprog.com/Instprog.default-library.lsp")

(set-protected1 'bad-boy
                (lambda()(setq counter 'ruin)
                         (setq temp 'destroy))
                '(counter temp))
 
(map (begin (set-protected1 'temp
                            (lambda(x)
                                (inc counter)
                                (bad-boy)
                                (println " Value " counter "===>" x))
                             '(x counter))
             temp)
      '("A" "B" "C"))
     
(println counter)

Value 1===>A

 Value 2===>B

 Value 3===>C

nil




However, contexts were designed exactly for that purpose, and I defined my functions only to see how it can be done without contexts, as proof-of-the-concept. Itistoday (Greg Slepak) defined the function similar to protected1 that uses contexts to ensure uniqueness of the used variables.
http://kazimirmajorinc.com/\">WWW site; http://kazimirmajorinc.blogspot.com\">blog.

Lutz

#21
Re: self modifying functions
QuoteBut this way might not be used in map.

Actually you can, if you quote myFunc when mapping:
> (define (myFunc x) (println "Value " (inc 0 1) " --> " x))
(lambda (x) (println "Value " (inc 0 1) " --> " x))
> (map 'myFunc '("A" "B" "C")) ; <-- note the quote before myFunc
Value 1 --> A
Value 2 --> B
Value 3 --> C
("A" "B" "C")
>

Without the quote map will construct expressions like this when iterating:
((lambda (x) ...) "A")
((lambda (x) ...) "B")
((lambda (x) ...) "C")

When quoting myFunc, map will construct this kind:
(myFunc "A")
(myFunc "B")
(myFunc "C")

The symbol reference to myFunc causes the same function to be used over and over and modified.



All built-in destructive functions in newLISP can do in place modification, if you want to (*). If this is a good programming style, is an entirely different topic. Just like the underlying question if code=data is a good thing or not. I think it's a good thing in the right circumstances.



(*) this is also tested in newlisp-x.x.x/qa-specific-tests/qa-inplace

johu

#22
Thank you, Lutz.



I know the difference between quoted and unquoted in map, also apply.

ale870

#23
Hello,



your posts are really interesting.



I think @Kazimir's solution is good, since it creates variables only for the needed time, and it is a more "classic programming" approach.

About @Lutz's solution, that is the one I was looking for, in fact the only way to eliminate side-effects was to implement a kind of "memory" inside the function self.

Some languages do not apply the concept of self-modifying code ( I remember self-modifying code when I made programs in assembler for MC68000!). Some languages use a "special" kind of local variables which can maintain their value across multiple function calls, even if the variable is not visible externally of the function self.

I think that self modifying code is a good approach due to the mature of lisp in general. This is a clear example sue to push to the limit of the dual nature of lisp (data<->programs). This concept remember me the dual nature of the photons (energy<->matter) ;-)



I never thought to use destructive functions in this way, and I like it! I will spend some time to better learn about them!
--