Hi Andrés,

thanks for the "curri" examples!

> Sorry for the large email and even while pretending to be a clarifying text
> it is not :(

I think it was :)

> Also don't want to give an image of pretending to arrange picolisp's flaw
> design, it is absolutely not, not only I don't have the needed
> understanding of picolisp nor the knowledge to do so but I consider
> picolisp a pretty good design with very smart decisions. What I trying to

No worry, I didn't get that impression.


I knew these use cases, but until now never had practical situations where they
seemed useful. As we saw, they can be handled with the 'curry' function if
necessary, but I believe that PicoLisp has better mechanisms - like fexprs and
unlimited number of arguments to most functions - to *directly* express the
programmer's intentions.

For example,

> (mapcar ((curri **) 2)  (2 3 4))
> # equivalent to (mapcar '((X) (** 2 X)) (2 3 4))

would be written shorter in PicoLip

   (mapcar ** (2 3 4) (2 .))
   -> (4 9 16)


> (setq lstLen ((curri mapcar) length))           # equivalent to (de lstLen
> (lst) (mapcar length lst))
> (lstLen '((1) (2 3 4) (2 3)))

   (mapcar length '((1) (2 3 4) (2 3)))
   -> (1 3 2)



> yes, I have to study your classiCurry function to clearly understand how it
> works and yes maybe I'm a bit confused about dynamic binding usefulness due
> to my static binding background

You are not alone :) I try to explain more, perhaps it is also useful for other
readers here.


> (let N 4 (print N))
> 
> binds symbol N to value 4 inside print expression and so it prints 4, this
> is what we expect of let binding behaviour

Yes, though instead of "binds .. inside print" I would say "binds N to 4 inside
the body of 'let'.

A lexically binding system would indeed magically replace N in the print
expression, but in PicoLisp N is bound to 4 during the *time* this let body
runs.


> same for this code:
> 
> (let N 4 (+ 1 N))
> 
> we expect to return 5

Yes.


> In a coherent way we expect this code:
> 
> (let N 4 ((X) (+ X N)))      # erroneus code, I know it!
> 
> to return a function which adds 4 to its parameter   (and it would do it if
> it doesn't throw a X undefined error)

No. This code is not wrong per se, it just does not return a function.

As you know, Lisp evaluates expressions by taking the CAR as a function, and the
CDR as arguments to that function. So when the body of 'let' is executed, (X) is
expected to evaluate to a function, and the result of evaluating (+ X N) is
passed to that function.

There might even be situations where the above expression makes sense. The
problem is more that '+' expects a number, so let's modify it slightly:

   : (de X ()     # Define 'X' to return a function
      println )
   -> X

   : (let N 4 ((X) (+ (size X) N)))
   6
   -> 6

What happens? (X) evaluates to the function 'println' (the function *pointer* to
be exact, as 'println' is a primitive):

   : X
   -> (NIL println)

   : (X)
   -> 22966530359  # Function pointer to the code of 'println'

Then (+ (size X) N) is evaluated

   : (size X)
   -> 2

and passed to be printed. It prints 6.


> But here the problem is we cannot write an anonymous function (a lambda)
> this way, we *must* quote it:
> 
> (let N 4 '((X) (+ X N)))

No, you *did* write an anonymous function. It ignored the value of N though.

In PicoLisp, code and data are absolutely equivalent. Thus, the list

   ((X) (+ X N))

is, first of all, data. A list of two lists.

The fact that it might be interpreted somewhere else as a function doesn't
matter. The point is that in our head we want to return a function, but here we
have a chunk of data to be returned. Thus it must be quoted, to inhibit the
evaluation as seen above.


> but now the quoting has the effect of not evaluating the lambda and thus
> preserving it from let symbol binding, this is an undesiderable effect in
> my opinion

Yes, because there is no lexical (static) binding. All symbols are evaluated
solely at runtime. If you need the values statically, you have to do it
explicitly, e.g. with 'curry' or simply with list operations:

   (let N 4 (list '(X) (list '+ 'X N)))
   -> ((X) (+ X 4))

Voila, the expected function is returned.


> So what to do? (from here I'm just reasoning loud) well, the key is to
> separate function evaluation from function calling, what if there would be
> a mark for type function?

No, this would be a big drawback. You lose the equivalence of code and data.

The term "function evaluation" is the same as "function calling". What you mean
is "function building", i.e. the process of building a function from data, to be
evaluated (or called) later.


> 1- read the whole let expression
> 2- read-macros get executed inmmediately (really I think this is part of
> step 1)
> 3- perform the bindings in "let prog" expression
> 4- evaluate the "let prog" in te context set by let bindings (due to step 3)

Exactly. And this is what happens. The point is just that the bindings are
dynamic, they are valid while the prog runs.


And to summarize, you can call a list like ((X) (+ X N)) "data" or "function".

Whether you treat it as data,

   : (length '((X) (+ X N)))
   -> 2

or call it as a function,

   : (let N 4 ('((X) (+ X N)) 3))
   -> 7

is decided at runtime, depending on what you do with it.

♪♫ Alex
-- 
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe

Reply via email to