newLISP Fan Club

Forum => newLISP newS => Topic started by: cormullion on December 16, 2006, 08:58:49 AM

Title: passing objects by reference or value
Post by: cormullion on December 16, 2006, 08:58:49 AM
Since it's quiet, can someone help me understand the difference between 'pass by value' and 'pass by reference'. I've looked at the manual, and I get the basic idea, but can't apply it easily yet in other situations...



Take this:


(set 'l:l (sequence 0 1000))

(define (reverse-list lst)
 (sort lst <)
 (nth-set 0 lst "first"))

(reverse-list l:l)


is this a pass by reference or a pass by value? What are the benefits of doing this? What happens if I miss out the colon..?
Title:
Post by: Lutz on December 16, 2006, 09:42:29 AM
You are passing the list by value. After the function is finished l:l is still the same.



To pass by reference in newLISP you have to pass the context alone:


(set 'l:l (sequence 1 10))

(define (reverse-list lst)  
 (sort lst:l >)
 (nth-set 0 lst:l "first"))

(reverse-list l) ; passed by reference

l:l => ("first" 9 8 7 6 5 4 3 2 1)


Note that I am passing 'l' not 'l:l'.



Also the default functor is only recognized as such when it is located in the 'functor' position, which is right after the opening parenthesis, and  when doing implicit indexing for rest/slice.



You can do that for 'set-nth' but not for 'sort':


(define (reverse-list lst)  
 (sort lst:l >)
 (nth-set (lst 0) "first"))

(reverse-list l) ; passed by reference


... will still work.



Lutz
Title:
Post by: cormullion on December 16, 2006, 10:13:53 AM
Hi Lutz. Thanks - one question, though:


(sort lst:l >)

here you've hard-coded the name of the list in the context? How would you write reverse-list so that it sorted the list passed in by the context?
Title:
Post by: Lutz on December 16, 2006, 10:32:19 AM
Perhaps in that case a macro is handier:


(define-macro (reverse-list)
    (sort (eval (args 0)) >) (nth-set 0 (eval (args 0)) "first"))

(set 'l (sequence 1 10))

(reverse-list l)

l => ("first" 9 8 7 6 5 4 3 2 1)


Lutz
Title:
Post by: cormullion on December 16, 2006, 11:25:04 AM
So - if I understand you - to pass by reference, you have to store your data in a context in a symbol with the same name as the context, then either pass the context name to the functions that can take implicit indexing of lists, or write a macro that lets you evaluate the context name and get the default symbol back before handing it to a function?



It doesn't seem right when I write it like this... Is there not an easier way to get the value of a default context symbol/functor...?
Title:
Post by: Lutz on December 16, 2006, 01:18:30 PM
Quoteto pass by reference, you have to store your data in a context in a symbol with the same name as the context ...


No, it does not have to be the the same name as the context. The default funtor is only important when when you have a syntax like this: (<functor> x y z) in implicit indexing, or (<number> [<number>]<functor>) in implicit rest/slice when <functor> can be the context name alone.



This example in the manual shows both cases passing a context, with and without the default functor:



file:///usr/share/newlisp/doc/newlisp_manual.html#pass_big


QuoteIs there not an easier way to get the value of a default context symbol/functor...?


... the value yes, but not the reference.



Lutz
Title:
Post by: cormullion on December 16, 2006, 02:50:04 PM
Is this passing by reference?


(set 'ctx:data (sequence 0 10000))

(define (reverse-list ctxt symbl)
  (sort (context ctxt symbl) >)
   (nth-set ((context ctxt symbl) 0) (rand 10)))

(define (p-ctxt ctxt)
 ; just a utility function
  (dotree (i ctxt)
    (println (eval i))))

(p-ctxt ctx)

(reverse-list ctx "data") ; passing 'ctx:data' by reference?


Is this going to be quicker than doing it the pass by value way?



(Thanks for the help Lutz btw)
Title:
Post by: Lutz on December 16, 2006, 03:58:40 PM
Yes, this is passing the list by reference and you will see the speed difference on that list of 10,000 items; on smaller lists of lets say a 100 items the difference would not be much.



Lutz



ps: there will be a more intuitive way of reference passing in the future.
Title:
Post by: cormullion on December 17, 2006, 12:23:50 AM
Thanks - I think I see the idea now. I didn't spot much of a speed benefit, because I rarely used lists large enough... to slow newLISP down...
Title:
Post by: Lutz on December 17, 2006, 05:52:55 AM
Quote didn't spot much of a speed benefit, because I rarely used lists large enough... to slow newLISP down...


... that is exactly the point. Also the the same design decisions in newLISP which make it a mostly value-passing language are the design features which also make it small and fast. Big list objects where speed will be affected are rare enough to handle them as a global variable or take the extra step of putting a namespace/context wrapper around it.



The implicit indexing mechanism, in which default functors are permitted as list specifiers passing only the context, make this an easy affair for all functions taking indexed lists, i.e. (set-nth (db idx) value). In this case  the context name 'db' will default to the 'db:db' variable name which contains the list.



Where you ran into difficulties was with functions which take the whole list as an argument, like 'sort' in your examples. In that case you have to fully spell out the list identifier, i.e. (sort db:data) or (sort db:db) in case of the default functor. The expression (sort db) alone would not be permitted and returns an error. Your last example is a way around this limitation and some changes I am working on will make this even easier and more intuitive.



Lutz
Title:
Post by: cormullion on December 17, 2006, 05:58:11 AM
Good to hear that more intuitive methods are on the way! So much better than when things get harder and more complicated with every release. My favourite situation is when there are hardly any exceptions to simple rules - life is much easier then, somehow.
Title:
Post by: cormullion on December 19, 2006, 01:30:48 PM
Hi lutz - could you explain your new stuff a bit for me? In 9.0.11 - you have this:


(set 'foo:foo '(a b c d e f g))

(define (mysort ctx func)
   (sort (eval (ctx)) func))

(mysort foo)


Why do you need the eval there? 'when evaluating a context by itself it returns the default symbol'. So (ctx) should then return the default symbol to sort...  After all, this would work in the mysort function:


(sort c:foo func)
Title:
Post by: Lutz on December 19, 2006, 02:13:50 PM
'sort' takes a list not the symbol. The following analogy between a normal symbol and the default symbol/functor explains it:


; example with normal symbol
(set 'lst '(4 3 6 5 4 3 7))
(sort lst) => (3 3 4 4 5 6 7)
(set 'ctx 'lst) => assigns the symbol

(symbol? ctx) => true
(sort ctx) => list expected in function sort : 'lst
(sort (eval ctx)) => (3 3 4 4 5 6 7)

; example with default symbol
(set 'foo:foo  '(4 3 6 5 4 3 7))
(sort foo:foo (3 3 4 4 5 6 7))
(set 'ctx foo) => assigns the context

(symbol? (ctx)) =>true
(sort (ctx)) => list expected in function sort : (ctx)
(sort (eval (ctx))) => (a b c d e f g)


Lutz
Title:
Post by: Lutz on December 19, 2006, 02:26:22 PM
... the advantage of returning the symbol is that we can do things like this:


(set 'foo:foo 123)

(set 'ctx foo)

(set (ctx) 999)

foo:foo => 999


so when we pass a context as a parameter and it gets assigned to a parameter variable 'ctx', we can access 3 things: the context, the default functor of the context and the contents of the default functor. And the contents of a default functor can be anything: a list, a number, a string a function or whatever.



Lutz
Title:
Post by: cormullion on December 19, 2006, 02:26:41 PM
ok - thanks. I think there's a hole in my understanding, i'll have to do some revision. :-)
Title:
Post by: Lutz on December 19, 2006, 02:31:38 PM
... think of evaluation like peeling off an onion ;). For undestanding of evaluation using newLISP in an interactive shell is reallly essential, because you can see/try out evaluation levels immedeately.



Lutz