Yet another?

Don Groves — 2007-08-16 03:45:35

Hello all,

The trouble with languages such as Joy, which I fell in love with a
couple of years ago, is that, when I hang around them long enough,
they get under my skin. They get into my mind and after a while, take
on a life of their own. My only relief from this situation is to write
one myself, so I am hereby announcing the birth of Catenate, yet
another concatenative language.

My selection of the name Catenate is two-fold. First is the obvious
one that catenate (cat) is the UNIX command for concatenating strings.
The second is more complex. In Catenate, memory is not allocated in
the usual way, instead it is pre-allocated as an array of fixed-size
"cells" and data and programs are represented as groups of cells. In
Botany, the term "catenate" refers to plant structures that are a
series or group of biological cells -- hence the similarity and the name.

Catenate is based on (1) Joy in particular and concatenative languages
in general; (2) my 35+ years of professional programing during which I
kept notes on things I would like to try after retirement; and (3) my
Forth experience during that time.

Catenate is written in C and compiles under the GCC environment. It is
not intended for speed, rather for completeness, safety, and for my
own research in mathematical topics (my university degree is in math).
If Catenate becomes useful for anyone else, that will be icing on the
cake.

Although it is coming along nicely, I haven't anything yet to publish
for others to try but I wanted to introduce myself to the group and
start joining in on the discussions of this highly interesting topic.

Sincerely,

Don Groves
Portland, Oregon, US.

pml060912 — 2007-08-16 13:06:13

--- In concatenative@yahoogroups.com, "Don Groves" <dgroves@...> wrote:
>
> Hello all,
>
> The trouble with languages such as Joy, which I fell in love with a
> couple of years ago, is that, when I hang around them long enough,
> they get under my skin. They get into my mind and after a while, take
> on a life of their own. My only relief from this situation is to write
> one myself, so I am hereby announcing the birth of Catenate, yet
> another concatenative language.

.
.
.

> Although it is coming along nicely, I haven't anything yet to publish
> for others to try but I wanted to introduce myself to the group and
> start joining in on the discussions of this highly interesting topic.
>
> Sincerely,
>
> Don Groves

That's roughly how I got here. My occasional work on my own
concatenative language, Furphy, is on the page
http://member.netlink.com.au/~peterl/furphy.html (though that might
not be true after next week, when I may have to change ISPs - buy now
while stocks last). So far I don't have a whole language, just paper
models and constructs I have tested out on top of Forth. The next
stage, if I get around to it, is to implement that in an Eforth like
way on top of Perl or Euphoria to snarf (piggyback) their garbage
collection while I prototype a bit more, and maybe their associative
indexing too. It doesn't seem worth committing to a more efficient
machine code implementation including the work of implementing garbage
collection etc. until I'm sure I know what I've got. I've got some of
Henry Baker's ideas in mind about that, as food for thought (e.g. an
instruction stack instead of a return stack, and making a whole new
copy on any DUP to simplify freeing up).

There is one construct I found that I really like, so far. That is to
compile new words anonymously, using unrecognised words to flag both
termination and naming, with end of source terminating the final
anonymous word which is then executed/evaluated. That allows a simple
compile and go behaviour without modes or immediate words, although I
shall probably provide some for implementing syntactic sugar more
conveniently (a preprocessor could provide it, so this doesn't break
the fundamental model).

Another construct is more fundamental but not as aesthetically
pleasing to my eyes, the pair FREEZE and THAW to cause and complete
partial evaluation so that IF - which has to be eager - can have a
workaround for lazy evaluation that works a bit like the referencing
workaround in plain vanilla C to make argument passing by value
achieve the effect of passing by name. And I am considering making a
user interface that is somewhat spreadsheet oriented, drawing on work
done for the Z88 and the Canon Cat, though that is further down the
track and I may never get there. P.M.Lawrence.

Christopher Diggins — 2007-08-16 16:17:39

These are very interesting posts, I love hearing about new languages, thanks
for sharing them Peter and Don. I would be interested in seeing this thread
grow into a summary of the concatenative community and who's doing what, so
I want to encourage you to introduce (or reintroduce) yourself and your
language research on this thread. In that vein, here is a brief introduction
of myself and my language:

=

My name is Christopher Diggins and I am currently developing a language
derived from Joy called Cat (http://www.cat-language.com). Cat's notable
features are static typing, type-inference, macro term-rewriting system, and
lambda expressions with named parameters. Cat is being developed for two
purposes: 1) a language for teaching, 2) an intermediate language for
translation, compilation, optimization, and analysis of other languages. I
have been working on Cat for over a year and a half.

All code for Cat is public domain, and the main project is in C# (Mono
compatible, so it *is* multi-platform), and a translator for a subset of the
language is also available in C++.

- Christopher


[Non-text portions of this message have been removed]

Don Groves — 2007-08-16 19:42:31

Thanks, Christopher and Peter for your kind replies.

I've looked at Cat before, Christopher, and found it very impressive!
I'll be
most pleased if Catenate ever approaches Cat's level of sophistication.

I just tried to read up on Furphy but the url wasn't found, so Peter
may be
changing his ISP even sooner that he thought.

Peter mentioned Euphoria which is another impressive language but its
Pascal-like syntax seems overly verbose after becoming used to Joy-type
syntax. However, Euphoria is the fastest (blazing fast!) full-featured
interpreter I've ever seen. I've corresponded with the author and he
said
the internals are Forth-inspired.

Happy to be aboard,
Don



On Aug 16, 2007, at 19:17 , Christopher Diggins wrote:

> These are very interesting posts, I love hearing about new
> languages, thanks
> for sharing them Peter and Don. I would be interested in seeing
> this thread
> grow into a summary of the concatenative community and who's doing
> what, so
> I want to encourage you to introduce (or reintroduce) yourself and
> your
> language research on this thread. In that vein, here is a brief
> introduction
> of myself and my language:
>
> =
>
> My name is Christopher Diggins and I am currently developing a
> language
> derived from Joy called Cat (http://www.cat-language.com). Cat's
> notable
> features are static typing, type-inference, macro term-rewriting
> system, and
> lambda expressions with named parameters. Cat is being developed
> for two
> purposes: 1) a language for teaching, 2) an intermediate language for
> translation, compilation, optimization, and analysis of other
> languages. I
> have been working on Cat for over a year and a half.
>
> All code for Cat is public domain, and the main project is in C# (Mono
> compatible, so it *is* multi-platform), and a translator for a
> subset of the
> language is also available in C++.
>
> - Christopher
>
>
> [Non-text portions of this message have been removed]
>
>
>
>
> Yahoo! Groups Links
>
>
>
>

Don Groves — 2007-08-16 20:14:50

A few more Catenate features:

Dynamic typing:
Data is typed; names are not. A name can hold any type of data.

Linearity:
Every memory cell has exactly one reference. When de-referenced, a
cell is cleared and is immediately available for re-allocation. Thus,
garbage collection is trivial (marking without sweeping ;-)

Unicode:
UTF8 encoding is used throughout.

Syntax:
Probably best described as a math-inspired, Lispish Joy. For example,
here is a sample terminal session:

stack: { }
=> 3

stack: {3}
=> 4 *

stack: {12}
=> _

And a more complex one calculating the area of a circle of radius 3:

stack: { }
=> 3.1459 defcon π

π defined

stack: { }
=> [dup *] defun ^2

^2 defined

stack: { }
=> [^2 π *] defun πr^2

πr^2 defined

stack: { }
=> 3 πr^2

stack: {28.3131 }
=> _

Questions, comments, and suggestions will be gladly received,
Don Groves

Daniel Ehrenberg — 2007-08-16 20:45:02

I'm not sure if I understand your no-garbage-collection idea properly.
How do you plan to make useful compound data structures when two
things cannot point to the same object in memory? It sounds like there
would be a lot of copying involved. If two functions want to index the
same array, will they each need a copy of that array? What about for
more complicated things, like binary trees; won't balancing require
more copying than is necessary in other languages?

Daniel Ehrenberg

On 8/16/07, Don Groves <dgroves@...> wrote:
> A few more Catenate features:
>
> Dynamic typing:
> Data is typed; names are not. A name can hold any type of data.
>
> Linearity:
> Every memory cell has exactly one reference. When de-referenced, a
> cell is cleared and is immediately available for re-allocation. Thus,
> garbage collection is trivial (marking without sweeping ;-)
>
> Unicode:
> UTF8 encoding is used throughout.
>
> Syntax:
> Probably best described as a math-inspired, Lispish Joy. For example,
> here is a sample terminal session:
>
> stack: { }
> => 3
>
> stack: {3}
> => 4 *
>
> stack: {12}
> => _
>
> And a more complex one calculating the area of a circle of radius 3:
>
> stack: { }
> => 3.1459 defcon π
>
> π defined
>
> stack: { }
> => [dup *] defun ^2
>
> ^2 defined
>
> stack: { }
> => [^2 π *] defun πr^2
>
> πr^2 defined
>
> stack: { }
> => 3 πr^2
>
> stack: {28.3131 }
> => _
>
> Questions, comments, and suggestions will be gladly received,
> Don Groves
>
>
>
>
> Yahoo! Groups Links
>
>
>
>

Don Groves — 2007-08-16 21:37:46

Hi Daniel,

Yes, Catenate will require more copying than non-linear languages but
the benefits of linearity outweigh the extra copying in my opinion.

I haven't considered the idea of two functions wanting to index the same
array but that doesn't sound like something I'd need to do anyway, at
least
it's never come up for me before ;-)

There still may be serious problems I haven't foreseen with my approach,
I'm no expert in all this and there's a lot of learning as I go
along. Kinda
what makes it so much fun!
--
Don Groves


On Aug 16, 2007, at 19:45 , Daniel Ehrenberg wrote:

> I'm not sure if I understand your no-garbage-collection idea properly.
> How do you plan to make useful compound data structures when two
> things cannot point to the same object in memory? It sounds like there
> would be a lot of copying involved. If two functions want to index the
> same array, will they each need a copy of that array? What about for
> more complicated things, like binary trees; won't balancing require
> more copying than is necessary in other languages?
>
> Daniel Ehrenberg
>
> On 8/16/07, Don Groves <dgroves@...> wrote:
>> A few more Catenate features:
>>
>> Dynamic typing:
>> Data is typed; names are not. A name can hold any type of data.
>>
>> Linearity:
>> Every memory cell has exactly one reference. When de-referenced, a
>> cell is cleared and is immediately available for re-allocation. Thus,
>> garbage collection is trivial (marking without sweeping ;-)
>>
>> Unicode:
>> UTF8 encoding is used throughout.
>>
>> Syntax:
>> Probably best described as a math-inspired, Lispish Joy. For example,
>> here is a sample terminal session:
>>
>> stack: { }
>> => 3
>>
>> stack: {3}
>> => 4 *
>>
>> stack: {12}
>> => _
>>
>> And a more complex one calculating the area of a circle of radius 3:
>>
>> stack: { }
>> => 3.1459 defcon π
>>
>> π defined
>>
>> stack: { }
>> => [dup *] defun ^2
>>
>> ^2 defined
>>
>> stack: { }
>> => [^2 π *] defun πr^2
>>
>> πr^2 defined
>>
>> stack: { }
>> => 3 πr^2
>>
>> stack: {28.3131 }
>> => _
>>
>> Questions, comments, and suggestions will be gladly received,
>> Don Groves
>>
>>
>>
>>
>> Yahoo! Groups Links
>>
>>
>>
>>
>
>
>
> Yahoo! Groups Links
>
>
>

William Tanksley, Jr — 2007-08-16 22:57:55

Don Groves <dgroves@...> wrote:
> I haven't considered the idea of two functions wanting to index the same
> array but that doesn't sound like something I'd need to do anyway, at
> least it's never come up for me before ;-)

If the functions need to do that sequentially, there's no problem --
you pass the reference to the first function, then the second
function. If they need to operate in parallel you have to make a copy.

> There still may be serious problems I haven't foreseen with my approach,
> I'm no expert in all this and there's a lot of learning as I go
> along. Kinda what makes it so much fun!

Obviously you've read your Henry Baker,
http://home.pipeline.com/~hbaker1/ForthStack.html (I cite this for the
benefit of others interested). He explains how linear logic works and
why it insists on only one reference per object.

> Don Groves

-Billy

Don Groves — 2007-08-17 01:53:59

Hi Billy,

Yes, I've read Baker's papers and they obviously affected my thinking
about languages.

Thanks for the tip on the other topic.
--
Don



On Aug 16, 2007, at 19:57 , William Tanksley, Jr wrote:

> Don Groves <dgroves@...> wrote:
>> I haven't considered the idea of two functions wanting to index
>> the same
>> array but that doesn't sound like something I'd need to do anyway, at
>> least it's never come up for me before ;-)
>
> If the functions need to do that sequentially, there's no problem --
> you pass the reference to the first function, then the second
> function. If they need to operate in parallel you have to make a copy.
>
>> There still may be serious problems I haven't foreseen with my
>> approach,
>> I'm no expert in all this and there's a lot of learning as I go
>> along. Kinda what makes it so much fun!
>
> Obviously you've read your Henry Baker,
> http://home.pipeline.com/~hbaker1/ForthStack.html (I cite this for the
> benefit of others interested). He explains how linear logic works and
> why it insists on only one reference per object.
>
>> Don Groves
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>

pml060912 — 2007-08-17 10:00:09

--- In concatenative@yahoogroups.com, Don Groves <dgroves@...> wrote:
>
> Thanks, Christopher and Peter for your kind replies.
>
> I've looked at Cat before, Christopher, and found it very impressive!
> I'll be
> most pleased if Catenate ever approaches Cat's level of sophistication.
>
> I just tried to read up on Furphy but the url wasn't found, so Peter
> may be
> changing his ISP even sooner that he thought.

I checked, and it's actually at
http://member.netlink.com.au/~peterl/furphy.htm - I was quoting an
earlier version from before the ISP moved everything around to suit
itself. If you get there you'll see that I too looked at a fair bit of
Henry Baker's thinking on linearity. The idea of not having to make
repeated copies by passing a reference to one function after another
sort of works, but it forces you into continuation passing (or
something very like it), which may not suit the internal logic of the
functions - or your own personal style. There's no real saving if the
function effectively has to copy to have something to work with itself.

One thing I thought of that would help with linearity is to define
Forth style @ and ! in terms of DUP, DROP and an inherently
conservative/linear MEMSWAP ( newconts addr --- oldconts addr ) or
possibly with stack effects the other way around, and maybe in terms
of SWAP and other inherently conservative/linear stuff too. Then a
wide range of behaviour can be built on the @ and ! even if those
don't get made publicly available in the main language, and you only
need to get DUP and DROP respecting the linearity to carry everything
else. You can implement dedicated efficient versions later, but this
lets you have everything in a prototype carried on a small base that
you can more easily test fully. This is paper model stuff I have only
sketched out so far, not prototyped yet.

The thing that reminded me of that is, MEMSWAP does naturally work in
that continuation passing way just to be conservative/linear. You have
to throw away an unwanted address on purpose, separately. PML.

Don Groves — 2007-08-17 19:07:06

Hi Peter,

I found it last evening by resorting to Google. You've got some
intriguing
ideas in Furphy, several things I haven't even considered yet. I've only
been working on Catenate since June, so it'll take a while for me to
catch
up with the rest of you. At the moment, I'm in major assimilation mode.
--
Don Groves

Programmer (n): An organism that turns coffee and cookies into software.



On Aug 17, 2007, at 19:00 , pml060912 wrote:

> --- In concatenative@yahoogroups.com, Don Groves <dgroves@...> wrote:
>>
>> Thanks, Christopher and Peter for your kind replies.
>>
>> I've looked at Cat before, Christopher, and found it very impressive!
>> I'll be
>> most pleased if Catenate ever approaches Cat's level of
>> sophistication.
>>
>> I just tried to read up on Furphy but the url wasn't found, so Peter
>> may be
>> changing his ISP even sooner that he thought.
>
> I checked, and it's actually at
> http://member.netlink.com.au/~peterl/furphy.htm - I was quoting an
> earlier version from before the ISP moved everything around to suit
> itself. If you get there you'll see that I too looked at a fair bit of
> Henry Baker's thinking on linearity. The idea of not having to make
> repeated copies by passing a reference to one function after another
> sort of works, but it forces you into continuation passing (or
> something very like it), which may not suit the internal logic of the
> functions - or your own personal style. There's no real saving if the
> function effectively has to copy to have something to work with
> itself.
>
> One thing I thought of that would help with linearity is to define
> Forth style @ and ! in terms of DUP, DROP and an inherently
> conservative/linear MEMSWAP ( newconts addr --- oldconts addr ) or
> possibly with stack effects the other way around, and maybe in terms
> of SWAP and other inherently conservative/linear stuff too. Then a
> wide range of behaviour can be built on the @ and ! even if those
> don't get made publicly available in the main language, and you only
> need to get DUP and DROP respecting the linearity to carry everything
> else. You can implement dedicated efficient versions later, but this
> lets you have everything in a prototype carried on a small base that
> you can more easily test fully. This is paper model stuff I have only
> sketched out so far, not prototyped yet.
>
> The thing that reminded me of that is, MEMSWAP does naturally work in
> that continuation passing way just to be conservative/linear. You have
> to throw away an unwanted address on purpose, separately. PML.
>
>
>
>
> Yahoo! Groups Links
>
>
>
>

Manfred Von Thun — 2007-09-07 08:06:57

For a long time I have been interested in the difference
between the standard kind of lambda calculus languages
and the concatenative languages. Only recently did it
occur to me that there is a third class which has been around
for some time. The three differ in how they treat actual
and formal parameters in definitions of functions and in
calls of functions. At one extreme are the concatenative
languages, at the other extreme those in the third class.
The standard lambda calculus languages are half way
between these two extremes.

Most programming languages belong in the class of
lambda calculus languages. Definitions look more or
less like this:

DEFINE foo(x,y,z) # head of definition
= ...x...y...z... # body of definition


So there is a list of formal parameters (x,y,z) in the
head, and an expression ...x...y...z... which is the body.
In the body each of the formal parameters occurs one
or more times, or even not at all. The various occurrences
can be in any order. (This is one of the strengths of
lambda calculus notation.) Once you have written the
entire definition, you can change your mind about the
parameter list: you can change it to (y,z,x) and there
is no need to change the body in any way. In a call
to the function, foo(a,b,c), with actual parameters (a,b,c)
the values of the actual parameters are given to the
corresponding formal parameters. Of course the effect
of the call depends on whether the original definition with
(x,y,z) or the revised definition with (y,z,x) is in force.
The order of the actual parameters is taken to match
the order of the formal parameters. To summarise:
In the body the formal parameters are used as names,
irrespective of their order in the parameter list;
in a call the order of the actuals and the formals are
the same, irrespective of the names of the formals.

In concatenative languages there are no formal parameter
lists. Definitions look like this:

DEFINE foo # head of definition
= ...dup...swap... pop... # body of definition

Instead of being able to refer to the actual parameters
by the name of the corresponding formal parameter,
it is now necessary to take the actual parameters as
they are on the stack, and do some shuffling to get
everything right in the body. Once you have written the
entire definition, you cannot just change the parameter
list because there isn¹t one. You also have to change the body of
the definition. In a call the order of the actual parameters
on the stack has to be in the order in which the body
of the definition expects them. To summarise:
In the body of the definition there is an expected order
of the actual parameters on the stack; in a call
the actual parameters have to be in the in the order
in which the body expects them.

So at the one extreme we can have the concatenative
method: there are no names, body and call
are done by order (on the stack). In the middle we have the
standard lambda calculus notation: there are names
which are used in the body, but calls use positions
that match the order of the formals in the parameter
list. At the other extreme there should be languages
which do not use order at all but rely on names both
in the body and in calls. Are there such beasts?

In such languages definitions would again look as in
lambda calculus languages:

DEFINE foo(x,y,z) # head of definition
= ...x...y...z... # body of definition

As before, you can change the order of the parameters
without any need to change the body of the definition.
But a call would look quite different: instead of relying
on the order of the formal parameters, use their names
as assignment statements, and make the assignments
in any order. Hence any of the following would be OK
as a call:

foo(x=a; y=b; z=c)
foo(y=b; z=c; x=a)
foo(z=c; y=b; x=a)

and so forth, a total of six possible combinations. All such
calls would be equivalent. To summarise: body and call
are done by using the names of the formal parameters.
My understanding is that there are some languages which
allow this kind of notation (Common Lisp ?, Ada ?) but
I cannot remember any details --- anyone?? But as far
as I know these languages use standard positional notation
by default and allow this other notation only as a
convenient variation. I have never heard of a language
in which this way of calling is standard. Anyone??

BEGIN COMMENT:
These languages can allow default parameters, and
definitions would look like this:

DEFINE foo(x=1,y=0,z=5) # head of definition
= ...x...y...z... # body of definition

Calls can then omit giving a value to a particular formal
parameter, in which case the default is used. Example:

foo(x=42,z=3) is equivalent to foo(z=3,x=42,y=0)

In Unix just about all of the utilities have many parameters
which default to sensible values. Consequently casual
users rarely need to know about them, but they
are there to be modified if one needs it.
END COMMENT

So, we seem to have three possiblities:
(1) bodies and calls rely on position
(2) bodies rely on names, calls rely on position
(3) bodies and calls rely on names

Nice to know that concatenative languages are pure,
isn¹t it. I thought this grouping of classes of languages
might be of interest to other language smiths.

- Manfred



[Non-text portions of this message have been removed]

rahul — 2007-09-07 09:07:55

|On 9/7/07, Manfred Von Thun <m.vonthun@...> wrote:
|...
|But as far
|as I know these languages use standard positional notation
|by default and allow this other notation only as a
|convenient variation. I have never heard of a language
|in which this way of calling is standard. Anyone??

SmallTalk? (and its descendants - ObjectiveC and ruby)
though in ruby this is not the default.
(the unix model of options, being the other, but it is not an enforced
standard.)

a SmallTalk example.
======================================
multiply: i1 with: i2 and: i3
"pointless multiply"
| mul |
mul := i1 * i2 * i3.
^mul
======================================
Usage:
>multiply 3 with:2 and: 5
=30
SmallTalk is also pure in its other aspects (much more than any of its
descendants). ie: the control structures are built on top of this
model.

John Cowan — 2007-09-07 14:32:40

Manfred Von Thun scripsit:

> My understanding is that there are some languages which
> allow this kind of notation (Common Lisp ?, Ada ?) but
> I cannot remember any details --- anyone??

Common Lisp (as well as some other Lisps such as DSSSL and Dylan)
does indeed allow keyword arguments. They must physically follow
the positional arguments (if any) both in declarations and in calls.
It is also possible to capture unknown keyword arguments for analysis
at runtime.

In certain domains keyword arguments are used by default. For example,
CL object constructors are normally keyword-based: you specify the
fields of a class by name, and the default constructor will accept
keyword arguments that initialize the fields. (It's also possible to
write non-default constructors that work like ordinary functions, by
order of arguments -- these are known as BOA constructors.)

Python has a unified model: parameters are declared by name, and calls
may use keywords, but non-keyworded arguments are assigned positionally.
However, in calls non-keyword arguments must precede keyword ones.
Again, it is possible to capture unknown keyword arguments.

Ada also has a unified model, but there are no constraints on ordering
in calls, and there is no provision for unknown keywords. Fortran 90
and 95 also use this model.

Lua provides modest syntactic sugar for keyword-only calls: this is
declared as a single argument representing a table mapping keywords
to their values, so all analysis must be done at run time. In Perl,
there is no special sugar, but passing a hash is common.

Algol 60 allowed calls of the form:

multiply(x) by: (y)

but this meant the same as "multiply(x, y)" and the keywords had no
significance to the compiler.

Smalltalk is keyword-only, as has been pointed out, except in the cases of
unary operators (postfix) and binary operators (infix). Binary operators
are syntactically restricted to use symbol characters only.

--
John Cowan http://ccil.org/~cowan cowan@...
SAXParserFactory [is] a hideous, evil monstrosity of a class that should
be hung, shot, beheaded, drawn and quartered, burned at the stake,
buried in unconsecrated ground, dug up, cremated, and the ashes tossed
in the Tiber while the complete cast of Wicked sings "Ding dong, the
witch is dead." --Elliotte Rusty Harold on xml-dev

Rodney D Price — 2007-09-07 17:11:56

This classification confuses me a bit. Are we talking about compile
time
or run time? There are lots of evaluation strategies that occur at
run time;
see http://en.wikipedia.org/wiki/Call_by_name for a nice summary. Given
that the code, whether written out as function calls with parameters
called
out by name or by position, compiles to one of the run time behaviors
listed in the article above, what difference does it make whether the
syntax
calls out the parameters by name or by position? That syntax
question is
independent of the semantics question about evaluation strategies.

So which is it? Syntax or semantics?

-Rod



On Sep 7, 2007, at 8:32 AM, John Cowan wrote:

> Manfred Von Thun scripsit:
>
> > My understanding is that there are some languages which
> > allow this kind of notation (Common Lisp ?, Ada ?) but
> > I cannot remember any details --- anyone??
>
> Common Lisp (as well as some other Lisps such as DSSSL and Dylan)
> does indeed allow keyword arguments. They must physically follow
> the positional arguments (if any) both in declarations and in calls.
> It is also possible to capture unknown keyword arguments for analysis
> at runtime.
>
> In certain domains keyword arguments are used by default. For example,
> CL object constructors are normally keyword-based: you specify the
> fields of a class by name, and the default constructor will accept
> keyword arguments that initialize the fields. (It's also possible to
> write non-default constructors that work like ordinary functions, by
> order of arguments -- these are known as BOA constructors.)
>
> Python has a unified model: parameters are declared by name, and calls
> may use keywords, but non-keyworded arguments are assigned
> positionally.
> However, in calls non-keyword arguments must precede keyword ones.
> Again, it is possible to capture unknown keyword arguments.
>
> Ada also has a unified model, but there are no constraints on ordering
> in calls, and there is no provision for unknown keywords. Fortran 90
> and 95 also use this model.
>
> Lua provides modest syntactic sugar for keyword-only calls: this is
> declared as a single argument representing a table mapping keywords
> to their values, so all analysis must be done at run time. In Perl,
> there is no special sugar, but passing a hash is common.
>
> Algol 60 allowed calls of the form:
>
> multiply(x) by: (y)
>
> but this meant the same as "multiply(x, y)" and the keywords had no
> significance to the compiler.
>
> Smalltalk is keyword-only, as has been pointed out, except in the
> cases of
> unary operators (postfix) and binary operators (infix). Binary
> operators
> are syntactically restricted to use symbol characters only.
>
> --
> John Cowan http://ccil.org/~cowan cowan@...
> SAXParserFactory [is] a hideous, evil monstrosity of a class that
> should
> be hung, shot, beheaded, drawn and quartered, burned at the stake,
> buried in unconsecrated ground, dug up, cremated, and the ashes tossed
> in the Tiber while the complete cast of Wicked sings "Ding dong, the
> witch is dead." --Elliotte Rusty Harold on xml-dev
>
>



[Non-text portions of this message have been removed]

William Tanksley, Jr — 2007-09-07 18:05:46

Rodney D Price <rodprice@...> wrote:
> So which is it? Syntax or semantics?

Both. It's about mapping syntax onto semantics. That's what a
programming language does.

I do agree with you that I'm not sure about the significance of this.
The actual difference is limited entirely to the function call
itself... That's a tiny part of a language.

BTW, I'd say that as much as I like Smalltalk selectors, they don't
qualify for this: they can't be used out of order, and in fact they're
part of the method name, not the parameters.

> -Rod

-Billy

Christopher Diggins — 2007-09-07 18:19:04

It is possible in C++ with library support:
http://www.boost.org/libs/parameter/doc/html/index.html

On 9/7/07, Manfred Von Thun <m.vonthun@...> wrote:
>
> For a long time I have been interested in the difference
> between the standard kind of lambda calculus languages
> and the concatenative languages. Only recently did it
> occur to me that there is a third class which has been around
> for some time. The three differ in how they treat actual
> and formal parameters in definitions of functions and in
> calls of functions. At one extreme are the concatenative
> languages, at the other extreme those in the third class.
> The standard lambda calculus languages are half way
> between these two extremes.
>
> Most programming languages belong in the class of
> lambda calculus languages. Definitions look more or
> less like this:
>
> DEFINE foo(x,y,z) # head of definition
> = ...x...y...z... # body of definition
>
> So there is a list of formal parameters (x,y,z) in the
> head, and an expression ...x...y...z... which is the body.
> In the body each of the formal parameters occurs one
> or more times, or even not at all. The various occurrences
> can be in any order. (This is one of the strengths of
> lambda calculus notation.) Once you have written the
> entire definition, you can change your mind about the
> parameter list: you can change it to (y,z,x) and there
> is no need to change the body in any way. In a call
> to the function, foo(a,b,c), with actual parameters (a,b,c)
> the values of the actual parameters are given to the
> corresponding formal parameters. Of course the effect
> of the call depends on whether the original definition with
> (x,y,z) or the revised definition with (y,z,x) is in force.
> The order of the actual parameters is taken to match
> the order of the formal parameters. To summarise:
> In the body the formal parameters are used as names,
> irrespective of their order in the parameter list;
> in a call the order of the actuals and the formals are
> the same, irrespective of the names of the formals.
>
> In concatenative languages there are no formal parameter
> lists. Definitions look like this:
>
> DEFINE foo # head of definition
> = ...dup...swap... pop... # body of definition
>
> Instead of being able to refer to the actual parameters
> by the name of the corresponding formal parameter,
> it is now necessary to take the actual parameters as
> they are on the stack, and do some shuffling to get
> everything right in the body. Once you have written the
> entire definition, you cannot just change the parameter
> list because there isn¹t one. You also have to change the body of
> the definition. In a call the order of the actual parameters
> on the stack has to be in the order in which the body
> of the definition expects them. To summarise:
> In the body of the definition there is an expected order
> of the actual parameters on the stack; in a call
> the actual parameters have to be in the in the order
> in which the body expects them.
>
> So at the one extreme we can have the concatenative
> method: there are no names, body and call
> are done by order (on the stack). In the middle we have the
> standard lambda calculus notation: there are names
> which are used in the body, but calls use positions
> that match the order of the formals in the parameter
> list. At the other extreme there should be languages
> which do not use order at all but rely on names both
> in the body and in calls. Are there such beasts?
>
> In such languages definitions would again look as in
> lambda calculus languages:
>
> DEFINE foo(x,y,z) # head of definition
> = ...x...y...z... # body of definition
>
> As before, you can change the order of the parameters
> without any need to change the body of the definition.
> But a call would look quite different: instead of relying
> on the order of the formal parameters, use their names
> as assignment statements, and make the assignments
> in any order. Hence any of the following would be OK
> as a call:
>
> foo(x=a; y=b; z=c)
> foo(y=b; z=c; x=a)
> foo(z=c; y=b; x=a)
>
> and so forth, a total of six possible combinations. All such
> calls would be equivalent. To summarise: body and call
> are done by using the names of the formal parameters.
> My understanding is that there are some languages which
> allow this kind of notation (Common Lisp ?, Ada ?) but
> I cannot remember any details --- anyone?? But as far
> as I know these languages use standard positional notation
> by default and allow this other notation only as a
> convenient variation. I have never heard of a language
> in which this way of calling is standard. Anyone??
>
> BEGIN COMMENT:
> These languages can allow default parameters, and
> definitions would look like this:
>
> DEFINE foo(x=1,y=0,z=5) # head of definition
> = ...x...y...z... # body of definition
>
> Calls can then omit giving a value to a particular formal
> parameter, in which case the default is used. Example:
>
> foo(x=42,z=3) is equivalent to foo(z=3,x=42,y=0)
>
> In Unix just about all of the utilities have many parameters
> which default to sensible values. Consequently casual
> users rarely need to know about them, but they
> are there to be modified if one needs it.
> END COMMENT
>
> So, we seem to have three possiblities:
> (1) bodies and calls rely on position
> (2) bodies rely on names, calls rely on position
> (3) bodies and calls rely on names
>
> Nice to know that concatenative languages are pure,
> isn¹t it. I thought this grouping of classes of languages
> might be of interest to other language smiths.
>
> - Manfred
>
> [Non-text portions of this message have been removed]
>
>
>


[Non-text portions of this message have been removed]

Stevan Apter — 2007-09-07 21:36:49

off the top of my head, i can imagine a construct like this
(i'm assuming iverson notation, in which the combinator is
what he would call a conjunction):

O is a combinator that takes a subset of a permutation
of the names of the arguments of f (on the left) and a
lambda containing those arguments (on the right), and
returns a function which binds those arguments to values.

f:{[a;b;c]a+b*c}

f . 10 20 30
610

(`c`b`a O f) . 10 20 30
230

then, for example,

(`c`a O f) . 10 20

returns a function of one argument b in which c is bound to
10 and a to 20.

my first reaction is that i shouldn't know anything about f
except the mapping of arguments to result, least of all what
names the programmer chose for the arguments. my second
reaction is that every function should be documented, and
that it might be thought that well-chosen argument names
should be exposed to the user of the function.

what about primitives, like -? i suppose the language could
just say that every primitive of one argument names its
argument x, of two x and y, &c. then

2(`y`x O -)3
1

it's not hard to write O in k.

----- Original Message -----
From: "Manfred Von Thun" <m.vonthun@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, September 07, 2007 3:06 AM
Subject: [stack] Parameters: ordered versus named


For a long time I have been interested in the difference
between the standard kind of lambda calculus languages
and the concatenative languages. Only recently did it
occur to me that there is a third class which has been around
for some time. The three differ in how they treat actual
and formal parameters in definitions of functions and in
calls of functions. At one extreme are the concatenative
languages, at the other extreme those in the third class.
The standard lambda calculus languages are half way
between these two extremes.

Most programming languages belong in the class of
lambda calculus languages. Definitions look more or
less like this:

DEFINE foo(x,y,z) # head of definition
= ...x...y...z... # body of definition


So there is a list of formal parameters (x,y,z) in the
head, and an expression ...x...y...z... which is the body.
In the body each of the formal parameters occurs one
or more times, or even not at all. The various occurrences
can be in any order. (This is one of the strengths of
lambda calculus notation.) Once you have written the
entire definition, you can change your mind about the
parameter list: you can change it to (y,z,x) and there
is no need to change the body in any way. In a call
to the function, foo(a,b,c), with actual parameters (a,b,c)
the values of the actual parameters are given to the
corresponding formal parameters. Of course the effect
of the call depends on whether the original definition with
(x,y,z) or the revised definition with (y,z,x) is in force.
The order of the actual parameters is taken to match
the order of the formal parameters. To summarise:
In the body the formal parameters are used as names,
irrespective of their order in the parameter list;
in a call the order of the actuals and the formals are
the same, irrespective of the names of the formals.

In concatenative languages there are no formal parameter
lists. Definitions look like this:

DEFINE foo # head of definition
= ...dup...swap... pop... # body of definition

Instead of being able to refer to the actual parameters
by the name of the corresponding formal parameter,
it is now necessary to take the actual parameters as
they are on the stack, and do some shuffling to get
everything right in the body. Once you have written the
entire definition, you cannot just change the parameter
list because there isn¹t one. You also have to change the body of
the definition. In a call the order of the actual parameters
on the stack has to be in the order in which the body
of the definition expects them. To summarise:
In the body of the definition there is an expected order
of the actual parameters on the stack; in a call
the actual parameters have to be in the in the order
in which the body expects them.

So at the one extreme we can have the concatenative
method: there are no names, body and call
are done by order (on the stack). In the middle we have the
standard lambda calculus notation: there are names
which are used in the body, but calls use positions
that match the order of the formals in the parameter
list. At the other extreme there should be languages
which do not use order at all but rely on names both
in the body and in calls. Are there such beasts?

In such languages definitions would again look as in
lambda calculus languages:

DEFINE foo(x,y,z) # head of definition
= ...x...y...z... # body of definition

As before, you can change the order of the parameters
without any need to change the body of the definition.
But a call would look quite different: instead of relying
on the order of the formal parameters, use their names
as assignment statements, and make the assignments
in any order. Hence any of the following would be OK
as a call:

foo(x=a; y=b; z=c)
foo(y=b; z=c; x=a)
foo(z=c; y=b; x=a)

and so forth, a total of six possible combinations. All such
calls would be equivalent. To summarise: body and call
are done by using the names of the formal parameters.
My understanding is that there are some languages which
allow this kind of notation (Common Lisp ?, Ada ?) but
I cannot remember any details --- anyone?? But as far
as I know these languages use standard positional notation
by default and allow this other notation only as a
convenient variation. I have never heard of a language
in which this way of calling is standard. Anyone??

BEGIN COMMENT:
These languages can allow default parameters, and
definitions would look like this:

DEFINE foo(x=1,y=0,z=5) # head of definition
= ...x...y...z... # body of definition

Calls can then omit giving a value to a particular formal
parameter, in which case the default is used. Example:

foo(x=42,z=3) is equivalent to foo(z=3,x=42,y=0)

In Unix just about all of the utilities have many parameters
which default to sensible values. Consequently casual
users rarely need to know about them, but they
are there to be modified if one needs it.
END COMMENT

So, we seem to have three possiblities:
(1) bodies and calls rely on position
(2) bodies rely on names, calls rely on position
(3) bodies and calls rely on names

Nice to know that concatenative languages are pure,
isn¹t it. I thought this grouping of classes of languages
might be of interest to other language smiths.

- Manfred



[Non-text portions of this message have been removed]

rahul — 2007-09-07 21:53:27

[William Tanksley, Jr <wtanksleyjr@...>]:

> BTW, I'd say that as much as I like Smalltalk selectors, they don't
> qualify for this: they can't be used out of order, and in fact they're
> part of the method name, not the parameters.

True, SmallTalk does not fit the bill - (had missed it.) though it seems
an arbitrary restriction for SmallTalk since it is enforcing the keys.

John Cowan — 2007-09-07 23:11:44

rahul scripsit:

> True, SmallTalk does not fit the bill - (had missed it.) though it seems
> an arbitrary restriction for SmallTalk since it is enforcing the keys.

You can override this if you want by defining withFoo:withBar: to mean
the same as withBar:withFoo:, and such a feature could be integrated
into the Smalltalk code browser if it seemed useful. However, method
overriding becomes tricky.

--
What asininity could I have uttered John Cowan <cowan@...>
that they applaud me thus? http://www.ccil.org/~cowan
--Phocion, Greek orator

kuwabatake — 2007-09-08 00:32:14

--- In concatenative@yahoogroups.com, Manfred Von Thun <m.vonthun@...>
wrote:

> foo(x=a; y=b; z=c)
> foo(y=b; z=c; x=a)
> foo(z=c; y=b; x=a)

Huh. This is very interesting.

One thing that springs to mind is that some parts of assembly
languages behave like this, using registers to hold arguments:

MOVE x <- a
MOVE y <- b
MOVE z <- c
CALL foo

..where the first three lines can be permuted any which way.
In an even purer version of this idea, expressions might not be
allowed on the right side of assignments (another `order matters'
`anonymous temp value' kind of technique) so instead of

MOVE x <- y + bar(1)

you would need to write

MOVE barIn <- 1
CALL bar
MOVE +In1 <- y
MOVE +In2 <- barOut
CALL +
MOVE x <- +Out

~Daniel

William Tanksley, Jr — 2007-09-08 00:47:28

kuwabatake <kuwabatake@...> wrote:
> One thing that springs to mind is that some parts of assembly
> languages behave like this, using registers to hold arguments:

That's true, but it doesn't fit this model, because this model is
about function calls, while those assignments have global effect.

> ~Daniel

-Billy

Don Groves — 2007-09-08 02:11:37

On Sep 7, 2007, at 19:06 , Manfred Von Thun wrote:

...

> In concatenative languages there are no formal parameter
> lists. Definitions look like this:
>
> DEFINE foo # head of definition
> = ...dup...swap... pop... # body of definition
>
> Instead of being able to refer to the actual parameters
> by the name of the corresponding formal parameter,
> it is now necessary to take the actual parameters as
> they are on the stack, and do some shuffling to get
> everything right in the body. Once you have written the
> entire definition, you cannot just change the parameter
> list because there isn’t one. You also have to change the body of
> the definition.

I believe it was sci-fi writer Robert Heinlein who coined the phrase,
"There
ain't no such thing as a free lunch." The price we pay for the
simplicity of
concatenative syntax is the amount of parameter checking we must do at
run-time which other languages to at compile-time.

The parametric details in DEFINE foo (int x, char y,void *z), etc.
are explicit
whereas they are implicit in DEFINE foo = ...dup...swap, etc. To give
a truly
realistic picture of a concatenative foo, shouldn't we also give its
stack effect,
as in this for map:

[a b ...] [f] map ==> [af bf ...] (where xf denotes the application
of f to x)

This makes it explicit that map must ensure the stack contains at
least two
parameters, that they are lists, and that xf makes sense for each x.

One beauty of concatenative notation is the hiding of information
such as
described here, but we all know that hidden info must be dealt with
eventually,
unless the language is so primitive as to not care about these matters.
--
Don Groves

William Tanksley, Jr — 2007-09-08 03:03:13

Don Groves <dgroves@...> wrote:
> I believe it was sci-fi writer Robert Heinlein who coined the phrase,
> "There ain't no such thing as a free lunch."

Possibly -- that's where I read it too.

> The price we pay for the
> simplicity of
> concatenative syntax is the amount of parameter checking we must do at
> run-time which other languages to at compile-time.

Not true. Concatenative semantics don't require parameter passing, so
there is nothing to do to parameters at runtime. There are, of course,
other prices we must pay; but that's not one of them.

> The parametric details in DEFINE foo (int x, char y,void *z), etc.
> are explicit
> whereas they are implicit in DEFINE foo = ...dup...swap, etc. To give

There are concatenative languages that require explicit type
specifications; Cat, Factor, and strongForth are three very different
examples. In general, most strongly statically typed concatenative
languages have defined a syntax something like this

"DEFINE foo ( int char pointer-to-void -- int )". The syntax is
similar to Forth's classical stack diagrams.

> a truly
> realistic picture of a concatenative foo, shouldn't we also give its
> stack effect, as in this for map:
> [a b ...] [f] map ==> [af bf ...] (where xf denotes the application
> of f to x)

We definitely should -- it's a good habit, even if the language you're
using doesn't use static types, you should put that in a comment.

> One beauty of concatenative notation is the hiding of information
> such as described here,

Nothing's hidden.

> but we all know that hidden info must be dealt with
> eventually,
> unless the language is so primitive as to not care about these matters.

But this is true. (Well, mostly -- Cat does type inferencing so you
don't always have to describe the types, while Enchilada defines its
semantics so that all its types are compatible.)

> Don Groves

-Billy

Joe Bowbeer — 2007-09-08 03:38:13

On 9/7/07, Manfred Von Thun <m.vonthun@...> wrote:
>
> In such languages definitions would again look as in
> lambda calculus languages:
>
> DEFINE foo(x,y,z) # head of definition
> = ...x...y...z... # body of definition
>
> As before, you can change the order of the parameters
> without any need to change the body of the definition.
> But a call would look quite different: instead of relying
> on the order of the formal parameters, use their names
> as assignment statements, and make the assignments
> in any order. Hence any of the following would be OK
> as a call:
>
> foo(x=a; y=b; z=c)
> foo(y=b; z=c; x=a)
> foo(z=c; y=b; x=a)
>

Python has optional keyword arguments with default values:

http://docs.python.org/tut/node6.html#SECTION006720000000000000000

E.g., eggs(x=a, y=b, z=c)

See also:

http://en.wikipedia.org/wiki/Named_parameter

--Joe

PS - This also reminds me of dynamic binding in LISP, where you could
do something degenerative like:

(let ((x a) (y b) (z c)) foo())

Don Groves — 2007-09-08 04:39:15

On Sep 7, 2007, at 19:03 , William Tanksley, Jr wrote:

> Don Groves <dgroves@...> wrote:
>
>> The price we pay for the
>> simplicity of
>> concatenative syntax is the amount of parameter checking we must
>> do at
>> run-time which other languages to at compile-time.
>
> Not true. Concatenative semantics don't require parameter passing, so
> there is nothing to do to parameters at runtime. There are, of course,
> other prices we must pay; but that's not one of them.

While it's true there's nothing to do "to" parameters, we still must
check for
their existence, yes? If you want to trap stack underflow before it
happens,
for example. And what do we do when a user enters a string and a number
then types "/"?


>> One beauty of concatenative notation is the hiding of information
>> such as described here,
>
> Nothing's hidden.
>
>> but we all know that hidden info must be dealt with
>> eventually,
>> unless the language is so primitive as to not care about these
>> matters.
>
> But this is true. (Well, mostly -- Cat does type inferencing so you
> don't always have to describe the types, while Enchilada defines its
> semantics so that all its types are compatible.)

Afraid I'm confused here. You say nothing is hidden, yet you agree (at
least partially) with my next statement.

As always, any corrections or advice is greatly appreciated. I'm here to
learn! So don't take any questions or statements on my part as anything
other than trying to understand ;-)

> -Billy

--
Don Groves

PS - Anyone know why my posts are so jagged looking? When I send
them, they are evenly formatted. Could it be caused by my email program,
mail.app on OS X?

Christopher Diggins — 2007-09-08 15:55:02

On 9/8/07, Don Groves <dgroves@...> wrote:
>
> On Sep 7, 2007, at 19:03 , William Tanksley, Jr wrote:
>
> > Don Groves <dgroves@...> wrote:
> >
> >> The price we pay for the
> >> simplicity of
> >> concatenative syntax is the amount of parameter checking we must
> >> do at
> >> run-time which other languages to at compile-time.
> >
> > Not true. Concatenative semantics don't require parameter passing, so
> > there is nothing to do to parameters at runtime. There are, of course,
> > other prices we must pay; but that's not one of them.
>
> While it's true there's nothing to do "to" parameters, we still must
> check for
> their existence, yes? If you want to trap stack underflow before it
> happens,
> for example. And what do we do when a user enters a string and a number
> then types "/"?

This is where a static type system with type inference comes in
useful. You can do all of this without annotation or runtime checks in
the compiler. See Cat (http://www.cat-language.com).

- Christopher

William Tanksley, Jr — 2007-09-08 17:18:43

Don Groves <dgroves@...> wrote:
> William Tanksley, Jr wrote:
> > Don Groves <dgroves@...> wrote:
> >> The price we pay for the
> >> simplicity of
> >> concatenative syntax is the amount of parameter checking we must
> >> do at run-time which other languages to at compile-time.

> > Not true. Concatenative semantics don't require parameter passing, so
> > there is nothing to do to parameters at runtime. There are, of course,
> > other prices we must pay; but that's not one of them.

> While it's true there's nothing to do "to" parameters, we still must
> check for
> their existence, yes? If you want to trap stack underflow before it
> happens,
> for example. And what do we do when a user enters a string and a number
> then types "/"?

If you want to stop it before it happens, you're going to do static
analysis. That can be done in a concatenative language using a
linear-time algorithm plus some annotations (and provide typechecking
as a bonus), or it can be done using type inference (using the same
algorithms that many modern functional languages use). StrongForth
uses the former; Cat uses the latter.

> >> One beauty of concatenative notation is the hiding of information
> >> such as described here,

> > Nothing's hidden.

> >> but we all know that hidden info must be dealt with
> >> eventually,
> >> unless the language is so primitive as to not care about these
> >> matters.

> > But this is true. (Well, mostly -- Cat does type inferencing so you
> > don't always have to describe the types, while Enchilada defines its
> > semantics so that all its types are compatible.)

> Afraid I'm confused here. You say nothing is hidden, yet you agree (at
> least partially) with my next statement.

That's because concatenative languages, in general, don't hide
information; but I agree that if you do hide information, you have to
pay the price. I was also illustrating that the price to be paid
wasn't what you thought it was -- Cat pays the price not by being more
primitive, but by having a sophisticated static type inference system
(which can, of course, slow some things down, but is undeniably NOT
primitive and is a desirable feature for many purposes).

> Don Groves

-Billy

Christopher Diggins — 2007-09-08 18:08:29

> That's because concatenative languages, in general, don't hide
> information; but I agree that if you do hide information, you have to
> pay the price. I was also illustrating that the price to be paid
> wasn't what you thought it was -- Cat pays the price not by being more
> primitive, but by having a sophisticated static type inference system
> (which can, of course, slow some things down, but is undeniably NOT
> primitive and is a desirable feature for many purposes).

A static type inference system certainly slows down compilation, but
all things given equal should speed up runtime execution because it
removes the need for runtime checks. Furthermore a static type
inference system makes it easier to optimize code, because you can
take advantage of properties that you have elucidated about the
runtime behaviour. However, every implementation is different.

A simple static type system has the disadvantage that typed terms will
disallow unbalanced stacks. You can't say things like:

if_tuesday [1] [1 2] ifte // does stack have one or two elements?

- Christopher

William Tanksley, Jr — 2007-09-08 18:24:30

Christopher Diggins <cdiggins@...> wrote:
> A simple static type system has the disadvantage that typed terms will
> disallow unbalanced stacks. You can't say things like:
> if_tuesday [1] [1 2] ifte // does stack have one or two elements?

Correct. I hadn't intended to imply that static typing was bad; I was
just conceding his point that there are costs (no free lunch).

> - Christopher

-Billy

Don Groves — 2007-09-08 19:36:46

On Sep 8, 2007, at 19:55 , Christopher Diggins wrote:

> On 9/8/07, Don Groves <dgroves@...> wrote:
>>
>> On Sep 7, 2007, at 19:03 , William Tanksley, Jr wrote:
>>
>>> Don Groves <dgroves@...> wrote:
>>>
>>>> The price we pay for the
>>>> simplicity of
>>>> concatenative syntax is the amount of parameter checking we must
>>>> do at
>>>> run-time which other languages to at compile-time.
>>>
>>> Not true. Concatenative semantics don't require parameter
>>> passing, so
>>> there is nothing to do to parameters at runtime. There are, of
>>> course,
>>> other prices we must pay; but that's not one of them.
>>
>> While it's true there's nothing to do "to" parameters, we still must
>> check for
>> their existence, yes? If you want to trap stack underflow before it
>> happens,
>> for example. And what do we do when a user enters a string and a
>> number
>> then types "/"?
>
> This is where a static type system with type inference comes in
> useful. You can do all of this without annotation or runtime checks in
> the compiler. See Cat (http://www.cat-language.com).
>
> - Christopher

Thanks, I'll study what you've done with Cat.
--
Don

Don Groves — 2007-09-08 19:41:32

On Sep 8, 2007, at 19:18 , William Tanksley, Jr wrote:

> Don Groves <dgroves@...> wrote:
>> William Tanksley, Jr wrote:
>>> Don Groves <dgroves@...> wrote:
>>>> The price we pay for the
>>>> simplicity of
>>>> concatenative syntax is the amount of parameter checking we must
>>>> do at run-time which other languages to at compile-time.
>
>>> Not true. Concatenative semantics don't require parameter
>>> passing, so
>>> there is nothing to do to parameters at runtime. There are, of
>>> course,
>>> other prices we must pay; but that's not one of them.
>
>> While it's true there's nothing to do "to" parameters, we still must
>> check for
>> their existence, yes? If you want to trap stack underflow before it
>> happens,
>> for example. And what do we do when a user enters a string and a
>> number
>> then types "/"?
>
> If you want to stop it before it happens, you're going to do static
> analysis. That can be done in a concatenative language using a
> linear-time algorithm plus some annotations (and provide typechecking
> as a bonus), or it can be done using type inference (using the same
> algorithms that many modern functional languages use). StrongForth
> uses the former; Cat uses the latter

Just before falling asleep last night, it hit me what has caused my
confusion
about all this: I've been thinking of the interactive user, not
running an already
compiled program. Of course, when a program has been fed through the
interpreter without errors, stack- and type checking is no longer
necessary.
Thanks for you help!


>>>> One beauty of concatenative notation is the hiding of information
>>>> such as described here,
>
>>> Nothing's hidden.
>
>>>> but we all know that hidden info must be dealt with
>>>> eventually,
>>>> unless the language is so primitive as to not care about these
>>>> matters.
>
>>> But this is true. (Well, mostly -- Cat does type inferencing so you
>>> don't always have to describe the types, while Enchilada defines its
>>> semantics so that all its types are compatible.)
>
>> Afraid I'm confused here. You say nothing is hidden, yet you agree
>> (at
>> least partially) with my next statement.
>
> That's because concatenative languages, in general, don't hide
> information; but I agree that if you do hide information, you have to
> pay the price. I was also illustrating that the price to be paid
> wasn't what you thought it was -- Cat pays the price not by being more
> primitive, but by having a sophisticated static type inference system
> (which can, of course, slow some things down, but is undeniably NOT
> primitive and is a desirable feature for many purposes).

Agreed!
--
Don

Don Groves — 2007-09-08 19:52:55

On Sep 8, 2007, at 19:08 , Christopher Diggins wrote:

>> That's because concatenative languages, in general, don't hide
>> information; but I agree that if you do hide information, you have to
>> pay the price. I was also illustrating that the price to be paid
>> wasn't what you thought it was -- Cat pays the price not by being
>> more
>> primitive, but by having a sophisticated static type inference system
>> (which can, of course, slow some things down, but is undeniably NOT
>> primitive and is a desirable feature for many purposes).
>
> A static type inference system certainly slows down compilation, but
> all things given equal should speed up runtime execution because it
> removes the need for runtime checks. Furthermore a static type
> inference system makes it easier to optimize code, because you can
> take advantage of properties that you have elucidated about the
> runtime behaviour. However, every implementation is different.
>
> A simple static type system has the disadvantage that typed terms will
> disallow unbalanced stacks. You can't say things like:
>
> if_tuesday [1] [1 2] ifte // does stack have one or two elements?
>
> - Christopher

Clearly, I need to learn more about type systems. Here's the Catenate
code implementing ifte, what kind of type system does this represent?


int ifte_() { // [f1] [f2] T|F ifte => (results of f1 if T)
// => (results of f2 if F)
if (depth < 3) {
StackError("ifte", "underflow");
return OK;
}
if (!islist(Y)) {
printf("\nifte: Y must be a list\n");
return UNKNOWN;
}
if (!islist(Z)) {
printf("\nifte: Z must be a list\n");
return UNKNOWN;
}
if (X == TRUE) { // T|F = true?
drop_(); drop_(); // (y) drop T and 'else' part (f2)
id_(); // execute 'then' part
}
else { // (n) false
drop_(); swap_(); drop_(); // drop F and 'then' part (f1)
id_(); // execute 'else' part
}
return OK;
}
--
Don

Christopher Diggins — 2007-09-08 20:42:12

On 9/8/07, Don Groves <dgroves@...> wrote:
> On Sep 8, 2007, at 19:08 , Christopher Diggins wrote:
>
> >> That's because concatenative languages, in general, don't hide
> >> information; but I agree that if you do hide information, you have to
> >> pay the price. I was also illustrating that the price to be paid
> >> wasn't what you thought it was -- Cat pays the price not by being
> >> more
> >> primitive, but by having a sophisticated static type inference system
> >> (which can, of course, slow some things down, but is undeniably NOT
> >> primitive and is a desirable feature for many purposes).
> >
> > A static type inference system certainly slows down compilation, but
> > all things given equal should speed up runtime execution because it
> > removes the need for runtime checks. Furthermore a static type
> > inference system makes it easier to optimize code, because you can
> > take advantage of properties that you have elucidated about the
> > runtime behaviour. However, every implementation is different.
> >
> > A simple static type system has the disadvantage that typed terms will
> > disallow unbalanced stacks. You can't say things like:
> >
> > if_tuesday [1] [1 2] ifte // does stack have one or two elements?
> >
> > - Christopher
>
> Clearly, I need to learn more about type systems. Here's the Catenate
> code implementing ifte, what kind of type system does this represent?
>
> int ifte_() { // [f1] [f2] T|F ifte => (results of f1 if T)
> // => (results of f2 if F)
> if (depth < 3) {
> StackError("ifte", "underflow");
> return OK;
> }
> if (!islist(Y)) {
> printf("\nifte: Y must be a list\n");
> return UNKNOWN;
> }
> if (!islist(Z)) {
> printf("\nifte: Z must be a list\n");
> return UNKNOWN;
> }
> if (X == TRUE) { // T|F = true?
> drop_(); drop_(); // (y) drop T and 'else' part (f2)
> id_(); // execute 'then' part
> }
> else { // (n) false
> drop_(); swap_(); drop_(); // drop F and 'then' part (f1)
> id_(); // execute 'else' part
> }
> return OK;
> }
> --
> Don

This is an example of a runtime checked language, also called a
dynamically typed language, though some theorists object to the term
dynamic typing. Personally I have no qualms with it.

-D

Christopher Diggins — 2007-09-08 20:44:46

> Just before falling asleep last night, it hit me what has caused my
> confusion
> about all this: I've been thinking of the interactive user, not
> running an already
> compiled program. Of course, when a program has been fed through the
> interpreter without errors, stack- and type checking is no longer
> necessary.
> Thanks for you help!

An interpreter can still be statically typed. Each expression fed into
the interpreter can be type-checked before it is run, and compared
with the current stack configuration. AFAIK this is a property
particular to languages with compositional type systems (e.g.
statically typed concatenative languages!)

-D

Don Groves — 2007-09-09 01:47:20

On Sep 8, 2007, at 19:44 , Christopher Diggins wrote:

>> Just before falling asleep last night, it hit me what has caused my
>> confusion
>> about all this: I've been thinking of the interactive user, not
>> running an already
>> compiled program. Of course, when a program has been fed through the
>> interpreter without errors, stack- and type checking is no longer
>> necessary.
>> Thanks for you help!
>
> An interpreter can still be statically typed. Each expression fed into
> the interpreter can be type-checked before it is run, and compared
> with the current stack configuration. AFAIK this is a property
> particular to languages with compositional type systems (e.g.
> statically typed concatenative languages!)
>
> -D


Thanks for the info. In what ways does Cat's inferred type system
differ?
--
Don

Don Groves — 2007-09-09 01:52:02

On Sep 8, 2007, at 19:42 , Christopher Diggins wrote:

> On 9/8/07, Don Groves <dgroves@...> wrote:
>> On Sep 8, 2007, at 19:08 , Christopher Diggins wrote:
>>
>>>> That's because concatenative languages, in general, don't hide
>>>> information; but I agree that if you do hide information, you
>>>> have to
>>>> pay the price. I was also illustrating that the price to be paid
>>>> wasn't what you thought it was -- Cat pays the price not by being
>>>> more
>>>> primitive, but by having a sophisticated static type inference
>>>> system
>>>> (which can, of course, slow some things down, but is undeniably NOT
>>>> primitive and is a desirable feature for many purposes).
>>>
>>> A static type inference system certainly slows down compilation, but
>>> all things given equal should speed up runtime execution because it
>>> removes the need for runtime checks. Furthermore a static type
>>> inference system makes it easier to optimize code, because you can
>>> take advantage of properties that you have elucidated about the
>>> runtime behaviour. However, every implementation is different.
>>>
>>> A simple static type system has the disadvantage that typed terms
>>> will
>>> disallow unbalanced stacks. You can't say things like:
>>>
>>> if_tuesday [1] [1 2] ifte // does stack have one or two elements?
>>>
>>> - Christopher
>>
>> Clearly, I need to learn more about type systems. Here's the Catenate
>> code implementing ifte, what kind of type system does this represent?
>>
>> int ifte_() { // [f1] [f2] T|F ifte => (results of f1 if T)
>> // => (results of f2 if F)
>> if (depth < 3) {
>> StackError("ifte", "underflow");
>> return OK;
>> }
>> if (!islist(Y)) {
>> printf("\nifte: Y must be a list\n");
>> return UNKNOWN;
>> }
>> if (!islist(Z)) {
>> printf("\nifte: Z must be a list\n");
>> return UNKNOWN;
>> }
>> if (X == TRUE) { // T|F = true?
>> drop_(); drop_(); // (y) drop T and 'else' part (f2)
>> id_(); // execute 'then' part
>> }
>> else { // (n) false
>> drop_(); swap_(); drop_(); // drop F and 'then' part (f1)
>> id_(); // execute 'else' part
>> }
>> return OK;
>> }
>> --
>> Don
>
> This is an example of a runtime checked language, also called a
> dynamically typed language, though some theorists object to the term
> dynamic typing. Personally I have no qualms with it.
>
> -D


OK, that's what I thought it was. It seems dynamic typing means two
things: (1) run-time type checking; and (2) data is typed --
variables are
not. Clearly (1) allows (2) but (2) is not required. Is this part of
the source
of disagreement over the name?
--
Don

pml060912 — 2007-09-09 06:25:44

--- In concatenative@yahoogroups.com, Manfred Von Thun
<m.vonthun@...> wrote:
>
> For a long time I have been interested in the difference
> between the standard kind of lambda calculus languages
> and the concatenative languages. Only recently did it
> occur to me that there is a third class which has been around
> for some time. The three differ in how they treat actual
> and formal parameters in definitions of functions and in
> calls of functions. At one extreme are the concatenative
> languages, at the other extreme those in the third class.
> The standard lambda calculus languages are half way
> between these two extremes.
.
.
.
At the other extreme there should be languages
> which do not use order at all but rely on names both
> in the body and in calls. Are there such beasts?
>
> In such languages definitions would again look as in
> lambda calculus languages:
>
> DEFINE foo(x,y,z) # head of definition
> = ...x...y...z... # body of definition
>
> As before, you can change the order of the parameters
> without any need to change the body of the definition.
> But a call would look quite different: instead of relying
> on the order of the formal parameters, use their names
> as assignment statements, and make the assignments
> in any order. Hence any of the following would be OK
> as a call:
>
> foo(x=a; y=b; z=c)
> foo(y=b; z=c; x=a)
> foo(z=c; y=b; x=a)
>
> and so forth, a total of six possible combinations. All such
> calls would be equivalent. To summarise: body and call
> are done by using the names of the formal parameters.
> My understanding is that there are some languages which
> allow this kind of notation (Common Lisp ?, Ada ?) but
> I cannot remember any details --- anyone?? But as far
> as I know these languages use standard positional notation
> by default and allow this other notation only as a
> convenient variation. I have never heard of a language
> in which this way of calling is standard. Anyone??

That looks like something I came across in P.J.Brown's "Writing
Interactive Compilers and Interpreters", although I forget if he
stated it explicitly or just implied it. In essence, he suggested
handling parameters and local variables by a "don't care" method, in
which they were really global variables like any other, only their
values were stacked/reinitialised and then restored on call/return.
Then the function call construct is little more than a subroutine
that can return a value, and foo(x=a; y=b; z=c) is a macro for the
pushing and popping of global variables x, y and z on either side of
gosub foo. The function needs no special declaration, either.

I actually looked into this approach for its smaller
implementation "footprint" in two areas:-

- extending awkward languages like Cobol so that they could
comfortably support their own compilers (the released version would
have had the extensions disabled); and

- making user defined functions easy to implement in a spreadsheet.

The latter relates to my musings on where to go next in paper
exploring for Furphy, since a spreadsheet would make a comfortable
programming interface for a functional programming language. PML.

Christopher Diggins — 2007-09-09 14:12:18

On 9/8/07, Don Groves <dgroves@...> wrote:
> On Sep 8, 2007, at 19:44 , Christopher Diggins wrote:
>
> >> Just before falling asleep last night, it hit me what has caused my
> >> confusion
> >> about all this: I've been thinking of the interactive user, not
> >> running an already
> >> compiled program. Of course, when a program has been fed through the
> >> interpreter without errors, stack- and type checking is no longer
> >> necessary.
> >> Thanks for you help!
> >
> > An interpreter can still be statically typed. Each expression fed into
> > the interpreter can be type-checked before it is run, and compared
> > with the current stack configuration. AFAIK this is a property
> > particular to languages with compositional type systems (e.g.
> > statically typed concatenative languages!)
> >
> > -D
>
> Thanks for the info. In what ways does Cat's inferred type system
> differ?
> --

From what?

-Christopher

Christopher Diggins — 2007-09-09 14:23:19

> > This is an example of a runtime checked language, also called a
> > dynamically typed language, though some theorists object to the term
> > dynamic typing. Personally I have no qualms with it.
> >
> > -D
>
> OK, that's what I thought it was. It seems dynamic typing means two
> things: (1) run-time type checking; and (2) data is typed --
> variables are
> not. Clearly (1) allows (2) but (2) is not required.
> Is this part of
> the source
> of disagreement over the name?

I shouldn't speculate too much. I think the disagreement is
artificial. There were some threads on
http://www.Lambda-the-Ultimate.org related to the issue, but in
searching for them, I see many people happily talking about dyanmic
typing, without complaining about the term.

Dynamic typing invariably means: runtime checking of tags associated
with values. All values that are runtime checked would have to have
tags identifying their type. Of course, you can mix static typing with
dynamic typing if you want.

I'm sorry if I have confused the issues for you.

- Christopher

Don Groves — 2007-09-09 19:26:51

On Sep 9, 2007, at 19:12 , Christopher Diggins wrote:

> On 9/8/07, Don Groves <dgroves@...> wrote:
>> On Sep 8, 2007, at 19:44 , Christopher Diggins wrote:
>>
>>>> Just before falling asleep last night, it hit me what has caused my
>>>> confusion
>>>> about all this: I've been thinking of the interactive user, not
>>>> running an already
>>>> compiled program. Of course, when a program has been fed through
>>>> the
>>>> interpreter without errors, stack- and type checking is no longer
>>>> necessary.
>>>> Thanks for you help!
>>>
>>> An interpreter can still be statically typed. Each expression fed
>>> into
>>> the interpreter can be type-checked before it is run, and compared
>>> with the current stack configuration. AFAIK this is a property
>>> particular to languages with compositional type systems (e.g.
>>> statically typed concatenative languages!)
>>>
>>> -D
>>
>> Thanks for the info. In what ways does Cat's inferred type system
>> differ?
>> --
>
> From what?
>
> -Christopher

From an "uninferred" one. I'm thinking a large part of my problem is
lack of vocabulary. I tried to find a definition of an "inferred" type
system via Google but was unsuccessful.
--
Don

Don Groves — 2007-09-09 19:29:53

On Sep 9, 2007, at 19:23 , Christopher Diggins wrote:

>>> This is an example of a runtime checked language, also called a
>>> dynamically typed language, though some theorists object to the term
>>> dynamic typing. Personally I have no qualms with it.
>>>
>>> -D
>>
>> OK, that's what I thought it was. It seems dynamic typing means two
>> things: (1) run-time type checking; and (2) data is typed --
>> variables are
>> not. Clearly (1) allows (2) but (2) is not required.
>> Is this part of
>> the source
>> of disagreement over the name?
>
> I shouldn't speculate too much. I think the disagreement is
> artificial. There were some threads on
> http://www.Lambda-the-Ultimate.org related to the issue, but in
> searching for them, I see many people happily talking about dyanmic
> typing, without complaining about the term.
>
> Dynamic typing invariably means: runtime checking of tags associated
> with values. All values that are runtime checked would have to have
> tags identifying their type. Of course, you can mix static typing with
> dynamic typing if you want.
>
> I'm sorry if I have confused the issues for you.
>
> - Christopher

No, my confusion is entirely my own and things are becoming clearer.
--
Don

Christopher Diggins — 2007-09-09 21:06:45

On 9/9/07, Don Groves <dgroves@...> wrote:
> >> Thanks for the info. In what ways does Cat's inferred type system
> >> differ?
> >> --
> >
> > From what?
> >
> > -Christopher
>
> From an "uninferred" one. I'm thinking a large part of my problem is
> lack of vocabulary. I tried to find a definition of an "inferred" type
> system via Google but was unsuccessful.

Some examples of languages with type inference are ML, OCaML, Haskell,
F#, Scala, Boo, Nice, Cyclone, and of course Cat.

Type inference (also known as type reconstruction) is not a property
of the type system but rather a property of a language. Two languages
can share a type system, whereas one requires type annotations (a.k.a
type signatures) wheras another doesn't. Some type systems lend
themselves to type inference (e.g. ML style type systems). Type
inference is often achieved using what is known as the Hindley-Milner
algorithm.

Your most successful search keywords are going to be "type inference".

Hope this helps,
Christopher

John Cowan — 2007-09-10 00:58:41

pml060912 scripsit:

> That looks like something I came across in P.J.Brown's "Writing
> Interactive Compilers and Interpreters", although I forget if he
> stated it explicitly or just implied it. In essence, he suggested
> handling parameters and local variables by a "don't care" method, in
> which they were really global variables like any other, only their
> values were stacked/reinitialised and then restored on call/return.

That's dynamic scoping (not to be confused with dynamic *typing*) and
it is a Really Really Bad Idea, because reasoning about the behavior
of programs becomes insanely difficult. You wind up not being able to
know where the value of a variable is set unless you know the exact
path by which it was called, and no procedure can guarantee that the
values of its parameters are safe from tampering unless it never calls
any other procedure. What's more, that particular *implementation*
of dynamic scoping (known as "shallow binding") only works in a single
thread of control; if there are multiple threads, it goes horribly wrong
in all sorts of ways.

Lexical scoping, in which variables only exist within the particular
portion of the program that declares them, is a far superior regime.
There are occasional uses for dynamic scoping here and there, and
some languages support it as an alternative to lexical scoping for
restricted use (Perl and Common Lisp, notably; also because of backward
compatibility). But it's hard to believe that any modern work on
interpreters would recommend it.

--
"Well, I'm back." --Sam John Cowan <cowan@...>

pml060912 — 2007-09-10 01:28:49

--- In concatenative@yahoogroups.com, John Cowan <cowan@...> wrote:
>
> pml060912 scripsit:
>
> > That looks like something I came across in P.J.Brown's "Writing
> > Interactive Compilers and Interpreters", although I forget if he
> > stated it explicitly or just implied it. In essence, he suggested
> > handling parameters and local variables by a "don't care" method,
in
> > which they were really global variables like any other, only
their
> > values were stacked/reinitialised and then restored on
call/return.
>
> That's dynamic scoping (not to be confused with dynamic *typing*)
and
> it is a Really Really Bad Idea, because reasoning about the behavior
> of programs becomes insanely difficult. You wind up not being able
to
> know where the value of a variable is set unless you know the exact
> path by which it was called, and no procedure can guarantee that the
> values of its parameters are safe from tampering unless it never
calls
> any other procedure.

That depends on what else is available to change them. As far as I
can see, it's less risky in spreadsheets than in other languages
since - depending on what base you have for building the formulas -
formula execution needn't change the cells you allocate for that use
(unless you foolishly use the same cell for one parameter as for the
formula using the parameter!). But as I said, at this point I'm just
mulling over ideas.

What's more, that particular *implementation*
> of dynamic scoping (known as "shallow binding") only works in a
single
> thread of control; if there are multiple threads, it goes horribly
wrong
> in all sorts of ways.
>
> Lexical scoping, in which variables only exist within the particular
> portion of the program that declares them, is a far superior regime.
> There are occasional uses for dynamic scoping here and there, and
> some languages support it as an alternative to lexical scoping for
> restricted use (Perl and Common Lisp, notably; also because of
backward
> compatibility). But it's hard to believe that any modern work on
> interpreters would recommend it.

P.J.Brown wrote the first edition in 1979, emphasising
implementation. He wasn't recommending this technique for the
language design, but as an implementation technique for passing
parameters to subroutines without having to implement the subroutine
bodies specially. You would constrain the language in other ways to
avoid some of these problems, for instance having an extra level of
indirection in descriptors would allow it to act more like parameter
handling in C. Brown also suggested having variables associated with
subroutines and only allowing access to other variables by giving
them what amounts to pathnames. On checking, I find that Brown only
recommended this technique for subroutines, not functions, and he
didn't specify the foo(x=a, y-b, z=c) notation for calling.

I realised that it wasn't the best way to extend Cobol (say) to make
it convenient for compiling itself, but it would have been a
realistic approach if the extended version was only used in house
with great care and not released. PML.

Don Groves — 2007-09-10 07:48:19

On Sep 9, 2007, at 19:06 , Christopher Diggins wrote:

> On 9/9/07, Don Groves <dgroves@...> wrote:
>>>> Thanks for the info. In what ways does Cat's inferred type system
>>>> differ?
>>>> --
>>>
>>> From what?
>>>
>>> -Christopher
>>
>> From an "uninferred" one. I'm thinking a large part of my problem is
>> lack of vocabulary. I tried to find a definition of an "inferred"
>> type
>> system via Google but was unsuccessful.
>
> Some examples of languages with type inference are ML, OCaML, Haskell,
> F#, Scala, Boo, Nice, Cyclone, and of course Cat.
>
> Type inference (also known as type reconstruction) is not a property
> of the type system but rather a property of a language. Two languages
> can share a type system, whereas one requires type annotations (a.k.a
> type signatures) wheras another doesn't. Some type systems lend
> themselves to type inference (e.g. ML style type systems). Type
> inference is often achieved using what is known as the Hindley-Milner
> algorithm.
>
> Your most successful search keywords are going to be "type inference".
>
> Hope this helps,
> Christopher

Yes, it helps a lot! Having fried my brain for the last two hours
reading
up on type inference, one thing stands out for the development of
Catenate:
if I infer the type of a variable from its initialization literal,
and restrict that
variable from ever holding a different type, then I can disable type
checking
for compiled programs. I'm not thinking much about execution speed yet,
but doing this should speed up things considerably down the road.
--
Don

Programmer (n): an organism that turns coffee and cookies into software.

Robbert van Dalen — 2007-09-07 09:03:23

Hi Manfred,

Interesting post as usual.

You may want to check: http://blog.moertel.com/articles/2006/01/20/
wondrous-oddities-rs-function-call-semantics
Function definitions in the R language follow your classification #3:
bodies and calls rely on names.

- Robbert

Manfred Von Thun — 2007-10-02 05:35:41

Thanks Robbert. Very interesting indeed. There is another feature
which I have not seen before (and which has nothing to do with
ordered/named parameters): one and the same procedure can
use Cartesian or polar coordinates, and their interrelation is
expressed in the list of (named) parameters. Very clever.

- Manfred

On 7/9/07 7:03 PM, "Robbert van Dalen" <robbert.van.dalen@...> wrote:

> Hi Manfred,
>
> Interesting post as usual.
>
> You may want to check: http://blog.moertel.com/articles/2006/01/20/
> wondrous-oddities-rs-function-call-semantics
> Function definitions in the R language follow your classification #3:
> bodies and calls rely on names.
>
> - Robbert
>
>



[Non-text portions of this message have been removed]