For Programmers: Free Programming Magazines  


Home > Archive > Functional > May 2007 > Lisp for the C21









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Lisp for the C21
Mark Tarver

2007-05-04, 4:10 am

QUOTE
Python has readable syntax, a huge library, and bindings for what
seems like every major in linux. Perl has CPAN. It seems with those
languages if you want to do something all you have to do is import
functionality from a library someone had written and use that.

In lisp you'd have to "roll your own".

Why should I keep on learning lisp when there are python and perl?
UNQUOTE

I can see where this guy is coming from (though I can't find the
original post any more (?)).

See my remarks on the Lisp for the Twenty First Century

http://www.lambdassociates.org/lC21.htm

for our take on this one.

Mark


Pascal Costanza

2007-05-05, 8:02 am

Jon Harrop wrote:
> Mark Tarver wrote:
>
> On a related note, OCaml has had optional and labelled arguments in the
> presence of currying for some time.


So does OCaml know when a function call is "done"?

> The OCaml equivalent of Lisp's "rest" is to accept a list, of course.


No, that's not equivalent.


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Chris Smith

2007-05-05, 7:04 pm

Pascal Costanza <pc@p-cos.net> wrote:
>
> No, that's not equivalent.


It seems to me that it's precisely equivalent. How is it not
equivalent?

--
Chris Smith
Pascal Costanza

2007-05-05, 7:04 pm

Chris Smith wrote:
> Pascal Costanza <pc@p-cos.net> wrote:
>
> It seems to me that it's precisely equivalent. How is it not
> equivalent?


In one case, I have to say (foo (list 1 2 3)), in the other I can simply
say (foo 1 2 3).


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Chris Smith

2007-05-05, 7:04 pm

Pascal Costanza <pc@p-cos.net> wrote:
> Chris Smith wrote:
>
> In one case, I have to say (foo (list 1 2 3)), in the other I can simply
> say (foo 1 2 3).


Somehow I doubt that's the most significant syntactic change necessary
to convert Lisp code into OCaml.

--
Chris Smith
Pascal Costanza

2007-05-05, 7:04 pm

Chris Smith wrote:
> Pascal Costanza <pc@p-cos.net> wrote:
>
> Somehow I doubt that's the most significant syntactic change necessary
> to convert Lisp code into OCaml.


:-D

Sure, but there is a convenience issue involved.

Assume you have developed a binary operator. It's natural to express it
as a two-argument function:

(defun foo (x y) ...) ;; Common Lisp
(define (foo x y) ...) ;; Scheme

Later on you realize that it is better to turn this into a function for
an arbitrary number of arguments. You can simply change the definition
like this:

(defun foo (&rest args) ...) ;; Common Lisp
(define (foo . args) ...) ;; Scheme

There is no need to change any of the already existing call sites.


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Raffael Cavallaro

2007-05-05, 7:04 pm

On 2007-05-04 11:32:14 -0400, Paul Rubin <http://phr.cx@NOSPAM.invalid> said:

> Anyone who didn't love lisp in the 20th century has no heart.
> Anyone who still loves it in the 21st, has no head.


By the same logic we should all be conservative Republicans. Given this
implication, I'll stick with lisp, thanks.

Jon Harrop

2007-05-05, 7:04 pm

Pascal Costanza wrote:
> (defun foo (&rest args) ...) ;; Common Lisp
> (define (foo . args) ...) ;; Scheme
>
> There is no need to change any of the already existing call sites.


There is a converse "convenience issue" from the OCaml:

let f x y = x+y
f 2 3

to the Lisp:

(defvar f (lambda (x) (lambda (y) (+ x y))))
(funcall (funcall f 2) 3)

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Chris Smith

2007-05-05, 7:04 pm

Pascal Costanza <pc@p-cos.net> wrote:
> Assume you have developed a binary operator. It's natural to express it
> as a two-argument function:
>
> (defun foo (x y) ...) ;; Common Lisp
> (define (foo x y) ...) ;; Scheme
>
> Later on you realize that it is better to turn this into a function for
> an arbitrary number of arguments.


I suppose this is a matter of perspective. I would tend to think that,
instead of it being better to convert it into an operator on a lit, that
it would be better to define a new operator that generalizes it:

fooAll = foldr foo foozero

where foozero is the identity of the operator foo (okay, I'm writing
Haskell rather than ML; but the same would work there if I weren't too
lazy to look up what folds are like in ML). If foo doesn't have an
identity, then this would be a cue to me to wonder whether generalizing
to a list makes any sense at all. I would then start asking questions
about what this operator should do with an empty list, for example.

> You can simply change the definition like this:
>
> (defun foo (&rest args) ...) ;; Common Lisp
> (define (foo . args) ...) ;; Scheme
>
> There is no need to change any of the already existing call sites.


Indeed, but neither is there a need to change the existing call sites
simply because you defined a fold. You'd just keep using the original
operator.

--
Chris Smith
Neelakantan Krishnaswami

2007-05-05, 7:04 pm

In article <5a32s3F2jbflkU1@mid.individual.net>, Pascal Costanza wrote:
> Jon Harrop wrote:
>
> So does OCaml know when a function call is "done"?


When you partially some keyword arguments, you get a new function that
needs fewer keyword arguments:

# let foo ~a ~b = a + b

val foo : a:int -> b:int -> int = <fun>


# let f = foo ~b:6

val f : a:int -> int = <fun>


# let x = f ~a:5

val x : int = 11

So you're "done" when you don't have any more arguments to supply.

--
Neel R. Krishnaswami
neelk@cs.cmu.edu
Pascal Costanza

2007-05-05, 7:04 pm

Neelakantan Krishnaswami wrote:
> In article <5a32s3F2jbflkU1@mid.individual.net>, Pascal Costanza wrote:
>
> When you partially some keyword arguments, you get a new function that
> needs fewer keyword arguments:
>
> # let foo ~a ~b = a + b
>
> val foo : a:int -> b:int -> int = <fun>
>
>
> # let f = foo ~b:6
>
> val f : a:int -> int = <fun>
>
>
> # let x = f ~a:5
>
> val x : int = 11
>
> So you're "done" when you don't have any more arguments to supply.


OK, that explains keyword arguments, but what about optional arguments?
How does OCaml distinguish between the cases where I don't want to pass
the (remaining) optional argument and where I simply haven't passed it yet?

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Jon Harrop

2007-05-05, 7:04 pm

Pascal Costanza wrote:
> OK, that explains keyword arguments, but what about optional arguments?
> How does OCaml distinguish between the cases where I don't want to pass
> the (remaining) optional argument and where I simply haven't passed it
> yet?


Optional arguments are succeeded by >0 non-optional arguments in OCaml.

Look at my Sudoku solver, for example:

http://www.ffconsultancy.com/ocaml/sudoku/

The "search" function begins:

let rec search ?(x=0) ?(y=0) f accu = match x, y with

That is a curried function with two optional arguments "x" and "y" that both
default to zero.

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Neelakantan Krishnaswami

2007-05-05, 7:04 pm

In article <5a441pF2n6urcU1@mid.individual.net>, Pascal Costanza wrote:
>
> OK, that explains keyword arguments, but what about optional
> arguments? How does OCaml distinguish between the cases where I
> don't want to pass the (remaining) optional argument and where I
> simply haven't passed it yet?


It can't distinguish it, so you get a compiler warning:

# let f ?(x=6) = x + x

Warning: This optional argument cannot be erased
val f : ?x:int -> int = <fun>

The compiler is saying it won't fill in the optional argument
automatically when you use it, because it can't tell when you mean to
curry and when you mean to pass it in optionally.

The idiom to avoid this problem is to have a last mandatory unit
argument -- this makes it clear to the compiler (and the reader!) that
all the arguments you want to pass it have been passed in:

# let f ?(x=6) () = x + x
val f : ?x:int -> unit -> int = <fun>

# f() (* Leave the optional arg out and use the default *)
- : int = 12

# f ~x:3 () (* Supply the optional arg *)
- : int = 6


Ocaml doesn't have rest arguments, though.

--
Neel R. Krishnaswami
neelk@cs.cmu.edu
Pascal Costanza

2007-05-05, 7:04 pm

Jon Harrop wrote:
> Pascal Costanza wrote:
>
> There is a converse "convenience issue" from the OCaml:
>
> let f x y = x+y
> f 2 3
>
> to the Lisp:
>
> (defvar f (lambda (x) (lambda (y) (+ x y))))
> (funcall (funcall f 2) 3)


Ah, the joy of an extensible programming language:

In Scheme:

(define cl-caller
(lambda (proc args nof-args nof-required-args)
(lambda more-args
(let ((new-args (append args more-args))
(nof-new-args (+ nof-args (length more-args))))
(if (>= nof-new-args nof-required-args)
(apply proc new-args)
(cl-caller proc new-args nof-new-args nof-required-args))))))

(define-syntax curried-lambda
(syntax-rules ()
((curried-lambda lambda-list body ...)
(let ((nof-required-args (length 'lambda-list)))
(lambda args
(let ((nof-args (length args))
(proc (lambda lambda-list body ...)))
(if (>= nof-args nof-required-args)
(apply proc args)
(cl-caller proc args nof-args nof-required-args))))))))

#;> (define foo (curried-lambda (x y z) (+ x y z)))
#;> (foo 5 6 7)
18
#;> (((foo 5) 6) 7)
18
#;> ((foo 5 6) 7)
18


In Common Lisp:

(defun cf-caller (function args nof-args nof-required-args)
(lambda (&rest more-args)
(declare (dynamic-extent more-args))
(let ((new-args (append args (copy-list more-args)))
(nof-new-args (+ nof-args (length more-args))))
(if (>= nof-new-args nof-required-args)
(apply function new-args)
(cf-caller function new-args nof-new-args nof-required-args)))))

(defmacro define-curried-function (name (&rest lambda-list) &body body)
(let ((args (gensym))
(nof-args (gensym))
(nof-required-args (length lambda-list))
(function (gensym)))
`(defun ,name (&rest ,args)
(declare (dynamic-extent ,args))
(let ((,nof-args (length ,args))
(,function (lambda ,lambda-list ,@body)))
(if (>= ,nof-args ,nof-required-args)
(apply ,function ,args)
(cf-caller ,function
(copy-list ,args) ,nof-args ,nof-required-args))))))

CL-USER 35 > (define-curried-function foo (x y z) (+ x y z))
FOO

CL-USER 36 > (foo 5 6 7)
18

CL-USER 37 > (funcall (foo 5 6) 7)
18

CL-USER 38 > (funcall (funcall (foo 5) 6) 7)
18


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Pascal Costanza

2007-05-05, 7:04 pm

Neelakantan Krishnaswami wrote:
> In article <5a441pF2n6urcU1@mid.individual.net>, Pascal Costanza wrote:
>
> It can't distinguish it, so you get a compiler warning:
>
> # let f ?(x=6) = x + x
>
> Warning: This optional argument cannot be erased
> val f : ?x:int -> int = <fun>
>
> The compiler is saying it won't fill in the optional argument
> automatically when you use it, because it can't tell when you mean to
> curry and when you mean to pass it in optionally.
>
> The idiom to avoid this problem is to have a last mandatory unit
> argument -- this makes it clear to the compiler (and the reader!) that
> all the arguments you want to pass it have been passed in:
>
> # let f ?(x=6) () = x + x
> val f : ?x:int -> unit -> int = <fun>
>
> # f() (* Leave the optional arg out and use the default *)
> - : int = 12
>
> # f ~x:3 () (* Supply the optional arg *)
> - : int = 6
>
>
> Ocaml doesn't have rest arguments, though.


OK, thanks a lot for the explanation. My implicit assumption based on
Lisp was that optional, etc., arguments are put at the end of an
argument list. I haven't thought about the idea to put them in front...


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Jon Harrop

2007-05-05, 7:04 pm

Pascal Costanza wrote:
> Ah, the joy of an extensible programming language:


Greenspun.

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Jon Harrop

2007-05-05, 7:04 pm

Neelakantan Krishnaswami wrote:
> Ocaml doesn't have rest arguments, though.


Just pass a list.

With no static typing Lisp has no notion of statically-enforced arity so
Lisp functions always pass a list of arguments whether that is what you
really want or not.

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Jon Harrop

2007-05-05, 7:04 pm

Pascal Costanza wrote:
> Chris Smith wrote:
>
> In one case, I have to say (foo (list 1 2 3)), in the other I can simply
> say (foo 1 2 3).


Your translation to Lisp is incorrect. Lisp cannot distinguish between these
two OCaml implementations because it lacks static typing:

let add (x, y) = x+y;;
let add [x; y] = x+y;;

For these purposes, a tuple (such as the pair in the first case) is simply a
list that is statically guaranteed to have two elements.

So the Lisp equivalent to both of these definitions is:

(defun foo (x y) (+ x y))

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Neelakantan Krishnaswami

2007-05-06, 7:03 pm

In article <463cfcce$0$8759$ed2619ec@ptn-nntp-reader02.plus.net>, Jon Harrop
wrote:
> Neelakantan Krishnaswami wrote:
>
> Just pass a list.
>
> With no static typing Lisp has no notion of statically-enforced
> arity so Lisp functions always pass a list of arguments whether that
> is what you really want or not.


Actually, it is coherent to talk about a function of one argument in
Lisp, because a retraction(*) between the universal domain of Lisp
values and the domain of continuous functions between Lisp values
exists.

Furthermore, you don't want it to be true, because there's *more*
stuff in ML's semantics than in Lisp's semantics, because you can
easily define universal domains in ML:

type univ = D of univ -> univ

The definability of this type means that if you think that Lisp
doesn't have a notion of arity, then neither can ML.

(*) A retraction between two domains C and D is a pair of functions
f : C -> D and g : D -> C, such that g o f = id, and f o g is below
the identity (ordered pointwise). Intuitively, you can think of the
retraction as "picking out" the elements of C in D. So when someone
says "functions from integers to integers" in Lisp, they're talking
about the appropriate retraction.


--
Neel R. Krishnaswami
neelk@cs.cmu.edu
Jon Harrop

2007-05-06, 7:03 pm

Neelakantan Krishnaswami wrote:
> In article <463cfcce$0$8759$ed2619ec@ptn-nntp-reader02.plus.net>, Jon
> Harrop wrote:
>
> ...
> type univ = D of univ -> univ
>
> The definability of this type means that if you think that Lisp
> doesn't have a notion of arity, then neither can ML.


I did say "statically-enforced arity". Lisp clearly has a notion of arity,
it is just tested a run-time.

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Markus E Leypold

2007-05-06, 7:03 pm


Pascal Costanza <pc@p-cos.net> writes:

> Neelakantan Krishnaswami wrote:
>
> OK, that explains keyword arguments, but what about optional
> arguments? How does OCaml distinguish between the cases where I don't
> want to pass the (remaining) optional argument and where I simply
> haven't passed it yet?


Optional arguments in tail position "can not be erased".:

# let f x ?y = x*4;;
^
Warning: This optional argument cannot be erased
val f : int -> ?y:'a -> int = <fun>
#
# let f x ?y ~z = x*4;;
^
Warning: This optional argument cannot be erased
val f : int -> ?y:'a -> z:'b -> int = <fun>
#

BTW: I have never understood why a language which has lists and a
notation to construct list values should ever need variadic arguments.

Regards -- Markus
Pascal Costanza

2007-05-06, 7:03 pm

Markus E Leypold wrote:

> BTW: I have never understood why a language which has lists and a
> notation to construct list values should ever need variadic arguments.


Because the way how arguments lists are represented is an implementation
detail, and I don't want to deal with implementation details in client code.

It doesn't make sense that in some cases I can say (foo 1 2 3) and in
others I have to say (bar (list 1 2 3)). Especially, I have to remember
which function understands which parameter passing convention.
Programming languages are there to abstract away over such details.


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Joachim Durchholz

2007-05-06, 7:03 pm

Pascal Costanza schrieb:
> Markus E Leypold wrote:
>
>
> Because the way how arguments lists are represented is an implementation
> detail, and I don't want to deal with implementation details in client
> code.


That argument cuts both ways. One could reasonably argue that the
implementation of an argument list as a "list" data structure is an
implementation detail that shouldn't be visible to programmers.
(Not easily discerned in Lisp where lists are even more ubiquitous.)

Regards,
Jo
Markus E Leypold

2007-05-06, 7:03 pm


Pascal Costanza <pc@p-cos.net> writes:

> Markus E Leypold wrote:
>
>
> Because the way how arguments lists are represented is an
> implementation detail, and I don't want to deal with implementation
> details in client code.
>
> It doesn't make sense that in some cases I can say (foo 1 2 3) and in
> others I have to say (bar (list 1 2 3)). Especially, I have to


I think it is a huge semantic difference wether a function accepts a
list or a fixed number of arguments. It should be explicit if a
function is working on a list. Supposedly such a list is also
homogenous, in the sense that all items have a certain type or obey
certain constraints.

I like Lisp. Nonetheless: I've never missed variadic functions in
Ocaml or Ada.

IMHO (+ 3 4) and (sum '(1 2 3 4 1 2)) are two conceptionally
completely different things. You see that, if you look at (sum '())
and (sum '(13)). Or at least I see. :-).

> remember which function understands which parameter passing
> convention. Programming languages are there to abstract away over such
> details.


Regards -- Markus



Jon Harrop

2007-05-06, 7:03 pm

Markus E Leypold wrote:
> BTW: I have never understood why a language which has lists and a
> notation to construct list values should ever need variadic arguments.


Static typing: variadic arguments are statically checked in OCaml and
different arguments may have different types (lists are homogeneous).

Currying: you can partially apply optional arguments in OCaml.

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Jon Harrop

2007-05-07, 8:03 am

Pascal Costanza wrote:
> It doesn't make sense that in some cases I can say (foo 1 2 3) and in
> others I have to say (bar (list 1 2 3))


Except Lisp always passed by list so both cases are (foo 1 2 3) in Lisp (but
not in other languages).

> Programming languages are there to abstract away over such details.


I'd rather abstractions that are forced upon me didn't make reading
the "n"th function argument O(n).

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Frederic Beal

2007-05-07, 8:03 am

On 2007-05-07, Jon Harrop <jon@ffconsultancy.com> wrote:
> Pascal Costanza wrote:
>
> Except Lisp always passed by list so both cases are (foo 1 2 3) in Lisp (but
> not in other languages).


With all due respect, what you say would be a bit daft. Lisp is
perfectly able to tell the difference between
(foo 1 2 3) and (foo (list 1 2 3)) and (foo (list 1) (list 2 3)).
In the first case, the _list_ of arguments contains _three_ items,
which are numbers, in the second case, the _list_ of arguments contains
_one_ argument, which is a list (which contains _three_ numbers), and
in the third case, etc. Of course there is no reason why the three
would be treated the same way (this would be most peculiar).

>
> I'd rather abstractions that are forced upon me didn't make reading
> the "n"th function argument O(n).


It does not. You have misconceptions about how Lisp works (and I say
that, even though I prefer Haskell now, but for REAL reasons).

--
Frederic
Jon Harrop

2007-05-07, 8:03 am

Frederic Beal wrote:
> On 2007-05-07, Jon Harrop <jon@ffconsultancy.com> wrote:
>
> With all due respect, what you say would be a bit daft.


Then you've misunderstood what I said.

> Lisp is perfectly able to tell the difference between
> (foo 1 2 3) and (foo (list 1 2 3)) and (foo (list 1) (list 2 3)).
> In the first case, the _list_ of arguments contains _three_ items,
> which are numbers, in the second case, the _list_ of arguments contains
> _one_ argument, which is a list (which contains _three_ numbers), and
> in the third case, etc.


Exactly. So a translation of this:

* (defun tail (head &rest tail) tail)
* (tail 1 2 3)
(2 3)

into OCaml/F# is:

# let tail (head::tail) = tail;;
# tail [1; 2; 3];;
- : int list = [2; 3]

>
> It does not.


Given a (linked) list of arguments, what is the complexity of getting
the "n"th argument? When does this apply in Lisp?

> You have misconceptions about how Lisp works (and I say
> that, even though I prefer Haskell now, but for REAL reasons).


Perhaps, but I have yet to hear a decent answer to the above question.

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Pascal Costanza

2007-05-07, 8:03 am

Joachim Durchholz wrote:
> Pascal Costanza schrieb:
>
> That argument cuts both ways. One could reasonably argue that the
> implementation of an argument list as a "list" data structure is an
> implementation detail that shouldn't be visible to programmers.
> (Not easily discerned in Lisp where lists are even more ubiquitous.)


Well, there are a number of trade offs involved, of course.

The fact that you can accept any list of argument as an instance of the
actual list data structure means that it is, for example, very
straightforward to implement wrapper functions, like this:

(defun foo-wrapper (&rest args)
... do something extra ...
(apply original-foo args))

This works no matter what because in Lisp you don't have to worry about
static types. [1]

A very interesting idiom, and one that is relatively often used in
conjunction with CLOS, is to use wrapper functions in combination with
keyword arguments. Since keyword arguments aren't processed at compile
time, but are passed as part of a &rest argument, it is very
straightforward to "override" keyword arguments, roughly like this:

(defun foo-wrapper (&rest args &key key1 key2)
(apply original-foo
:key2 new-value-for-key2
args)) ;; all other keyword arguments remain the same

So in these cases, you absolutely have to be aware of the fact that
arguments lists are indeed represented as lists at runtime, and how they
are processed.

However, this is all at the implementation side of a library, the client
side doesn't have to see what's going on behind the scenes.



Pascal


[1] That's a trade off - I am not necessarily saying here that dynamic
typing is always superior.

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Pascal Costanza

2007-05-07, 8:03 am

Markus E Leypold wrote:
> Pascal Costanza <pc@p-cos.net> writes:
>
>
> I think it is a huge semantic difference wether a function accepts a
> list or a fixed number of arguments. It should be explicit if a
> function is working on a list. Supposedly such a list is also
> homogenous, in the sense that all items have a certain type or obey
> certain constraints.


No, for example in the case of keyword arguments, such a list is
definitely not homogeneous.

> I like Lisp. Nonetheless: I've never missed variadic functions in
> Ocaml or Ada.


Sure, those languages make completely different trade offs.

> IMHO (+ 3 4) and (sum '(1 2 3 4 1 2)) are two conceptionally
> completely different things. You see that, if you look at (sum '())
> and (sum '(13)). Or at least I see. :-).


Well, we have (+ 3 4) and (+ 1 2 3 4 1 2) and even (+) in Lisp. ;-)


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Pascal Costanza

2007-05-07, 8:03 am

Jon Harrop wrote:
> Pascal Costanza wrote:
>
> Except Lisp always passed by list so both cases are (foo 1 2 3) in Lisp (but
> not in other languages).


That's incorrect.

If the function definition looks like this:

(defun foo (x y z) ...)

....it's completely impossible to detect how the arguments are passed
internally. All decent Common Lisp and Scheme implementations will use
whatever is the most efficient way to do so.

>
> I'd rather abstractions that are forced upon me didn't make reading
> the "n"th function argument O(n).


You should better stop talking about things that you don't understand.


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Jon Harrop

2007-05-07, 8:03 am

Pascal Costanza wrote:
> If the function definition looks like this:
>
> (defun foo (x y z) ...)
>
> ...it's completely impossible to detect how the arguments are passed
> internally. All decent Common Lisp and Scheme implementations will use
> whatever is the most efficient way to do so.


Yes. That is not what I was referring to.

My point is that you can choose static (pass optional arguments or "option"
values) or dynamic (pass a list) checking of arity in OCaml/F# but not in
Lisp.

So there is no point in trying to start with a definition written in Lisp
because the language cannot express precisely what I'm talking about. That
is why I'm likening Lisp's approach to passing a list in OCaml, because it
will be a source of run-time errors (when the list has the wrong number of
elements).

If you write this Lisp:

(defun f (x y) (...))

then an F# translation is likely to express the fact that this function
takes two arguments:

let f (x, y) = ...

An F# translation is not likely to pass the arguments as a list:

let f [x; y] = ...

because it is an unnecessary source of run-time errors, but that does allow
an arbitrary number of arguments to follow:

let f (x::y::rest) = ...

The translation is still imperfect because the arguments passed in a list
must be of the same type, which might then be the dynamically typed "obj"
type in F# for example.

All of this is in relation to Lisp definitions that are called as (foo x y)
and not (foo (list x y)).

The translation is still imperfect, of course. In reality, I find OCaml's
optional arguments indispensable and I am currently eagerly waiting
optional arguments in F#.

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Frederic Beal

2007-05-07, 8:03 am

On 2007-05-07, Jon Harrop <jon@ffconsultancy.com> wrote:
> Yes. That is not what I was referring to.
>
> My point is that you can choose static (pass optional arguments or "option"
> values) or dynamic (pass a list) checking of arity in OCaml/F# but not in
> Lisp.


What you are saying makes no sense at all. The way arguments are passed
is a property of the *implementation*. Your discussion about option
values and arity is a property of the *specification*. There is no
link between them.

> So there is no point in trying to start with a definition written in Lisp
> because the language cannot express precisely what I'm talking about. That
> is why I'm likening Lisp's approach to passing a list in OCaml, because it
> will be a source of run-time errors (when the list has the wrong number of
> elements).
>
> If you write this Lisp:
>
> (defun f (x y) (...))
>
> then an F# translation is likely to express the fact that this function
> takes two arguments:
>
> let f (x, y) = ...
>
> An F# translation is not likely to pass the arguments as a list:
>
> let f [x; y] = ...


But why on earth do you seem to imply that Lisp would pass arguments like
thatĀ_? Two arguments your function have, two arguments will be passedĀ_-
it is done in C, in OCaml, in Fortran, in Lisp. You can pass more or
less arguments, yes, but that only implies the callee will eventually begin by
a check on the number of arguments.

> All of this is in relation to Lisp definitions that are called as (foo x y)
> and not (foo (list x y)).


Did you show any relationĀ_?

> The translation is still imperfect, of course.


On this point I agree.

--
Frederic
Pascal Costanza

2007-05-07, 8:03 am

Jon Harrop wrote:
> Pascal Costanza wrote:
>
> Yes. That is not what I was referring to.
>
> My point is that you can choose static (pass optional arguments or "option"
> values) or dynamic (pass a list) checking of arity in OCaml/F# but not in
> Lisp.


(defun foo (x y &optional z) ...)

This function accepts two or three arguments, not more, not less.

If you desire to do something against your apparent lack of knowledge
about Lisp in this particular area, you should check
http://www.lispworks.com/documentat...c/Body/03_e.htm

Specifically,
http://www.lispworks.com/documentat...ody/03_eaaa.htm even
allows an implementation to signal errors at compile time when there are
mismatches between the arguments passed to and the parameters accepted
by a function.

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Joachim Durchholz

2007-05-07, 7:04 pm

Pascal Costanza schrieb:
> [1] That's a trade off - I am not necessarily saying here that dynamic
> typing is always superior.


It also just occurred to me that in a language with static typing,
having a Rest list would mean that when accessing the parameters, you'd
need some form of run-time type check.
That would defeat most of the purposes of static typing, so I think
that's the reason why there is no mechanism in analogy to Lisp's Rest.

Regards,
Jo
Thant Tessman

2007-05-07, 7:04 pm

Pascal Costanza wrote:
> Markus E Leypold wrote:


[...]

>
> No, for example in the case of keyword arguments, such a list is
> definitely not homogeneous.


Keyword arguments are syntactic sugar for what really ought to be
structured data.

>
> Sure, those languages make completely different trade offs.
>
>
> Well, we have (+ 3 4) and (+ 1 2 3 4 1 2) and even (+) in Lisp. ;-)


And as he said, they are conceptually completely different things. If
you want fold a function taking two arguments across a list, go ahead
and do it. It's trivial. If you want to curry a single argument on to a
function taking two arguments, go ahead and do it. It's trivial. But
since it is trivial to build these things from more elemental concepts,
it has no business being built into the language.

-thant
Pascal Costanza

2007-05-07, 7:04 pm

Thant Tessman wrote:
> Pascal Costanza wrote:
>
> [...]
>
>
> Keyword arguments are syntactic sugar for what really ought to be
> structured data.


No, no, no, they are really more useful than that. ;)

>
> And as he said, they are conceptually completely different things. If
> you want fold a function taking two arguments across a list, go ahead
> and do it. It's trivial. If you want to curry a single argument on to a
> function taking two arguments, go ahead and do it. It's trivial. But
> since it is trivial to build these things from more elemental concepts,
> it has no business being built into the language.


So it seems that your position is that a language should be minimal /
simple / small. I don't agree with that, but we will probably not find
common ground here. But that's fine... :)

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Jon Harrop

2007-05-07, 7:04 pm

Pascal Costanza wrote:
> (defun foo (x y &optional z) ...)
>
> This function accepts two or three arguments, not more, not less.


This function is not statically checked, so it is equivalent to the
dynamically checked example I gave and not to the statically checked one.

You cannot translate the statically checked implementation into CL because
the language is not expressive enough.

> Specifically,
> http://www.lispworks.com/documentat...ody/03_eaaa.htm even
> allows an implementation to signal errors at compile time when there are
> mismatches between the arguments passed to and the parameters accepted
> by a function.


"it might be signaled at compile time"

So it is only guaranteed to be done by run time.

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Pascal Costanza

2007-05-07, 7:04 pm

Jon Harrop wrote:
> Pascal Costanza wrote:
>
> This function is not statically checked, so it is equivalent to the
> dynamically checked example I gave and not to the statically checked one.
>
> You cannot translate the statically checked implementation into CL because
> the language is not expressive enough.


None of this has anything to do with whether arguments are passed as
lists or not.


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Jon Harrop

2007-05-07, 7:04 pm

Frederic Beal wrote:
> On 2007-05-07, Jon Harrop <jon@ffconsultancy.com> wrote:
>
> What you are saying makes no sense at all. The way arguments are passed
> is a property of the *implementation*.


There are several different ways to pass optional arguments in Standard ML,
OCaml and F#. They all have different implications.

I was not referring to how these are implemented. I was referring to the
different characteristics of these approaches.

>
> But why on earth do you seem to imply that Lisp would pass arguments like
> thatĀ_? Two arguments your function have, two arguments will be passedĀ_-
> it is done in C, in OCaml, in Fortran, in Lisp.


No. C, OCaml and Fortran all check arity at compile time (static type
checking). Lisp makes no guarantees about this except that it will be
checked. So they are not equivalent.

As a "list processing language", Lisp passes a list of arguments and checks
at run-time that the number of arguments is correct. Lisp compilers try to
do static checks but, as a dynamic language, this cannot be guaranteed.

How then do you translate a simple Lisp function call into OCaml such that
it can cause run-time type errors when you provide the wrong number of
arguments to a function? I'm saying "you pass a list" because it gives you
the source of run-time errors that Lisp gives.

> You can pass more or
> less arguments, yes, but that only implies the callee will eventually
> begin by a check on the number of arguments.


Only in Lisp. These run-time checks are completely removed in the static
languages.

Take the following example. Define a closure "f" and a function "h" that
applies "f" to two arguments:

* (defvar f (lambda (n) (+ n n)))

F
* (defun h () (apply f '(1 2)))
STYLE-WARNING: redefining H in DEFUN

H

Note that the body of "h" is clearly wrong but the compiler gave no warning
or error. Only when "h" is applied to we get a run-time error:

* (h)

debugger invoked on a SB-INT:SIMPLE-PROGRAM-ERROR in thread
#<THREAD "initial thread" {10025EF941}>:
invalid number of arguments: 2

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.

((LAMBDA (N)) 1)
0]

So this is not equivalent to the OCaml:

# let f n = n+n;;
val f : int -> int = <fun>
# let h() = f 1 2;;
This function is applied to too many arguments, maybe you forgot a `;'

because OCaml statically checked the program and found that it was wrong.

So maybe it a better translation is:

# let f [n] = n+n;;
Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
val f : int list -> int = <fun>
# let h() = f [1; 2];;
val h : unit -> int = <fun>

The function now compiles (and the compiler warns that it had to add a
run-time check) and will give a run-time error if it is invoked with the
wrong number of arguments:

# h();;
Exception: Match_failure ("", 1, 6).

In this sense, Lisp's &rest arguments are nothing more than the tail of the
argument list:

# let f (n::rest) = n, rest;;
Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
val f : 'a list -> 'a * 'a list = <fun>

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Rainer Joswig

2007-05-07, 7:04 pm

In article <463fa73b$0$8746$ed2619ec@ptn-nntp-reader02.plus.net>,
Jon Harrop <jon@ffconsultancy.com> wrote:

....

> Take the following example. Define a closure "f" and a function "h" that
> applies "f" to two arguments:


f is not a closure. It is just an anonymous function.

>
> * (defvar f (lambda (n) (+ n n)))
>
> F
> * (defun h () (apply f '(1 2)))
> STYLE-WARNING: redefining H in DEFUN
>
> H
>
> Note that the body of "h" is clearly wrong but the compiler gave no warning
> or error. Only when "h" is applied to we get a run-time error:



The body of h is not wrong.

Since you lookup the function from a special variable (dynamic
binding), f can mean at runtime something else:

(let ((f (lambda (a b) (cons a b))))
(h))

Works just fine and perfectly legal.
David Hopwood

2007-05-07, 10:03 pm

Joachim Durchholz wrote:
> Pascal Costanza schrieb:
>
>
> It also just occurred to me that in a language with static typing,
> having a Rest list would mean that when accessing the parameters, you'd
> need some form of run-time type check.


Not necessarily. In Haskell's printf, the arguments are required to
implement a typeclass. In an object-oriented language, the arguments could
be required to implement a common supertype. Neither of these need a
run-time check that can fail.

--
David Hopwood <david.hopwood@industrial-designers.co.uk>
Joachim Durchholz

2007-05-08, 4:09 am

David Hopwood schrieb:
> Joachim Durchholz wrote:
>
> Not necessarily. In Haskell's printf, the arguments are required to
> implement a typeclass. In an object-oriented language, the arguments could
> be required to implement a common supertype. Neither of these need a
> run-time check that can fail.


Sorry, there was a tacit assumption in what I wrote: that Rest is
equivalent to a normal, arbitrarily-typed parameter list.

If you can restrict all remaining parameters to a common supertype, then
Rest lists do make sense.
Most examples given here (including the Lisp ones) indeed make use of this.

However, I think there are also uses that are similar (or even
equivalent) to currying, where a dynamically typed language can use a
Rest list and a statically typed one would be forced to use currying
(plus parametric polymorphism).

Regards,
Jo
Markus E Leypold

2007-05-08, 8:04 am


Pascal Costanza <pc@p-cos.net> writes:

> Markus E Leypold wrote:
>
> No, for example in the case of keyword arguments, such a list is
> definitely not homogeneous.


Exactly -- and it also is not arbitrarily long, but a fix number of
keywords is associated to a argument, perhaps optionally. That is why
e.g. Ocaml doesn't model that as a list.

>
> Sure, those languages make completely different trade offs.


They are trading variadic functions against ... what?

>
> Well, we have (+ 3 4) and (+ 1 2 3 4 1 2) and even (+) in Lisp. ;-)


I see, you missed my point: In my view of things (sum ...) is defined
in terms of the more primitive binary operator '+' as a list fold. It
pains me, to have the fold as only and primitive operation.

Note that originally I said:

|| BTW: I have never understood why a language which has lists and a
|| notation to construct list values should ever need variadic
|| arguments.

Note also that I said "need" (as opposed to "shall not have").

To which you answered:

| Because the way how arguments lists are represented is an
| implementation detail, and I don't want to deal with implementation
| details in client code.

Exactly: Semantically a variable number of arguments is just a
list. It's an implementation detail how it is passed as an argument. A
language that has proper lists, doesn't need variadic functions
(esp. if it also has named arguments).

I'm not really interested in continuing that topic, BTW. It looks a
bit cranky.

Regards -- Markus

Markus E Leypold

2007-05-08, 8:04 am


Jon Harrop <jon@ffconsultancy.com> writes:

> Markus E Leypold wrote:
>
> Static typing: variadic arguments are statically checked in OCaml and
> different arguments may have different types (lists are homogeneous).
>
> Currying: you can partially apply optional arguments in OCaml.


So does a "language which has lists and a notation to construct list
values" now "need variadic arguments"? Or what is the meaning of your
delphic reply? You confuse me.

Optional arguments (IMHO) are not variadic, just arguments that have
defaults (like 'None' in OCaml) and can be left out at
invocation. Perhaps this is a source of confusion in our attempts to
communicate here?

Regards -- Markus
Thant Tessman

2007-05-09, 4:11 am

Pascal Costanza wrote:
> Thant Tessman wrote:
>
> No, no, no, they are really more useful than that. ;)


(I know I'll regret this, but...) How?


[...]

>
> So it seems that your position is that a language should be minimal /
> simple / small. I don't agree with that, but we will probably not find
> common ground here. But that's fine... :)


For what it's worth, Scheme has variadic plus function too. And I'm sure
it was designed that way exactly because it was considered minimal /
simple / small from a lispy point of view. But I still consider it a
design mistake.

The real reason variadic plus function is inferior to the two-argument
plus function with fold or curry when you want it is that you don't want
to have to implement new fundamentally binary functions (like matrix
multiplication) as variadic functions just to match the interface. This
becomes important when you manipulate these things at a higher order.

Note that two-argument plus function with fold is semantically
indistinguishable from the variadic version, and the former arrangement
likely provides more opportunities for performance optimization in the
common case since it probably allows for more robust type inference.

-thant
Pascal Costanza

2007-05-09, 4:11 am

Markus E Leypold wrote:
> Pascal Costanza <pc@p-cos.net> writes:
>
>
> Exactly -- and it also is not arbitrarily long, but a fix number of
> keywords is associated to a argument, perhaps optionally. That is why
> e.g. Ocaml doesn't model that as a list.


In Common Lisp, a list of keywords can be arbitrarily long. For each
keyword, the leftmost keyword-value pair is used. This enables the idiom
for overriding keyword arguments, which I have described elsewhere in
this thread. (And that's a relatively common idiom in Common Lisp.)

>
> They are trading variadic functions against ... what?


As soon as you have static type checking, you know for each function /
procedure call what the involved types are. This also means that you
have to implement similar wrapper functions repeatedly for each kind of
function. In a dynamically typed language, you can implement wrapper
functions which are mostly, or sometimes even completely, oblivious to
the kind of function they wrap. This means that you can reduce the
number of lines that you have to implement.

(That's a variation of the idea that it is better to write 100 functions
for one data structure than 10 functions for 10 data structures.)

>
> I see, you missed my point: In my view of things (sum ...) is defined
> in terms of the more primitive binary operator '+' as a list fold. It
> pains me, to have the fold as only and primitive operation.


No, I didn't miss your point. We simply disagree.

> Note that originally I said:
>
> || BTW: I have never understood why a language which has lists and a
> || notation to construct list values should ever need variadic
> || arguments.
>
> Note also that I said "need" (as opposed to "shall not have").


Language design is not about necessity. It is about convenience. If it
were about necessity, any Turing-complete language would do, including
assembly language or, if you want to stay really minimal, Unlambda.

Convenience depends on a multitude of factors and is, ultimately,
subjective. (There are, of course, technical and semantic issues
involved which stay more or less the same everywhere, but these are not
the only issues.)

I don't want to say (fold + 0 '(1 2 3)) because (+ 1 2 3) is much
clearer to me.


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Markus E Leypold

2007-05-09, 4:11 am


Pascal Costanza <pc@p-cos.net> writes:
>
>
> Language design is not about necessity. It is about convenience. If it


So OCaml or Ada or whatever are inconvenient? I'm surprissed. But
never mind: I'm not really interested in fighting about this kind of
philosophy: There is no way to account for taste, and that is what it
boilds down to.

To motivate my original remark: C has no way to write a list in the
program. So variadic functions are used to pass a variable number of
thingies to some computation process. In Ada one can write down array,
like this:

A := (1,2,3);

and the need for variadic functions (and all their unsafety) goes
away: Just pass an Array if you need to pass a variable number of
things.

Every functional language I remember has lists as matter of course, so
there's also no necessity for variadic function.

With regards to what Lisp does: It uses variadic functions to emulate
(dynamically) a number of things that strongly typed languages have
built into the type system:

- optional arguments (with and without defaults) keyword
- arguments

It's probably a matter of taste, but I think it convenient to have the
keywords and their arguments checked statically and be part of the
type signature.

OCaml can do almost the same thing as Lisp does with &rest and that is
invocing the function as

f arg1 arg2 [ String "adas"; Integer 12; String "text" ]

or

f arg1 arg2 [ Outfile "adas"; Count 12; Infile "text" ]

With the exception of the fact that you have to define the tags as
varinat type first (thus providing a static type for the variable
list) and 2 additional parenthesis, that strikes me as very similar to
what Lisp does.

Which leads me to stipulate that Ocaml has "more" than Lisp here (WTR
to keyword args) since the Lisp way to pass argument lists can be
employed in Ocaml too, but hardly ever is, since it has been
superseded by statically typed language constructs with special
syntax.

You might disagree and you're welcome to do so. I find the (excuse me)
fanatism with which you defend what you (and you alone) perceive as a
slight on Lisp somewhat suspicious. I, personally, am more interested
in an overview, transfer between verious languages and insight into
various trade offs that come with certain language restrictions or
options. I've to thank you for drawing my attention to that peculiar
way, Lisp handels arguments. But I found one of Jon's contributions
much more interesting where he hinted on dependencies between variadic
functions and static typing. (And as far as I see it, to discuss that
exhaustively we'd have to write down all relevant definitions very
carefully, since there seems to be a lot of confusion, e.g. wether an
optional argument makes a function variadic: In my book not, but other
peoples MMV :-).

Back again to my original point:

- C needed variadic functions because it has no list data type and
specifically no notation for lists (best example is printf).

- When you leave the C sector and have a language with lists, the
need for variadic functions to pass a variable number of things is
greatly diminished.

- Lisp still supports variadic functions. This is interesting, also
from a historical point of view. my suspicion is, that it needs
variadic functions to emulate named arguments.

That it provides also a possibility to pass a variable number of
things without having to add this

(f 1 2 (a b c))

extra parenthesis seems to me to be a side benefit: Lisp never had
problems with too much parenthesis, didn't it :-).

- In languages which have named arguments and lists there is
absolutely no need for variadic functions.

They are not missed in Ada and OCaml and I don't have the
impression that these languages are "inconvenient".

> were about necessity, any Turing-complete language would do, including
> assembly language or, if you want to stay really minimal, Unlambda.
>
> Convenience depends on a multitude of factors and is, ultimately,
> subjective. (There are, of course, technical and semantic issues
> involved which stay more or less the same everywhere, but these are
> not the only issues.)
>
> I don't want to say (fold + 0 '(1 2 3)) because (+ 1 2 3) is much
> clearer to me.



Right. Still I'd like to separate '+' and 'sum'. One is the primitive,
the other a list function based on that arithmetic primitive.

Regards -- Markus

Rainer Joswig

2007-05-09, 4:11 am

In article <gt7irjz6gl.fsf@hod.lan.m-e-leypold.de>,
Markus E Leypold
<development-2006-8ecbb5cc8aREMOVETHIS@ANDTHATm-e-leypold.de> wrote:

> Pascal Costanza <pc@p-cos.net> writes:
>
> So OCaml or Ada or whatever are inconvenient? I'm surprissed. But
> never mind: I'm not really interested in fighting about this kind of
> philosophy: There is no way to account for taste, and that is what it
> boilds down to.
>
> To motivate my original remark: C has no way to write a list in the
> program. So variadic functions are used to pass a variable number of
> thingies to some computation process. In Ada one can write down array,
> like this:
>
> A := (1,2,3);
>
> and the need for variadic functions (and all their unsafety) goes
> away: Just pass an Array if you need to pass a variable number of
> things.
>
> Every functional language I remember has lists as matter of course, so
> there's also no necessity for variadic function.
>
> With regards to what Lisp does: It uses variadic functions to emulate
> (dynamically) a number of things that strongly typed


strongly typed?

> languages have
> built into the type system:
>
> - optional arguments (with and without defaults) keyword
> - arguments


I'm not sure what you mean. But let me clarify:

* Common Lisp has optional and keyword arguments.
Keywords arguments are also optional.

* Common Lisp has a &rest argument

Both are separate. Keyword and optional arguments are unrelated to
&rest arguments. You can specify optional arguments
or keyword arguments with using &rest.

You can also imagine that implementors will avoid
to let the compiler use lists in these cases. Lists
come explicitly into play when using APPLY or
when using &REST arguments. But that's what they
are for.


Say:

(defun foo (bar &optional baz)
... )

A compiler can check that the function is called with
either one or two arguments. No lists are involved.


(defun foo (bar &optional baz)
(or bar baz))

(defun test ()
(foo t nil t))

*** Warning in TEST: FOO is called with the wrong number of arguments: Got 3 wanted at least 1 at most 2

So, above can be statically checked by a compiler.


(defun foo (bar &key baz)
... )

A compiler can check that the function is called
is one argument and optionally with an additional
keyword argument. It can also check the the keyword
really is BAZ. There are also no lists involved.

(defun foo (bar &key baz)
(or bar baz))

(defun test ()
(foo t :bar nil))

;Compiler warnings :
; Function call arguments don't match current definition of FOO, in TEST.

Again, a compiler can check that.

One can create a list of args passed, for example
the list of passed keyword args:

(defun foo (bar &rest args &key baz)
... )

In this example ARGS will contain a list of
passed keyword args. This gets interesting when
you allow other keyword arguments to be passed
that are not in the argument list:

(defun foo (bar &rest args &key baz &allow-other-keys)
... )

In above case the keyword arguments will no longer
be checked by the compiler, because you can pass
any keyword argument.

--
http://lispm.dyndns.org
Markus E Leypold

2007-05-09, 4:11 am


Rainer Joswig <joswig@lisp.de> writes:

> In article <gt7irjz6gl.fsf@hod.lan.m-e-leypold.de>,
> Markus E Leypold
> <development-2006-8ecbb5cc8aREMOVETHIS@ANDTHATm-e-leypold.de> wrote:
>

---
[color=darkred]
>
> strongly typed?


Scusami -- I intended to say statically here.

>
>
> I'm not sure what you mean. But let me clarify:
>
> * Common Lisp has optional and keyword arguments.
> Keywords arguments are also optional.
>
> * Common Lisp has a &rest argument
>
> Both are separate. Keyword and optional arguments are unrelated to
> &rest arguments. You can specify optional arguments
> or keyword arguments with using &rest.
>
> You can also imagine that implementors will avoid
> to let the compiler use lists in these cases. Lists
> come explicitly into play when using APPLY or
> when using &REST arguments. But that's what they
> are for.


OK. Actually I understood Pascal's argument differently. I've to admit
that most of my actual Lisp experience is not Lisp but Scheme (and
Emacs Lisp) and the Lisps I dabbled with before where strange PC Lisps
that weren't Common (Lisp :-).


>
> Say:
>
> (defun foo (bar &optional baz)
> ... )
>
> A compiler can check that the function is called with
> either one or two arguments. No lists are involved.
>
>
> (defun foo (bar &optional baz)
> (or bar baz))
>
> (defun test ()
> (foo t nil t))
>
> *** Warning in TEST: FOO is called with the wrong number of arguments: Got 3 wanted at least 1 at most 2
>
> So, above can be statically checked by a compiler.


Only the structure of the things in the list is not checked, but that
is how Lisp works anyway :-).


> (defun foo (bar &key baz)
> ... )
>
> A compiler can check that the function is called
> is one argument and optionally with an additional
> keyword argument. It can also check the the keyword
> really is BAZ. There are also no lists involved.
>
> (defun foo (bar &key baz)
> (or bar baz))
>


(mark that as #1)

> (defun test ()
> (foo t :bar nil))


(mark that as #2)

>
> ;Compiler warnings :
> ; Function call arguments don't match current definition of FOO, in TEST.
>
> Again, a compiler can check that.
>
> One can create a list of args passed, for example
> the list of passed keyword args:
>
> (defun foo (bar &rest args &key baz)
> ... )
>
> In this example ARGS will contain a list of
> passed keyword args. This gets interesting when
> you allow other keyword arguments to be passed
> that are not in the argument list:
>
> (defun foo (bar &rest args &key baz &allow-other-keys)
> ... )
>
> In above case the keyword arguments will no longer
> be checked by the compiler, because you can pass
> any keyword argument.


It is good to know that keyword arguments are independent from &rest
in a proper Common Lisp. Perhaps I should have looked that up before.

Buuut: My original point centered around the necessity / usefulness of
&rest and now I'm more convinced than before that the ONLY useful
purpose is to avoid an extra pair of parentheses like in

(f something 1 2 3 4) vs. (f something '(1 2 3 4))

I'd like to rest my case here, since I did't want to participate in
another thread of hairsplitting :-). Thanks for your explanation,
though, that has been educational :-).

Regards -- Markus

Rainer Joswig

2007-05-09, 4:11 am

In article <5jvef3xio3.fsf@hod.lan.m-e-leypold.de>,
Markus E Leypold
<development-2006-8ecbb5cc8aREMOVETHIS@ANDTHATm-e-leypold.de> wrote:

>
> Only the structure of the things in the list is not checked, but that
> is how Lisp works anyway :-).


Depends. The standard says nothing about it, but it does not hinder any implementation.

(defun foo (a b)
(declare (integer a b))
(+ a b))

(defun bar (x)
(foo 3.2 x))

Often declarations like above will be used for optimization purposes,
but a compiler, in this case SBCL, can do type inference and
static type checking.

; compiling file "/tmp/test.lisp" (written 08 MAY 2007 08:46:53 PM):
; compiling (DEFUN FOO ...)
; compiling (DEFUN BAR ...)
; file: /tmp/test.lisp
; in: DEFUN BAR
; (FOO 3.2 X)
;
; note: deleting unreachable code
;
; caught WARNING:
; Asserted type INTEGER conflicts with derived type
; (VALUES (SINGLE-FLOAT 3.2 3.2) &OPTIONAL).
; See also:
; The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished
; caught 1 WARNING condition
; printed 1 note

....

> Buuut: My original point centered around the necessity / usefulness of
> &rest and now I'm more convinced than before that the ONLY useful
> purpose is to avoid an extra pair of parentheses like in
>
> (f something 1 2 3 4) vs. (f something '(1 2 3 4))
>
> I'd like to rest my case here, since I did't want to participate in
> another thread of hairsplitting :-). Thanks for your explanation,
> though, that has been educational :-).


Two last points for 'education':

First:

The function who uses &rest can be written like this:

(defun foo (&rest args)
(declare (dynamic-extent args))
(mapcar #'sqrt args))

This allows the compiler to allocate the list on the stack.
So if you write (foo 1 2 2 3 4), you don't have
to worry about consing a list. So you move the
burden from the caller of the function to
the function.

Second:

&rest arguments are sometimes handy if you deal with keyword arguments.

Imagine a drawing function that calls two other drawing functions.
The drawing function takes a lot of arguments
(line-thickness, dashed?, ...). It wants to pass a few
of these arguments to the other drawing arguments,
but maybe not all.

What you do then is not to write down all the parameters you
want to pass, but you take the complete arglist as a list,
filter out the arguments you don't want to pass
and then you apply the drawing functions with these
new filtered arglists.

So, there are also some other use cases where &REST is handy.
But usually it is good practice to avoid it.

--
http://lispm.dyndns.org
Pascal Costanza

2007-05-09, 4:11 am

Thant Tessman wrote:
> Pascal Costanza wrote:
>
> (I know I'll regret this, but...) How?


There is a "war story" about the usefulness of keyword parameters at
http://lib.store.yahoo.net/lib/paul...bbnexcerpts.txt (in the latter
third of the text - just search for "keyword parameters").


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Pascal Costanza

2007-05-09, 4:11 am

Markus E Leypold wrote:
> Pascal Costanza <pc@p-cos.net> writes:
>
> So OCaml or Ada or whatever are inconvenient? I'm surprised.


That's not what I said. What I said is: "Convenience depends on a
multitude of factors and is, ultimately, subjective."

Apparently, OCaml and Ada work better for you than Lisp. That's fine
with me. Really, it is! ;)


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Markus E Leypold

2007-05-09, 4:11 am


Pascal Costanza <pc@p-cos.net> writes:

> Markus E Leypold wrote:
>
> That's not what I said. What I said is: "Convenience depends on a
> multitude of factors and is, ultimately, subjective."
>
> Apparently, OCaml and Ada work better for you than Lisp. That's fine
> with me. Really, it is! ;)


Actually they both just work more often for me and I'm not missing
variadic functions there. That was all my point. BTW, SML and FORTRAN
do work as often for me as Lisp, but that has nothing to do with
language design :-).

Regards -- Markus

Jon Harrop

2007-05-09, 4:11 am

Markus E Leypold wrote:
> Jon Harrop <jon@ffconsultancy.com> writes:
>
> So does a "language which has lists and a notation to construct list
> values" now "need variadic arguments"? Or what is the meaning of your
> delphic reply? You confuse me.


Variadic arguments are not needed but they are beneficial because they offer
different trade-offs.

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/produc...journal/?usenet
Joachim Durchholz

2007-05-09, 7:06 pm

Jon Harrop schrieb:
> Variadic arguments are not needed but they are beneficial because they offer
> different trade-offs.


I've been doing far too much PHP lately, and been using variadic arguments.
Almost invariably, I have replaced them with list parameters. The
functions tended to acquire additional parameters, and if the end of the
parameter list is already saturated with a Rest construct, you can't do
that.
This may be because the software is currently fluctuating towards a
state of better abstraction, so functions tend to acquire and lose
parameters on a fairly regular basis. I think the only functions that
will retain a variable number of parameters in the long run are the
functions that implement currying (and there, it's indispensable, not
just a shorthand for a list parameter).

Regards,
Jo
Thant Tessman

2007-05-09, 7:06 pm

Pascal Costanza wrote:
> Thant Tessman wrote:
>
> There is a "war story" about the usefulness of keyword parameters at
> http://lib.store.yahoo.net/lib/paul...bbnexcerpts.txt (in the latter
> third of the text - just search for "keyword parameters").


Paul Graham says he found keyword parameters useful. But the only
explanation he gives is that it allowed him to "add another dimension to
the behavior of one of the operators" when he needed to. He doesn't
explain why keyword parameters allow him to accomplish this in a way
superior to simply passing in some other dynamically-generated data
structure.

-thant
Pascal Costanza

2007-05-09, 7:06 pm

Thant Tessman wrote:
> Pascal Costanza wrote:
>
> Paul Graham says he found keyword parameters useful. But the only
> explanation he gives is that it allowed him to "add another dimension to
> the behavior of one of the operators" when he needed to. He doesn't
> explain why keyword parameters allow him to accomplish this in a way
> superior to simply passing in some other dynamically-generated data
> structure.


OK, here is very rough illustration.

Assume you have the following function defined in your library:

(defun foo (a b c)
...)

There are several sites in your program which call foo, all passing
three parameters each.

Now you realize for nth+1 case that you actually need more influence on
what foo actually does, which is best achieved with another parameter.
You would have to change the parameter list and go to all the call sites
and pass one extra parameter.

With keyword parameters, this is simpler. You just add a keyword
parameter with a default value that is appropriate for all existing cases:

(defun foo (a b c &key (d -some-default-value-))
...)

Now the call sites can stay as they are. The one call site that needs
the extra influence on foo's behavior just passes the extra parameter:

(foo 1 2 3 :d 42)

There are other techniques to get the same effect, but this works pretty
well in Common Lisp (and the link to Paul Graham's story shows a case
where it has been used in an actual system).

What's really useful here is that you can change the function at runtime
without ever shutting down the running system. It won't affect the
already existing call sites (the currently executing functions will just
continue to execute with the old version, and the new calls will use the
new version - but since there is no difference in semantics for them,
that's all fine), and the new call site is new code anyway.

Another nicety is that, if you have implemented your wrapper functions
in a way that they just blindly pass along everything they receive, then
new keyword parameters will also be blindly passed along. Intermediate
code doesn't have to know about these parameters, only the sites that
define and use them.

I hope this makes it clearer...


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Thant Tessman

2007-05-09, 7:07 pm

Pascal Costanza wrote:

[...]

> Now the call sites can stay as they are. The one call site that needs
> the extra influence on foo's behavior just passes the extra parameter:
>
> (foo 1 2 3 :d 42)


In C++ you can define a new version of foo that takes four arguments
without the need to modify any calls to a pre-existing version of foo
that takes three.

In Scheme you can define a function (define (foo a b . rest) ...) that
won't disturb any pre-existing call sites to a two-argument version of foo.

This has nothing to do with keyword arguments as such.

And since you *have* to modify any code you want to call the new version
of the code with a new parameter, all you're really saving yourself is
the need to come up with a new function name. This new function is
certainly free to call the old function if it wants to. Is the ability
to avoid the need for a new function name for the new version (i.e.
overloading) really that big a deal?

A keyword argument is, in its most elemental essence, nothing more than
an optional argument that happens to be tagged with a symbol. My
experience is with Dylan (a *very* long time ago), but if Common Lisp
statically dispatches based on which keyword parameters are provided at
a call site, all it is really doing is sneaking a static type system of
sorts in through the back door. Why not keep the notion of function
dispatch on argument type (and/or number) separate from the notion of
optional or dynamically-generated data and just make sure you support
both of those concepts well from the beginning instead of conflating
them in the form of keyword arguments?

> What's really useful here is that you can change the function at runtime
> without ever shutting down the running system.


I know from first-hand experience how this is, but again, this has
nothing to do with keyword arguments as such.


[...]

> Another nicety is that, if you have implemented your wrapper functions
> in a way that they just blindly pass along everything they receive, then
> new keyword parameters will also be blindly passed along. Intermediate
> code doesn't have to know about these parameters, only the sites that
> define and use them.


Yet again, how does keyword arguments do this job better than, say,
passing along a list of tagged elements?

> I hope this makes it clearer...


Thanks for putting the effort into it, but if I may be so bold, it was
never not clear.

-thant


>
>
> Pascal
>

Pascal Costanza

2007-05-09, 7:07 pm

Thant Tessman wrote:
> Pascal Costanza wrote:
>
> [...]
>
>
> In C++ you can define a new version of foo that takes four arguments
> without the need to modify any calls to a pre-existing version of foo
> that takes three.
>
> In Scheme you can define a function (define (foo a b . rest) ...) that
> won't disturb any pre-existing call sites to a two-argument version of foo.
>
> This has nothing to do with keyword arguments as such.
>
> And since you *have* to modify any code you want to call the new version
> of the code with a new parameter, all you're really saving yourself is
> the need to come up with a new function name. This new function is
> certainly free to call the old function if it wants to. Is the ability
> to avoid the need for a new function name for the new version (i.e.
> overloading) really that big a deal?


Yes. The names are the primary hint for a programmer what's going on in
the code.

> A keyword argument is, in its most elemental essence, nothing more than
> an optional argument that happens to be tagged with a symbol. My
> experience is with Dylan (a *very* long time ago), but if Common Lisp
> statically dispatches based on which keyword parameters are provided at
> a call site, all it is really doing is sneaking a static type system of
> sorts in through the back door. Why not keep the notion of function
> dispatch on argument type (and/or number) separate from the notion of
> optional or dynamically-generated data and just make sure you support
> both of those concepts well from the beginning instead of conflating
> them in the form of keyword arguments?
>
>
> I know from first-hand experience how this is, but again, this has
> nothing to do with keyword arguments as such.


No, but it meshes very well.

>
> Yet again, how does keyword arguments do this job better than, say,
> passing along a list of tagged elements?


....because the keyword parameters are already processed for you when you
"pick them up" in the code, whereas you would have to process such
lists yourself.

Yes, this is all rather vague and more about warm and fuzzy feelings
than about crisp and clear necessities. I am just trying to explain what
in my experience works very well.

>
> Thanks for putting the effort into it, but if I may be so bold, it was
> never not clear.


That's good then. ;-)


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Alexander Schmolck

2007-05-09, 7:07 pm

Thant Tessman <adm@standarddeviance.com> writes:

> Pascal Costanza wrote:
>
> [...]
>
>
> In C++ you can define a new version of foo that takes four arguments without
> the need to modify any calls to a pre-existing version of foo that takes
> three.
>
> In Scheme you can define a function (define (foo a b . rest) ...) that won't
> disturb any pre-existing call sites to a two-argument version of foo.
>
> This has nothing to do with keyword arguments as such.


Sure it has. What happens if you want to add more than one additional optional
argument? Or, since this is c.l.functional, curry an argument other than the
first or the last?

> And since you *have* to modify any code you want to call the new version of
> the code with a new parameter, all you're really saving yourself is the need
> to come up with a new function name.


The same argument is applicable to arity > 1.

> This new function is certainly free to call the old function if it wants to.
> Is the ability to avoid the need for a new function name for the new version
> (i.e. overloading) really that big a deal?


Yes. Look e.g. at how various functions/methods in the python standard library
evolved over time -- introducing a new function/method each time would have
resulted in very unwieldy APIs.

> A keyword argument is, in its most elemental essence, nothing more than an
> optional argument that happens to be tagged with a symbol.


Yes. This happens to be very useful though, inter alia because humans have
evidently not evolved particularly effective cognitive machinery to associate
ordinals with things, whereas associating words with things comes natural.
Even from a purely visual perspecitve, it's IMO much easier to read a function
call with n > 3 keyword arguments than with the same number of positional
arguments.

> My experience is with Dylan (a *very* long time ago), but if Common Lisp
> statically dispatches based on which keyword parameters are provided at a
> call site, all it is really doing is sneaking a static type system of sorts
> in through the back door.


I don't thikn common lisp doesn't "dispatches" on keyword arguments. Keyword
arguments are just a *standard* way of passing a list of symbol/value tuples.

> Why not keep the notion of function dispatch on argument type (and/or
> number) separate from the notion of optional or dynamically-generated data
> and just make sure you support both of those concepts well from the
> beginning instead of conflating them in the form of keyword arguments?
>
>
> I know from first-hand experience how this is, but again, this has
> nothing to do with keyword arguments as such.
>
>
> [...]
>
>
> Yet again, how does keyword arguments do this job better than, say, passing
> along a list of tagged elements?


Because it is a standardized way of doing and thus has language support (which
means it can optimized etc.), is familiar to everyone and done in an uniform
way?

>
> Thanks for putting the effort into it, but if I may be so bold, it was never
> not clear.


How much code in a language with keyword arguments (from day one) have you
written? I'm asking because I'm puzzled by the resistance keyword arguments
seem to engender in some quarters -- not having them (or some other
light-weight mechanism of encoding parameters non-positionally) seems really
braindamaged to me -- sort of like creating a lisp without docstrings, but
worse. So I'm sort of wondering whether people knew what there were doing when
they decided to drop keyword arguments in F# or designed R^*RS scheme and I
need to develop a less myopic point of view.

'as

Markus E Leypold

2007-05-10, 4:17 am


Jon Harrop <jon@ffconsultancy.com> writes:

> Markus E Leypold wrote:
>
> Variadic arguments are not needed but they are beneficial because they offer
> different trade-offs.


I'm still not among the believers as you might have gathered from my
other replies. :-).


Regards .. Markus

Thant Tessman

2007-05-10, 10:04 pm

Alexander Schmolck wrote:
> Thant Tessman <adm@standarddeviance.com> writes:
>
>
> Sure it has. What happens if you want to add more than one additional optional
> argument? Or, since this is c.l.functional, curry an argument other than the
> first or the last?


I don't understand the point you're trying to make. In C++ you can
overload function names on any number of arguments. In the Scheme
example, the "rest" function argument takes a list of any number of
extra arguments passed to the function. And lambda allows you to "curry"
any function argument you want.

[...]

>
> Yes. Look e.g. at how various functions/methods in the python standard library
> evolved over time -- introducing a new function/method each time would have
> resulted in very unwieldy APIs.


Are you really suggesting that the evolution of APIs is mainly about
adding new function arguments?


>
> Yes. This happens to be very useful though, inter alia because humans have
> evidently not evolved particularly effective cognitive machinery to associate
> ordinals with things, whereas associating words with things comes natural.


Which is why many programming languages support a data structure known
as a "record" or "structure" or even "class". Again, this has nothing to
do with keyword parameters (except to the degree that keyword parameters
are used to construct records).

[...]


>
> Because it is a standardized way of doing and thus has language support (which
> means it can optimized etc.), is familiar to everyone and done in an uniform
> way?


Exactly what is being optimized? Dylan statically dispatches methods
based on keyword arguments. It stole its object system from CLOS, so I
assume it does exactly the same thing. I think Smalltalk does something
similar.


> How much code in a language with keyword arguments (from day one) have you
> written? I'm asking because I'm puzzled by the resistance keyword arguments
> seem to engender in some quarters [...]


Back when I bought into this whole object-oriented thing, I spent a lot
of time building object systems. I stole a lot of ideas from a lot of
places. Keyword parameters is one of the things I considered stealing,
but eventually decided the thinking behind them was a little muddy. It
took a while, but I eventually decided that the thinking behind OO
itself is a little muddy.


> [...] So I'm sort of wondering whether people knew what there were doing when
> they decided to drop keyword arguments in F# or designed R^*RS scheme and I
> need to develop a less myopic point of view.


It would not be difficult to add keyword arguments to an object system
implemented in Scheme via macros. In fact, the Dylan programming
language started out as just such an object system. I bet there are
others. Yet strangely, most Scheme programmers don't miss them.

-thant

Edence4

2007-05-12, 4:41 pm

Katie Holmes touching her boobs!
http://Katie-Holmes-touching-her-bo...hp?movie=726648
Alexander Schmolck

2007-05-13, 7:14 pm

Thant Tessman <adm@standarddeviance.com> writes:

> Alexander Schmolck wrote:
>
> I don't understand the point you're trying to make.


list.sort(cmp=whatever)
list.sort(reverse=True) # new
list.sort(key=abs) # new

list.sort(reverse=True,key=abs)

The arguments don't interfere with each other, how someone who wants to just
use reverse doesn't have to specify dummies for for key and cmp, remeber their
positions or even be aware of their existence and further how the code is
self-documenting. I'm also sure it's easier to remeber the names 'reverse' and
'key' than some arbitrary positions.

> In C++ you can overload function names on any number of arguments.


Well, yeah but not all languages are statically typed and don't you think even
those that are would be better off with keyword arguments than with the
monstrosity that is C++-style type-based overloading?

> In the Scheme example, the "rest" function argument takes a list of any
> number of extra arguments passed to the function.


Why should I need to specify 4 arguments if I just want to specify the 5th.

> And lambda allows you to "curry" any function argument you want.


Can you write

curry(f,x=3)

using lambda?

> [...]
>
>
> Are you really suggesting that the evolution of APIs is mainly about adding
> new function arguments?


Of course not. What I'm saying is that being able to add function arguments
without problems is beneficials for the evolution of APIs, because you can
introduce little tweaks without bloating the namespace or burdening the users.

For example python's max function now also grew a 'key' keyword parameter;
max(-2,1,key=abs) == -2

I happen to find this handy, but its unlikely anyone would have introduced it
if it would have required a new max_with_key function or somesuch.

>
> Which is why many programming languages support a data structure known as a
> "record" or "structure" or even "class".


And how many of these languages have some light-weight syntax for that? Or are
you really suggesting that in Java one doesn't need keyword arguments because
one can always create a new class-file for that purpose?

> Again, this has nothing to do with keyword parameters (except to the degree
> that keyword parameters are used to construct records).


If the language provides records that are as convenient and as widely used as
keyword parameters in python or CL, then that would be fine with me too.

>
> [...]
>
>
>
> Exactly what is being optimized?


1. Syntax
2. Performance

I bet when I write f(x,bar=3) in python and (f x :bar 3) in CL I incur less of
a performance overhead than f(x, struct('bar', 3')) in matlab (although I
haven't timed it).

Guess which one I find least readable? Guess which one is one possibility
amongst many? Guess which language has really horrible function signatures
where quite different things happen to parameters at fixed positions depending
on the total number of parameters?