Barebone implementation of concatenative language in c or c++

blazeski — 2010-05-11 22:50:49

Could someone recommend a minimalistic implementation of a concatenative language written in c or c++?

thanks
Slobodan

William Tanksley, Jr — 2010-05-11 23:16:15

blazeski <blaseski@...> wrote:

> Could someone recommend a minimalistic implementation of a concatenative
> language written in c or c++?
>

Isn't Joy written in C? (I don't recall.)

There are plenty of Forths... Is that sufficient? If so, you might possibly
want to look at FICL, which is actually widely used.

I've never heard of 'libokit' before, but it's apparently a library for
writing concatenative languages which have support for automatic memory
management. Perhaps it could help (it comes with a demo language)?

Slobodan
>

-Wm


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

John Cowan — 2010-05-11 23:49:46

William Tanksley, Jr scripsit:

> Isn't Joy written in C? (I don't recall.)

Yes, it is. It depends only on ANSI C features. I also have about 60%
of an implementation written in Chicken Scheme, which compiles down to C.

--
How they ever reached any conclusion at all <cowan@...>
is starkly unknowable to the human mind. http://www.ccil.org/~cowan
--"Backstage Lensman", Randall Garrett

blazeski — 2010-05-12 09:52:12

Thanks for the tips will take a look at them.
I'm writing some c++ code and I want to include a minimalistic scheme for scripting (I don't need GC but I must support native types). Then I got a suggestion that concatenative language naive implementation might be even simpler. So I'm basically looking for a source code of a toy example.

Slobodan

William Tanksley, Jr — 2010-05-12 14:37:30

blazeski <blaseski@...> wrote:

> Thanks for the tips will take a look at them.
> I'm writing some c++ code and I want to include a minimalistic scheme for
> scripting (I don't need GC but I must support native types). Then I got a
> suggestion that concatenative language naive implementation might be even
> simpler. So I'm basically looking for a source code of a toy example.
>

Ah, yes; if you don't want/need GC you're likely well off to use a decent
embeddable Forth with an object system, and FICL is designed for and has
been extensively used for exactly that. If you needed/were-OK-with GC you'd
be fine with Scheme itself, or Lua. (I don't know of any concatenative/GC
language that's been designed to be embedded, which is something of a pity.)


> Slobodan


-Wm


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

John Cowan — 2010-05-12 17:13:26

William Tanksley, Jr scripsit:

> Ah, yes; if you don't want/need GC you're likely well off to use a decent
> embeddable Forth with an object system, and FICL is designed for and has
> been extensively used for exactly that. If you needed/were-OK-with GC you'd
> be fine with Scheme itself, or Lua. (I don't know of any concatenative/GC
> language that's been designed to be embedded, which is something of a pity.)

Let me put in a plug for Chibi Scheme, which is a full R5RS Scheme (with
GC, of course) that is designed for embedding. It's a small but fast
bytecode interpreter in the style of Lua, but less limited.
(Not that Lua doesn't have its own good points.)

--
I suggest you solicit aid of my followers John Cowan
or learn the difficult art of mud-breathing. cowan@...
--Great-Souled Sam http://www.ccil.org/~cowan

blazeski — 2010-05-12 18:23:27

I'm really grateful for suggested implementations, and I'm sure that those are useful even in production environment. However I'm looking for a toy implementation so I could quickly understand what happens under the hood of stack languages. Something designed for pedagogical purposes not for speed or efficiency. Something small enough that could be written over the weekend from scratch and contains just the bare minimum of the stack operations so it could be called interpreter.
Or if you can't think of something like that what are the bare minimum of operators so I could call a language concatenative:
For example from reading Brent Kerby Theory of concatenative combinators http://tunes.org/~iepos/joy.html if I implement : swap, dup, zap, unit, cat, cons, i, dip + (numbers and symbols) would that be enough to have a primitive joy like language?

Jeremy Cowgar — 2010-05-12 18:25:49

On 5/12/2010 2:23 PM, blazeski wrote:
>
> I'm really grateful for suggested implementations, and I'm sure that
> those are useful even in production environment. However I'm looking
> for a toy implementation so I could quickly understand what happens
> under the hood of stack languages.
>

http://4.flowsnake.org/archives/252

Jeremy



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

William Tanksley, Jr — 2010-05-12 19:02:47

The smallest possible number of required functions is 2... You need a data
pseudo-combinator and an execution combinator. But this isn't helpful :-). I
wrote a language that does that, and it's not in any way useful. If you want
to look, it's at http://bitbucket.org/wtanksleyjr/tworing, but be warned...
It's not pretty.

There are a lot of one-day Forth implementations out there, but most of them
don't try to have just a minimal number of words; that gets really difficult
without garbage collection.

minForth looks like it might be a consideration for you... But I'd still
look at FICL, which is really well-tested. Rainbow Forth is _really_ simple
(most of the Machine Forth derivatives are), but the need to trust machine
code and read assembly might be too much.

Hmm, someone did some research... Check out
http://groups.google.com/group/comp.lang.forth/msg/10872cb68edcb526?pli=1 for
some answers to the question (although it doesn't link to any source).

Most of what I see is designed to be minimal and usable, not to be simply
minimal.

-Wm

blazeski <blaseski@...> wrote:

> I'm really grateful for suggested implementations, and I'm sure that those
> are useful even in production environment. However I'm looking for a toy
> implementation so I could quickly understand what happens under the hood of
> stack languages. Something designed for pedagogical purposes not for speed
> or efficiency. Something small enough that could be written over the weekend
> from scratch and contains just the bare minimum of the stack operations so
> it could be called interpreter.
> Or if you can't think of something like that what are the bare minimum of
> operators so I could call a language concatenative:
> For example from reading Brent Kerby Theory of concatenative combinators
> http://tunes.org/~iepos/joy.html if I implement : swap, dup, zap, unit,
> cat, cons, i, dip + (numbers and symbols) would that be enough to have a
> primitive joy like language?
>
>
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>
>


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

john@johnnowak.com — 2010-05-12 19:28:26

On Wed, 12 May 2010 18:23:27 -0000, "blazeski" <blaseski@...> wrote:

> For example from reading Brent Kerby Theory of concatenative combinators
> http://tunes.org/~iepos/joy.html if I implement : swap, dup, zap, unit,
> cat, cons, i, dip + (numbers and symbols) would that be enough to have a
> primitive joy like language?

The "classical" base seems to be swap, dup, drop, dip, cons, uncons, null,
and if. These are easy to implement in any sort of extended lambda
calculus
by treating the stack as nested tuples (with ':' as cons and '->,' as
cond):

swap = \(a, (b, s)). (b, (a, s))
dup = \(a, s). (a, (a, s))
drop = \(a, s). s
dip = \(a, (b, s)). (b, a s)
null = \s. ([], s)
cons = \(a, (b, s)). (a:b, s)
uncons = \(a:b, s). (a, (b, s))
if = \(a, (b, (c, s))). a -> b s, c s

More minimal bases are possible, but the above set is the most direct.

- jn

Ruurd — 2010-05-18 11:16:40

If you want a minimal implementation:

http://github.com/jdp/concom

Or, if you want it to be like joy:

http://www.latrobe.edu.au/philosophy/phimvt/sympas/s42minjoy.html

concom does not have numbers; the small joy version has numbers, characters, booleans, symbols and it has more operators than concom.

Joy is built up from three concepts: concatenative notation, quotations and combinators. The structured programming paradigm is built from the concepts: sequencing, selection, iteration, subroutines, arrays. When comparing the two paradigms: concatenative notation is roughly the same as sequencing; selection and iteration are covered by combinators; subroutines and arrays by quotations.

When comparing Joy to other functional programming languages, it looks like Joy does not hide data, but makes it accessible on a stack. Other functional programming languages hide data in the parameters to their functions. Having data accessible means that it is possible to think about Joy is if it were an imperative programming language. But it is not, because it does not have assignment.

And it is not a stack language either. The order of evaluation is not specified. Take for instance a small example: 2 3 [+] i. This evaluates to 5, but I do not have to start the rewriting at the left.[+] i is equal to + and 2 3 + is equal to 5, so I can start at the right if I like.

> For example from reading Brent Kerby Theory of concatenative combinators http://tunes.org/~iepos/joy.html if I implement : swap, dup, zap, unit, cat, cons, i, dip + (numbers and symbols) would that be enough to have a primitive joy like language?
>

john@johnnowak.com — 2010-05-18 16:19:03

On Tue, 18 May 2010 11:16:40 -0000, "Ruurd" <r.wiersma26@...>
wrote:

> And it is not a stack language either. The order of evaluation is not
> specified. Take for instance a small example: 2 3 [+] i. This evaluates
to
> 5, but I do not have to start the rewriting at the left.[+] i is equal
to +
> and 2 3 + is equal to 5, so I can start at the right if I like.

A critique of ascribing this property to Joy if I may...

This view breaks down once your functions have side effects (which is the
case in Joy). Because if this, the arbitrary order reduction you're
talking
about needs to be subject to side constraints regarding purity.

The utility of arbitrary order rewriting in Joy is also severely limited
by
the fact that quotations are not opaque. For example, it is not valid to
rewrite [1 2 +] to [3] because the quotation may be treated as a list
later
on. It is for this reason that I consider Joy's approach to quotations to
be
broken; a separation of quotations from lists would solve the problem.

However, even if you do separate lists from quotations, you will end up
with
other concerns about arbitrary order reduction. For example, you may have
a
rule like '{A} drop == id' (where '{A}' denotes some list A), but that
rule
will not be valid in the presence of strict evaluation if you have partial
functions (as Joy does).

In short, you can say it isn't a stack language because it doesn't need to
be evaluated in that order, but in practice you likely want it to be, and
I believe the current Joy implementation takes this approach.

Finally, applicative languages arguably have an advantage over Joy in
terms
of arbitrary order reduction. For example, take this Haskell function:

bi f g x = (f x, g x)

And the rough Joy equivalent:

x [F] [G] bi = x F x G

The Haskell version has the advantage that we can easily reduce 'f x'
completely independent of 'g x'. The Joy version does not have this
advantage; in general, it will be necessary to reduce 'x F' before it is
safe to reduce 'x G' because 'G' may require more than one element on the
stack. Accordingly, the applicative approach has the advantage here in
terms
of order-independent reduction.

- jn

john@johnnowak.com — 2010-05-18 16:41:27

Sorry -- here's a copy slightly less mangled by my horrible webmail:

On Tue, 18 May 2010 11:16:40 -0000, "Ruurd" <r.wiersma26@...>
wrote:

> And it is not a stack language either. The order of
> evaluation is not specified. Take for instance a small
> example: 2 3 [+] i. This evaluates to 5, but I do not
> have to start the rewriting at the left.[+] i is equal
> to + and 2 3 + is equal to 5, so I can start at the
> right if I like.

A critique of ascribing this property to Joy if I may...

This view breaks down once your functions have side effects (which is the
case in Joy). Because if this, the arbitrary order reduction you're talking
about needs to be subject to side constraints regarding purity.

The utility of arbitrary order rewriting in Joy is also severely limited
by the fact that quotations are not opaque. For example, it is not valid to
rewrite [1 2 +] to [3] because the quotation may be treated as a list later
on. It is for this reason that I consider Joy's approach to quotations to
be broken; a separation of quotations from lists would solve the problem.

However, even if you do separate lists from quotations, you will end up
with other concerns about arbitrary order reduction. For example, you may
have a rule like '{A} drop == id' (where '{A}' denotes some list formed by
the list of expressions A), but that rule will not be valid in the presence
of strict evaluation if you have partial functions (as Joy does).

In short, you can say it isn't a stack language because it doesn't need to
be evaluated in a particular order, but in practice you likely want it to
be, and I believe the current Joy implementation takes this approach.

Finally, applicative languages arguably have an advantage over Joy in
terms of arbitrary order reduction. For example, take this Haskell
function:

bi f g x = (f x, g x)

And the common Joy equivalent:

x [F] [G] bi = x F x G

The Haskell version has the advantage that we can easily reduce 'f x'
completely independent of 'g x'. The Joy version does not have this
advantage; in general, it will be necessary to reduce 'x F' before it is
safe to reduce 'x G' because 'G' may require more than one element on the
stack. Accordingly, the applicative approach has the advantage here in
terms of order-independent reduction.

- jn

Ruurd — 2010-05-18 17:49:52

I'll respond to this one.

Side effects. Then usual culprits are I/O. Now suppose I have a program: get 3 [+] i. I can still proceed from right to left, except that when I am about to execute + I do not find two numbers, but a function and a number. So 'get' must be evaluated before + is evaluated. This means that when + finds something that is a function, it has to invoke the function before proceeding with the addition.

Quotations. If A, B, C, D, E are valid programs and B == D E, then A B C == A D E C and vice versa. That leaves the question: what is a valid program. The empty program is valid. A program that has a matching number of [ and ] is valid. When this rule is followed it will not be possible to substitute within a quotation, because then I would have to choose in your example: A == [, D == 1, E == 2 +, C == ], B == 3; A and C are not valid programs according to the definition.

In your example: x [F] [G] bi = x F x G it depends on the arity of G whether it can be executed first, doesn't it? If it does not need something on the stack left there by F, then it can be executed first.

The current implementation uses a stack, yes. But I was not talking about implementations, I was talking about the language and the way that I, as a person, can evaluate expressions in whatever order I like. If I can do that, mentally or on paper, it is a property of Joy, the language. Manfred also thinks that it should be possible to build Joy, the implementation, as a rewriting system: http://www.latrobe.edu.au/philosophy/phimvt/joy/j07rrs.html, so who am I to question that?

--- In concatenative@yahoogroups.com, <john@...> wrote:
>
> Sorry -- here's a copy slightly less mangled by my horrible webmail:
>
> On Tue, 18 May 2010 11:16:40 -0000, "Ruurd" <r.wiersma26@...>
> wrote:
>
> > And it is not a stack language either. The order of
> > evaluation is not specified. Take for instance a small
> > example: 2 3 [+] i. This evaluates to 5, but I do not
> > have to start the rewriting at the left.[+] i is equal
> > to + and 2 3 + is equal to 5, so I can start at the
> > right if I like.
>
> A critique of ascribing this property to Joy if I may...
>
> This view breaks down once your functions have side effects (which is the
> case in Joy). Because if this, the arbitrary order reduction you're talking
> about needs to be subject to side constraints regarding purity.
>
> The utility of arbitrary order rewriting in Joy is also severely limited
> by the fact that quotations are not opaque. For example, it is not valid to
> rewrite [1 2 +] to [3] because the quotation may be treated as a list later
> on. It is for this reason that I consider Joy's approach to quotations to
> be broken; a separation of quotations from lists would solve the problem.
>
> However, even if you do separate lists from quotations, you will end up
> with other concerns about arbitrary order reduction. For example, you may
> have a rule like '{A} drop == id' (where '{A}' denotes some list formed by
> the list of expressions A), but that rule will not be valid in the presence
> of strict evaluation if you have partial functions (as Joy does).
>
> In short, you can say it isn't a stack language because it doesn't need to
> be evaluated in a particular order, but in practice you likely want it to
> be, and I believe the current Joy implementation takes this approach.
>
> Finally, applicative languages arguably have an advantage over Joy in
> terms of arbitrary order reduction. For example, take this Haskell
> function:
>
> bi f g x = (f x, g x)
>
> And the common Joy equivalent:
>
> x [F] [G] bi = x F x G
>
> The Haskell version has the advantage that we can easily reduce 'f x'
> completely independent of 'g x'. The Joy version does not have this
> advantage; in general, it will be necessary to reduce 'x F' before it is
> safe to reduce 'x G' because 'G' may require more than one element on the
> stack. Accordingly, the applicative approach has the advantage here in
> terms of order-independent reduction.
>
> - jn
>

John Cowan — 2010-05-18 18:42:16

john@... scripsit:

> This view breaks down once your functions have side effects (which is the
> case in Joy). Because if this, the arbitrary order reduction you're talking
> about needs to be subject to side constraints regarding purity.

Quite so. Joy, like ML or Pure, is an eager impure functional programming
language. Consequently, arbitrary reordering is not permitted.

> The utility of arbitrary order rewriting in Joy is also severely limited
> by the fact that quotations are not opaque. For example, it is not valid to
> rewrite [1 2 +] to [3] because the quotation may be treated as a list later
> on. It is for this reason that I consider Joy's approach to quotations to
> be broken; a separation of quotations from lists would solve the problem.

If you consider it a problem. I consider it one of Joy's special beauties:
the abstraction for a sequence of datums is the same as the abstraction for
a non-primitive function, which is just a sequence of functions.

> Finally, applicative languages arguably have an advantage over Joy in
> terms of arbitrary order reduction. For example, take this Haskell
> function:

I don't think your example has anything to do with applicativeness, but
with purity and laziness. A pure lazy concatenative language would have
the same strengths (and weaknesses) as Haskell.

--
Kill Gorgun! Kill orc-folk! John Cowan
No other words please Wild Men. cowan@...
Drive away bad air and darkness http://www.ccil.org/~cowan
with bright iron! --Ghan-buri-Ghan http://www.ccil.org/~cowan

John Cowan — 2010-05-18 18:45:49

Ruurd scripsit:

> Side effects. Then usual culprits are I/O. Now suppose I have a program:
> get 3 [+] i. I can still proceed from right to left, except that when
> I am about to execute + I do not find two numbers, but a function and
> a number. So 'get' must be evaluated before + is evaluated. This means
> that when + finds something that is a function, it has to invoke the
> function before proceeding with the addition.

This amounts to creating a lazy variety of Joy, a perfectly good idea,
but not equivalent to standard Joy, any more than Haskell is ML.

> The current implementation uses a stack, yes. But I was not talking
> about implementations, I was talking about the language and the
> way that I, as a person, can evaluate expressions in whatever
> order I like. If I can do that, mentally or on paper, it is a
> property of Joy, the language.

Only it's not.

> Manfred also thinks that it should be possible to
> build Joy, the implementation, as a rewriting system:
> http://www.latrobe.edu.au/philosophy/phimvt/joy/j07rrs.html, so who
> am I to question that?

Sure, but it would have to be an eager impure rewriting system. Which is
what Pure is.

--
Verbogeny is one of the pleasurettes John Cowan <cowan@...>
of a creatific thinkerizer. http://www.ccil.org/~cowan
--Peter da Silva

Ruurd — 2010-05-18 19:53:32

We seem to disagree about what a language is. In my dictionary a language consists of: pronunciation, syntax, semantics. It seems to me that stack or rewriting is part of the semantics and therefore part of the language. If I have a simple expression like: 2 3 +, there is no way for you to convince me that this must be executed on a stack. I only see three symbols that in the next evaluation step are replaced by one symbol.

Making a lazy Joy is quite unnecessary, because there is already a lazy library: http://www.latrobe.edu.au/philosophy/phimvt/joy/jp-reprod.html

--- In concatenative@yahoogroups.com, John Cowan <cowan@...> wrote:
> > The current implementation uses a stack, yes. But I was not talking
> > about implementations, I was talking about the language and the
> > way that I, as a person, can evaluate expressions in whatever
> > order I like. If I can do that, mentally or on paper, it is a
> > property of Joy, the language.
>
> Only it's not.

John Nowak — 2010-05-18 23:03:49

On 05/18/2010 01:49 PM, Ruurd wrote:
>
> I'll respond to this one.
>
> Side effects. Then usual culprits are I/O.
> Now suppose I have a program: get 3 [+] i.
> I can still proceed from right to left,
> except that when I am about to execute + I
> do not find two numbers, but a function
> and a number. So 'get' must be evaluated
> before + is evaluated. This means that
> when + finds something that is a function,
> it has to invoke the function before
> proceeding with the addition.

But that's not all. If 'f' and 'g' both take one argument on the stack
and I have the program 'x f y g' where x and y are values, I can only
evaluate 'g' before 'f' if I know 'f' is pure and total. This is not
really an interesting property of Joy at all. Any functional language
allows arbitrary order reduction such cases (e.g. '(f x, g y)').

> Quotations. If A, B, C, D, E are valid
> programs and B == D E, then A B C ==
> A D E C and vice versa. That leaves the
> question: what is a valid program. The
> empty program is valid. A program that
> has a matching number of [ and ] is valid.

That's actually not true. For example, '+' is not a valid program. It's
a valid expression, but not every expression is a valid program.

> When this rule is followed it will not
> be possible to substitute within a
> quotation, because then I would have to
> choose in your example: A == [, D == 1,
> E == 2 +, C == ], B == 3; A and C are
> not valid programs according to the
> definition.

I think you've misunderstood me. My point is that, for example:

1 2 + == 3
[1 2 +] /= [3] (because [1 2 +] head /= [3] head)

This is because Joy's quotations are not opaque. In languages that offer
abstractions (typically via lambda), you don't have this problem as it
is safe to simplify within abstractions. Not offering abstractions and
providing no way to implement them is an objectively bad thing. I see
nothing good about this aspect of Joy.

> In your example: x [F] [G] bi = x F x G
> it depends on the arity of G whether it
> can be executed first, doesn't it?

Yes, and that's exactly the problem. After all, determining the arity of
G is, in general, undecidable!

> The current implementation uses a stack,
> yes. But I was not talking about
> implementations, I was talking about the
> language and the way that I, as a person,
> can evaluate expressions in whatever order
> I like. If I can do that, mentally or on
> paper, it is a property of Joy, the
> language.

But that's not a reason to say Joy isn't a stack-based language. By your
logic, neither is Forth! Indeed it is possible to compile Forth to a
language like C without using a stack at all (given some minor
restrictions such as that both branches of a conditional must have the
same stack effect).

- jn

John Nowak — 2010-05-18 23:53:48

On 05/18/2010 02:42 PM, John Cowan wrote:

> If you consider it a problem. I consider it one of Joy's special beauties:
> the abstraction for a sequence of datums is the same as the abstraction for
> a non-primitive function, which is just a sequence of functions.

Sure, it's simple, but what's beautiful about it? It's a bit like having
Scheme without lambda really. I believe Pico Lisp takes an approach
similar to this, but no other Lisp I've aware of does, and for good
reason. If given the choice between a language with the ability to
construct abstractions and one without, all other things being equal,
I'm certainly opting for the former. I can't think of an instance where
I wouldn't.

>> Finally, applicative languages arguably have an advantage over Joy in
>> terms of arbitrary order reduction. For example, take this Haskell
>> function:
>
> I don't think your example has anything to do with applicativeness, but
> with purity and laziness. A pure lazy concatenative language would have
> the same strengths (and weaknesses) as Haskell.

Perhaps I've made my point poorly. I'm actually pointing at the lack of
many nice algebraic laws in Joy precisely because Joy is concatenative.
Laziness has nothing to do with it. Look at my example again:

Haskell: bi f g x = (f x, g x)
Joy: x [F] [G] bi = x F x G

The issue is that the 'bi' in Haskell always permits arbitrary order
reduction whereas determining if it is possible in the Joy version is
undecidable. This is true regardless of concerns related to strictness.

This is a general problem with Joy. For example, the following law holds
in Haskell:

map f . map g == map (f . g)

However, the following does *not* hold in Joy:

[F] map [G] map == [F G] map
(where [F] map = [null?] [] [uncons [F] dip [F] map cons] ifte)

Similar issues hold for other functions like fold. The source of the
problem is the fact that all functions in Joy are lifted to operate on
products (i.e. the stack). This makes it difficult or impossible to
write higher order functions that have the usual laws because it is
difficult or impossible to prevent functions from accessing arbitrary
amounts of the program's state. For example, the above law for map does
hold with the side condition that 'F' has a stack effect of ( x -- y ).

Manfred's "The Algebra of Joy" talks about Joy's suitability for
equational reasoning and algebraic manipulation, but I'm of the opinion
that it's not actually very good in this regard. I'd actually say that
languages like Clean or Haskell -- or better yet, something like
Charity, Squiggol, or Hagino's CPL -- are far better in this regard.
This is doubly true since Joy offers no control over effects.

This is not to say that Joy is not interesting is other ways of course.
However, I'm of the opinion nowadays that it fails in its (primary?)
objective of providing simple and powerful algebraic manipulation. It's
for this reason that I've generally lost interest in concatenative
languages.

Please correct me if I'm wrong on any of my points.

- jn

John Cowan — 2010-05-19 03:27:19

John Nowak scripsit:

> Sure, it's simple, but what's beautiful about it? It's a bit like having
> Scheme without lambda really.

Scheme (and other applicative languages) need lambda or something like
it because they need to bind names locally. Joy has only global names,
so it doesn't need any such abstraction: non-primitive functions are
literally nothing but lists of objects, where every object in Joy has
both a datum interpretation ('3' means 3) and a function interpretation
('3' means "accept a sequence, return an equivalent sequence but with
3 pushed on it").

> If given the choice between a language with the ability to
> construct abstractions and one without, all other things being equal,
> I'm certainly opting for the former. I can't think of an instance where
> I wouldn't.

Well, given the choice I'd rather have a language that binds local names,
which is why I'm a Joy implementer but not a Joy user. Although, as Henry
Baker showed, a lambda-based language without assignment and with single-use
names is equivalent to a concatenative language.

> Look at my example again:
>
> Haskell: bi f g x = (f x, g x)
> Joy: x [F] [G] bi = x F x G
>
> The issue is that the 'bi' in Haskell always permits arbitrary order
> reduction whereas determining if it is possible in the Joy version is
> undecidable. This is true regardless of concerns related to strictness.

But Haskell only permits arbitrary-order reduction *because* it is
pure and lazy. If you define bi in ML, you'll get a function which
must be evaluated left to right, because it returns bottom if f does.
That's why I say concatenation is not directly relevant.

> Similar issues hold for other functions like fold. The source of the
> problem is the fact that all functions in Joy are lifted to operate on
> products (i.e. the stack). This makes it difficult or impossible to
> write higher order functions that have the usual laws because it is
> difficult or impossible to prevent functions from accessing arbitrary
> amounts of the program's state. For example, the above law for map does
> hold with the side condition that 'F' has a stack effect of ( x -- y ).

That's true. Note, however, that the "map law" doesn't hold for Scheme
either, except with the side condition that F returns just once and with
just one value.

> Manfred's "The Algebra of Joy" talks about Joy's suitability for
> equational reasoning and algebraic manipulation, but I'm of the opinion
> that it's not actually very good in this regard. I'd actually say that
> languages like Clean or Haskell -- or better yet, something like
> Charity, Squiggol, or Hagino's CPL -- are far better in this regard.

Or Pure, which directly does equational reasoning.

--
Schlingt dreifach einen Kreis vom dies! John Cowan <cowan@...>
Schliesst euer Aug vor heiliger Schau, http://www.ccil.org/~cowan
Denn er genoss vom Honig-Tau,
Und trank die Milch vom Paradies. --Coleridge (tr. Politzer)

John Cowan — 2010-05-19 03:31:01

Ruurd scripsit:

> We seem to disagree about what a language is. In my dictionary a
> language consists of: pronunciation, syntax, semantics. It seems to me
> that stack or rewriting is part of the semantics and therefore part of
> the language. If I have a simple expression like: 2 3 +, there is no
> way for you to convince me that this must be executed on a stack. I
> only see three symbols that in the next evaluation step are replaced
> by one symbol.

If you only look at the simple case 2+3, C looks pure and functional too.
You have to examine the totality of the language. But whether a stack
is used or a series of rewrite rules is an implementation detail.

> Making a lazy Joy is quite unnecessary, because there is already a lazy
> library: http://www.latrobe.edu.au/philosophy/phimvt/joy/jp-reprod.html

That library is about lazy lists, not about Haskell-style pervasive
laziness. In Haskell, all primitive functions notice whether their
arguments are datums or thunks, and if they are thunks, they get forced;
this is not provided in Joy.

--
But that, he realized, was a foolish John Cowan
thought; as no one knew better than he cowan@...
that the Wall had no other side. http://www.ccil.org/~cowan
--Arthur C. Clarke, "The Wall of Darkness"

Ruurd — 2010-05-19 07:15:38

> > Quotations. If A, B, C, D, E are valid
> > programs and B == D E, then A B C ==
> > A D E C and vice versa. That leaves the
> > question: what is a valid program. The
> > empty program is valid. A program that
> > has a matching number of [ and ] is valid.
>
> That's actually not true. For example, '+' is not a valid program. It's
> a valid expression, but not every expression is a valid program.

I dón't see why + is not a valid program. When executed it will give a runtime error when there is nothing to add, but validity is a syntactic property. (I have not said so, but with [ and ] I mean the tokens, not the characters). Also, according to the grammar of Joy:
http://www.latrobe.edu.au/philosophy/phimvt/joy/j09imp.html
there is no such thing as an expression. There is only term and factor, a term being equal to zero or more factors. And a program is a term, of course.

>
> > When this rule is followed it will not
> > be possible to substitute within a
> > quotation, because then I would have to
> > choose in your example: A == [, D == 1,
> > E == 2 +, C == ], B == 3; A and C are
> > not valid programs according to the
> > definition.
>
> I think you've misunderstood me. My point is that, for example:

No, I did not misunderstand you. I give rules that distinguish environments that allow substitution and environments where that is not allowed. It is not necessary to do substitution everywhere.

If A and B are valid programs, then so is A B. This rule allows me to make programs bigger. The substitution rule allows me to inline if I favor speed or to factor out if I favor program size. I don't see any reason why I would want to simplify within an abstraction.

blazeski — 2010-05-19 07:38:00

Just what I was looking for, many thanks.

Slobodan

--- In concatenative@yahoogroups.com, "Ruurd" <r.wiersma26@...> wrote:
>
>
>
> If you want a minimal implementation:
>
> http://github.com/jdp/concom
>
> Or, if you want it to be like joy:
>
> http://www.latrobe.edu.au/philosophy/phimvt/sympas/s42minjoy.html
>
> concom does not have numbers; the small joy version has numbers, characters, booleans, symbols and it has more operators than concom.
>
> Joy is built up from three concepts: concatenative notation, quotations and combinators. The structured programming paradigm is built from the concepts: sequencing, selection, iteration, subroutines, arrays. When comparing the two paradigms: concatenative notation is roughly the same as sequencing; selection and iteration are covered by combinators; subroutines and arrays by quotations.
>
> When comparing Joy to other functional programming languages, it looks like Joy does not hide data, but makes it accessible on a stack. Other functional programming languages hide data in the parameters to their functions. Having data accessible means that it is possible to think about Joy is if it were an imperative programming language. But it is not, because it does not have assignment.
>
> And it is not a stack language either. The order of evaluation is not specified. Take for instance a small example: 2 3 [+] i. This evaluates to 5, but I do not have to start the rewriting at the left.[+] i is equal to + and 2 3 + is equal to 5, so I can start at the right if I like.
>
> > For example from reading Brent Kerby Theory of concatenative combinators http://tunes.org/~iepos/joy.html if I implement : swap, dup, zap, unit, cat, cons, i, dip + (numbers and symbols) would that be enough to have a primitive joy like language?
> >
>

john@johnnowak.com — 2010-05-19 16:57:21

On Tue, 18 May 2010 23:27:19 -0400, John Cowan <cowan@...> wrote:
> John Nowak scripsit:
>
>> Sure, it's simple, but what's beautiful about it? It's a bit like
having
>> Scheme without lambda really.
>
> Scheme (and other applicative languages) need lambda or something like
> it because they need to bind names locally. Joy has only global names,
> so it doesn't need any such abstraction: non-primitive functions are
> literally nothing but lists of objects, where every object in Joy has
> both a datum interpretation ('3' means 3) and a function interpretation
> ('3' means "accept a sequence, return an equivalent sequence but with
> 3 pushed on it").

I think you're maybe missing my point. I'm aware Scheme needs lambda to
bind names. However, there is no reason lambda needs to form *opaque*
abstractions. In Pico Lisp, it does not; you can modify a lambda as if it
were a pile of conses and atoms. In most Lisps, however, it does form
opaque abstractions because of the many advantages of having your functions
be opaque. I believe the benefits of opaque abstractions would apply to Joy
regardless of the issue of binding.

> But Haskell only permits arbitrary-order reduction *because* it is
> pure and lazy. If you define bi in ML, you'll get a function which
> must be evaluated left to right, because it returns bottom if f does.
> That's why I say concatenation is not directly relevant.

Let me put it another way. It's easy to have a parallel 'bi' in an ML-like
language with the type '(a -> b) -> (c -> d) -> (a, c) -> (b, d)'. You
can't do this in the concatenative setting because bi has the type 'R a [R
-> S] [S a -> T] -> T' and hence it is not possible to enforce that the
quotation on the top of the stack does not use more than one element. You
*could* give it a type like 'R a [R -> S] [a -> T] -> S ++ T', but this
sort of type level concatenation is undecidable.

Even if your language is eager and you're not considering parallel
combinators, there are still benefits to the applicative approach. In
particular, you can reduce and transform 'f x' in strictness-preserving
ways without worrying about the arity of 'g x'. This is useful when doing
algebraic manipulation of programs regardless of your evaluation mechanism.

- jn

William Tanksley, Jr — 2010-05-19 17:00:55

John Nowak <john@...> wrote:
> Sure, it's simple, but what's beautiful about it? It's a bit like having
> Scheme without lambda really. I believe Pico Lisp takes an approach
> similar to this, but no other Lisp I've aware of does, and for good
> reason. If given the choice between a language with the ability to
> construct abstractions and one without, all other things being equal,
> I'm certainly opting for the former. I can't think of an instance where
> I wouldn't.

I also don't like how Joy uses the same notation for list literals as
it does for function literals. Other concatenative languages don't do
this. I don't understand the rest of your response, in particular the
part about not being able to construct abstractions which seems so
important to you.

>>> Finally, applicative languages arguably have an advantage over Joy in
>>> terms of arbitrary order reduction. For example, take this Haskell
>>> function:

>> I don't think your example has anything to do with applicativeness, but
>> with purity and laziness.  A pure lazy concatenative language would have
>> the same strengths (and weaknesses) as Haskell.

> Perhaps I've made my point poorly. I'm actually pointing at the lack of
> many nice algebraic laws in Joy precisely because Joy is concatenative.

No. The parallel evaluation laws don't apply because concatenative
languages are not tree-structured. The laws that apply are
_different_. You can't criticize Greek because it doesn't use a roman
alphabet -- although you could criticize ancient Egyptian for not
using an alphabet at all, and English because it uses an overly
complex phonetic system. Anyhow, before this set of criticism begins,
you have to justly characterize the algebraic laws that DO apply,
rather than mourning the ones that obviously do NOT.

> Laziness has nothing to do with it. Look at my example again:

Laziness is one of the things that can't easily be imported to a
concatenative language.

> The issue is that the 'bi' in Haskell always permits arbitrary order
> reduction whereas determining if it is possible in the Joy version is
> undecidable. This is true regardless of concerns related to strictness.

In Joy it's undecidable. In Haskell it's decidable. The distinction
between the two isn't that one is concatenative and the other is
applicative; it's that Haskell's type system is designed to allow this
kind of decision, while Joy isn't. There are concatenative languages
which make deciding this linear in the size of the function
(StrongForth, for example).

> - jn

-Wm

john@johnnowak.com — 2010-05-19 17:04:52

On Wed, 19 May 2010 07:15:38 -0000, "Ruurd" <r.wiersma26@...>
wrote:

> I don't see why + is not a valid program. When executed it will give a
> runtime error when there is nothing to add, but validity is a syntactic
> property.

Validity is not, in general, a syntactic property (e.g. it may be rejected
for type-level reasons). But yes, if you're considering the error resulting
from such a program to be a well-defined result, then your program is
technically valid. I'm not sure Joy is well-defined enough to answer this
question properly. I'll concede that you're likely right in this cases
though; my brain was stuck in typed mode as usual.

>> I think you've misunderstood me. My point is that, for example:
>
> No, I did not misunderstand you. I give rules that distinguish
> environments that allow substitution and environments where
> that is not allowed. It is not necessary to do substitution
> everywhere.

No it isn't, but it's very, very useful if you're interested in algebraic
manipulation of programs (which is, in my understanding, the principal
issue that Manfred wanted to address with Joy).

- jn

john@johnnowak.com — 2010-05-19 17:16:06

On Wed, 19 May 2010 11:57:21 -0500, <john@...> wrote:

> Even if your language is eager and you're not considering parallel
> combinators, there are still benefits to the applicative approach. In
> particular, you can reduce and transform 'f x' in strictness-preserving
> ways without worrying about the arity of 'g x'. This is useful when
doing
> algebraic manipulation of programs regardless of your evaluation
mechanism.

Final clarification. I mean you can apply rules like this without
consideration for arity and coarity:

cross f g . bi h i == cross (f . h) (g . i)
where bi f g x = (f x, g x)
cross f g x y = (f x, g y)

This is equivalent to a rule like the following in Joy except for the fact
that, of course, this rule isn't remotely valid:

[H] [I] bi [F] [G] bi* == [H F] [I G] bi*
where [F] [G] bi = [keep] dip i
[F] [G] bi* = [dip] dip i

It is, however, valid if all functions have a stack effect of ( x -- x ).
Because the functions in the applicative version aren't lifted to operate
on a stack, you don't have this issue.

- jn

john@johnnowak.com — 2010-05-19 17:21:05

I am never posting at work again.

> cross f g . bi h i == cross (f . h) (g . i)

cross f g . bi h i == bi (f . h) (g . i)

> [H] [I] bi [F] [G] bi* == [H F] [I G] bi*

[H] [I] bi [F] [G] bi* == [H F] [I G] bi

Argh!

- jn

pml060912 — 2010-05-20 14:32:38

--- In concatenative@yahoogroups.com, "William Tanksley, Jr" <wtanksleyjr@...> wrote:
>
> John Nowak <john@...> wrote:
.
.
.
> > Laziness has nothing to do with it. Look at my example again:
>
> Laziness is one of the things that can't easily be imported to a
> concatenative language.

Well, that depends on what you call lazy. For Furphy, I worked out an indirection mechanism that allows you to produce the effect of laziness even when everything actually encountered is handled eagerly: the pair FREEZE and THAW, although you would usually use prefrozen code set up with syntactic sugar instead of FREEZE. FREEZE, THAW, and the syntactic sugar alternative are all quite easy to implement and to use. P.M.Lawrence.

William Tanksley, Jr — 2010-05-21 14:23:37

pml060912 <pml540114@...> wrote:
> "William Tanksley, Jr" <wtanksleyjr@...> wrote:
>> Laziness is one of the things that can't easily be imported to a
>> concatenative language.

> Well, that depends on what you call lazy.

Actually, I don't think there's ambiguity in this context. A lazy
language is one in which the semantics for the parameters to a
function are performed only if the parameter is actually USED. This
means that you can have a function with a parameter that cannot be
evaluated completely, and the program will run if the parameter is not
actually used in the function.

> For Furphy,

http://users.beagle.com.au/peterl/furphy.html (I think -- is that your
latest work?).

> I worked out an indirection mechanism that allows you
> to produce the effect of laziness even when everything
> actually encountered is handled eagerly: the pair
> FREEZE and THAW,

That's not a lazy language, I think. Unfortunately, I'm unqualified to
judge, since I'm unable to read your examples; I have no idea where
the words TRUE_OPTION and FALSE_OPTION are defined; they just appear
there. (I vaguely recall having figured out how to read Furphy, but
I've forgotten since.)

The distinction is that in this language you have to explicitly mark
the non-evaluated parts; a truly lazy language needs no such marker.

> P.M.Lawrence.

-Wm

pml060912 — 2010-05-23 05:08:53

--- In concatenative@yahoogroups.com, "William Tanksley, Jr" <wtanksleyjr@...> wrote:
>
> pml060912 <pml540114@...> wrote:
> > "William Tanksley, Jr" <wtanksleyjr@> wrote:
> >> Laziness is one of the things that can't easily be imported to a
> >> concatenative language.
>
> > Well, that depends on what you call lazy.
>
> Actually, I don't think there's ambiguity in this context. A lazy
> language is one in which the semantics for the parameters to a
> function are performed only if the parameter is actually USED. This
> means that you can have a function with a parameter that cannot be
> evaluated completely, and the program will run if the parameter is not
> actually used in the function.

The indirection mechanism achieves that effect very simply, so it depends whether you are willing to call it lazy even though it is only delivering lazy semantics and isn't using lazy syntax to get there. But, since the mechanism is simple, it wouldn't be hard to set up a concatenative language that had syntactic sugar for that as well - making it both syntactically and semantically lazy. That's why I thought it was relevant.

>
> > For Furphy,
>
> http://users.beagle.com.au/peterl/furphy.html (I think -- is that your
> latest work?).

Well, the latest that's firmed up. I have been thinking about an operator that would "glue" two frozen words together to get a new (frozen) word, and which sorts of garbage collection would be the best choices.

>
> > I worked out an indirection mechanism that allows you
> > to produce the effect of laziness even when everything
> > actually encountered is handled eagerly: the pair
> > FREEZE and THAW,
>
> That's not a lazy language, I think. Unfortunately, I'm unqualified to
> judge, since I'm unable to read your examples; I have no idea where
> the words TRUE_OPTION and FALSE_OPTION are defined; they just appear
> there. (I vaguely recall having figured out how to read Furphy, but
> I've forgotten since.)

That "appearing" IS the definition, the first time they appear. With that kind of reverse polish naming, an unrecognised token closes the current definition and is assigned as its name. Here are the definitions from the example code again:-

FREEZE TRUE_STUFF TRUE_OPTION

FREEZE FALSE_STUFF FALSE_OPTION

(I didn't define TRUE_STUFF and FALSE_STUFF.)

>
> The distinction is that in this language you have to explicitly mark
> the non-evaluated parts; a truly lazy language needs no such marker.

Since there is already some laziness, with nothing being done apart from immediate words until the source has been compiled, it would be easy enough to invert that so that everything is frozen unless deliberately invoked. You would just drop the "and go" feature from the compile and go compiler and also change new word definitions to default to frozen, leaving invocation up to suitable immediate words. On the whole I prefer this way around since it is a closer and more readable match to what is usually needed, but it does show that there isn't really a big problem implementing full laziness. P.M.Lawrence.

Don Groves — 2010-05-23 06:49:08

> --- In concatenative@yahoogroups.com, "William Tanksley, Jr"
> <wtanksleyjr@...> wrote:
>>
>> pml060912 <pml540114@...> wrote:
>>> "William Tanksley, Jr" <wtanksleyjr@> wrote:
>>>> Laziness is one of the things that can't easily be imported to a
>>>> concatenative language.

It seems to me that laziness can be implemented simply by
encapsulating all data in functions. If a particular function
is not called, that data is not evaluated. What am I missing?

don

Ruurd — 2010-05-23 08:06:11

Strictly False, mentioned in message #2834 (the link is still valid)also has quotations.

--- In concatenative@yahoogroups.com, "William Tanksley, Jr" <wtanksleyjr@...> wrote:
> I also don't like how Joy uses the same notation for list literals as
> it does for function literals. Other concatenative languages don't do
> this.

Ruurd — 2010-05-23 08:23:18

The discussion is partly about what laziness is and partly about whether the language supports it as the default evaluation mechanism.
Haskell is lazy, Ocaml is strict. That is why Haskell is 9 times slower than Ocaml: http://www.cubbi.com/fibonacci.html
The discussion is not about performance, but it does show that laziness comes with a price. Using lazy constructs in a strict language also comes with a price. Now it is the programmer who has to set up the construct, everywhere he needs it.
By capturing data in a quotation Joy already offers lazy constructs. Of course, it is now up to the programmer to decide when and where the data is needed. Now the programmer can not be lazy.

--- In concatenative@yahoogroups.com, Don Groves <dgroves@...> wrote:
>
> > --- In concatenative@yahoogroups.com, "William Tanksley, Jr"
> > <wtanksleyjr@> wrote:
> >>
> >> pml060912 <pml540114@> wrote:
> >>> "William Tanksley, Jr" <wtanksleyjr@> wrote:
> >>>> Laziness is one of the things that can't easily be imported to a
> >>>> concatenative language.
>
> It seems to me that laziness can be implemented simply by
> encapsulating all data in functions. If a particular function
> is not called, that data is not evaluated. What am I missing?
>
> don
>

Ruurd — 2010-05-23 09:00:21

Joy is tree-structured. Also, the only thing that is usually needed to change a sequential program into a parallel evaluated program is to change map into pmap.
As an example, look at Manfred's question about the quadratic formula in message #2176 and his answer in message #2228. His solution uses the ternary combinator to compute three subexpressions. These expressions can in theory be computed in parallel.
When I thought about parallel evaluation, because computations can be done while the program is waiting for I/O to finish, I was advised not to use parallel evaluation for that purpose. It would be simpler to use non-blocking I/O functions.
When thinking about a parallel language out of the many languages that claim to be parallel the only language that makes any sense is Erlang. Now that desktop processors are multicore, there is a lot of interest in a good parallel language with a nice and familiar syntax that is acceptable for mainstream programmers.

--- In concatenative@yahoogroups.com, "William Tanksley, Jr" > No. The parallel evaluation laws don't apply because concatenative
> languages are not tree-structured.

William Tanksley, Jr — 2010-05-23 17:20:46

Ruurd <r.wiersma26@...> wrote:
> Joy is tree-structured.

When you consider list and function literals, I admit there's a
syntactic tree structure (and for Joy, function literals are essential
to writing programs). But concatenative languages in general are not
intrinsically tree-structured; applicative languages are, since every
function call forms the root of a subtree. It's possible to define a
library that allows one to write entirely flat programs in any
concatenative language. (My current efforts on this front show that it
is ugly and inconvenient when one attempts to be 100% "flat"; as with
most things in programming, being satisfied with 99% purity is much
better than going for 100%.)

> Also, the only thing that is usually needed to change a sequential program into a parallel evaluated program is to change map into pmap.

Unless I missed something, that wasn't what was being discussed; we
were talking about implicit compiler support for parallelism, the sort
of thing that allows compilers to generate decent code for modern
(superscalar) architectures.

Also... In general, "changing map into pmap" is insufficient (there
are MANY other places for parallelism, and an explicit map is too
rare) and often incorrect (map consumes and generates an ordered list,
so computing in parallel simply means that the runtime will have to
assemble the result list in the sequential order).

> When I thought about parallel evaluation, because computations can be done while the program is waiting for I/O to finish, I was advised not to use parallel evaluation for that purpose. It would be simpler to use non-blocking I/O functions.

Exactly -- one needs profound support for parallel computation, not
merely a few quick changes.

> When thinking about a parallel language out of the many languages that claim to be parallel the only language that makes any sense is Erlang. Now that desktop processors are multicore, there is a lot of interest in a good parallel language with a nice and familiar syntax that is acceptable for mainstream programmers.

I largely agree. Except that I'm not sure about Erlang being the only
thing that "makes any sense"; there are many other options that do
much the same thing. Erlang is simply very well tested and supported.
I'd like to see what might happen with a parallel version of k4 (a
proprietary language from kx.com, a remote derivative of APL); more
appropriately for this group, the concatenative languages Enchilada
and Ripple, both of which are intrinsically parallel (although
Enchilada is probably TOO parallel to be practical).

-Wm

William Tanksley, Jr — 2010-05-23 17:33:59

Ruurd <r.wiersma26@...> wrote:
> The discussion is partly about what laziness is and partly about whether the language supports it as the default evaluation mechanism.

Yes.

> Haskell is lazy, Ocaml is strict. That is why Haskell is 9 times slower than Ocaml: http://www.cubbi.com/fibonacci.html

False -- Ocaml is fast because it's deliberately impure. This allows
the programmer to make speed-related decisions, thus allowing the
compiler to succeed with much less specialized optimization code.
Haskell is slow because it is pure; the programmer is (essentially)
not permitted to write code for speed (it's possible, but the result
is extremely ugly code with TONS of interwoven monads). New Haskell
compilers become MUCH faster as their optimizers become more
sophisticated (I doubt that Ocaml still holds a 9x advantage).

As I said in my last post, it's more practical to be 99% pure than 100%.

> By capturing data in a quotation Joy already offers lazy constructs. Of course, it is now up to the programmer to decide when and where the data is needed. Now the programmer can not be lazy.

I don't see how you can claim that allowing list literals is similar
to being a lazy language. That simply destroys the meaning of the
word, to the point that machine language becomes a lazy language.

-Wm

William Tanksley, Jr — 2010-07-16 17:21:20

Don Groves <dgroves@...> wrote:
>>>> "William Tanksley, Jr" <wtanksleyjr@> wrote:
>>>>> Laziness is one of the things that can't easily be imported to a
>>>>> concatenative language.
> It seems to me that laziness can be implemented simply by
> encapsulating all data in functions. If a particular function
> is not called, that data is not evaluated. What am I missing?

Data isn't supposed to be evaluated, of course; I think you meant
encapsulating all functions in data rather than the other way around.
And indeed, that does allow you to control when your parameters are
evaluated (thus making them lazy); but it also makes tracking
parameters part of writing and reading your program -- and a lot of
that winds up being done at runtime.

I guess my basic point is that there's a type of laziness, let's call
it automatic laziness, that just can't be done in a concatenative
language. Syntax sugar won't save you from that problem, I think,
since there's too many operations that need to be done.

This doesn't mean I don't like concatenative languages; I just think
there's some research to be done in this area.

You know, it occurs to me that one place to look for a starting point
in that research might be colorforth. Take a look:
http://rainbowforth.sourceforge.net/blocks.html (see block 36,
'factorial', for a programlike block, most of the earlier stuff is the
source code for the language, including a lot of machine code).
Colorforth allows you to write code that's simply neutrally stored, or
markers that help you access that stored code, or immediately executed
code, or code that generates results that go into a definition...
There's a lot of different things to do. The different operations are
distinguished by color codes.

> don

-Wm

Don Groves — 2010-07-21 03:18:00

On 16 Jul 2010, at 10:21, William Tanksley, Jr wrote:

> Don Groves <dgroves@...> wrote:
>>>>> "William Tanksley, Jr" <wtanksleyjr@> wrote:
>>>>>> Laziness is one of the things that can't easily be imported to a
>>>>>> concatenative language.
>> It seems to me that laziness can be implemented simply by
>> encapsulating all data in functions. If a particular function
>> is not called, that data is not evaluated. What am I missing?
>
> Data isn't supposed to be evaluated, of course; I think you meant
> encapsulating all functions in data rather than the other way around.

OK, guess I may need some hand-holding here:

Isn't call-by-reference essentially equivalent to lazy evaluation?
If the called function doesn't need a certain parameter during a
particular call, that parameter will not be evaluated. So, parameters
(data) are only evaluated when needed, i.e., they are lazy.

In a call-by-value environment, It seems to me that incapsulating
all data in functions yields the same effect as call-by-reference. If
a parameter is not needed, its evaluating function will not be called.

If I'm on the wrong track with this, please enlighten me!

don


> And indeed, that does allow you to control when your parameters are
> evaluated (thus making them lazy); but it also makes tracking
> parameters part of writing and reading your program -- and a lot of
> that winds up being done at runtime.
>
> I guess my basic point is that there's a type of laziness, let's call
> it automatic laziness, that just can't be done in a concatenative
> language. Syntax sugar won't save you from that problem, I think,
> since there's too many operations that need to be done.
>
> This doesn't mean I don't like concatenative languages; I just think
> there's some research to be done in this area.
>
> You know, it occurs to me that one place to look for a starting point
> in that research might be colorforth. Take a look:
> http://rainbowforth.sourceforge.net/blocks.html (see block 36,
> 'factorial', for a programlike block, most of the earlier stuff is the
> source code for the language, including a lot of machine code).
> Colorforth allows you to write code that's simply neutrally stored, or
> markers that help you access that stored code, or immediately executed
> code, or code that generates results that go into a definition...
> There's a lot of different things to do. The different operations are
> distinguished by color codes.
>
>> don
>
> -Wm
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>

John Nowak — 2010-07-21 04:11:10

On 2010.07.20, at 11:18 PM, Don Groves wrote:

> Isn't call-by-reference essentially equivalent to lazy evaluation?

Nope. Call-by-reference is an abomination that shouldn't exist. Lazy evaluation is unrelated.

A quick run through here should help:
http://en.wikipedia.org/wiki/Evaluation_strategy

Caveat: It is possible to have an eager yet non-strict language. Wikipedia conflates eagerness and strictness which is unfortunate, although most existing eager languages are indeed strict. The Id programming language is an example of an eager, non-strict language.

Wikipedia is bad at precision.

> In a call-by-value environment, It seems to me that incapsulating
> all data in functions yields the same effect as call-by-reference.

If you replace "call-by-reference" with "lazy evaluation" (or similar), then you're essentially right if your language has lambda abstractions under which evaluation does not occur until they're applied. Lazy evaluation will share the result of evaluating the parameter though if it is needed in multiple places. You have to manually memoize the function passed in to emulate this in an eager setting.

Concatenative languages do not have lambda abstractions, but you can achieve a similar result by placing a function on the stack and lifting values into it (e.g. via Factor's misnamed 'curry' function; it really should be called 'partial-apply' or similar, although that's not exactly right either).

- jn

William Tanksley, Jr — 2010-07-21 04:18:15

Don Groves <dgroves@...> wrote:
> William Tanksley, Jr wrote:
>> Don Groves <dgroves@...> wrote:
>>>>>> "William Tanksley, Jr" <wtanksleyjr@> wrote:
>>>>>>> Laziness is one of the things that can't easily be imported to a
>>>>>>> concatenative language.
>>>  It seems to me that laziness can be implemented simply by
>>> encapsulating all data in functions. If a particular function
>>> is not called, that data is not evaluated. What am I missing?

>> Data isn't supposed to be evaluated, of course; I think you meant
>> encapsulating all functions in data rather than the other way around.

> OK, guess I may need some hand-holding here:

No... It's just unclear presuppositions. Thanks for making them clear
by asking about them.

> Isn't call-by-reference essentially equivalent to lazy evaluation?

No; C++ allows call by reference semantics (explicitly specified, of
course) without being lazy at all. An early Fortran had implicit call
by reference, again not lazy (but, I'm told, very dangerous). I don't
use Perl, but I'm told it's call-by-reference.

Wikipedia just reminded me that you're thinking of "call by name" or
"call by need", but those are simply ways to achieve lazy evaluation.

> If the called function doesn't need a certain parameter during a
> particular call, that parameter will not be evaluated. So, parameters
> (data) are only evaluated when needed, i.e., they are lazy.

That IS what lazy evaluation is, yes.

> In a call-by-value environment, It seems to me that encapsulating
> all data in functions yields the same effect as call-by-reference. If
> a parameter is not needed, its evaluating function will not be called.

This gets confusing to me, so hold on tight... :-)

In order to pass a function to a procedure, the function has to be
available as data. If it's not, you can't pass it.

Now, if your language is lazy (or non-strict) this doesn't matter; you
can avoid executing the function by never mentioning its parameter on
an execution path. This allows languages that don't provide
higher-order functions to still be very expressive.

> don

-Wm

William Tanksley, Jr — 2010-07-21 04:32:17

John Nowak <john@...> wrote:
> Don Groves wrote:
>> Isn't call-by-reference essentially equivalent to lazy evaluation?
> Nope. Call-by-reference is an abomination that shouldn't exist. Lazy evaluation is unrelated.

Heh. John, is this because CBR hypothetically allows mutation of
arbitrary items? I'm not sure that it shouldn't exist, but it should
certainly be confined to explicitly declared OUT parameters.

> A quick run through here should help:
> http://en.wikipedia.org/wiki/Evaluation_strategy

Yes, that's what I used after I wrote my first draft to make it look
like I knew what I was talking about.

> Caveat: It is possible to have an eager yet non-strict language. Wikipedia conflates eagerness and strictness which is unfortunate, although most existing eager languages are indeed strict. The Id programming language is an example of an eager, non-strict language.

WP's explanation of non-strictness on that page is pathetic. BUT, I'm
puzzled... Their definition is clear, and it matches what the first
pageful Google results seem to be using, and their definition utterly
precludes yours... So I'm curious. How is Id both eager and
non-strict?

> Wikipedia is bad at precision.

If this is true, its accuracy is more to be bemoaned than its precision.

Usually its clarity is worse. Especially in highly technical matters.

> Concatenative languages do not have lambda abstractions, but you can achieve a similar result by placing a function on the stack and lifting values into it (e.g. via Factor's misnamed 'curry' function; it really should be called 'partial-apply' or similar, although that's not exactly right either).

In a more general sense, what you're describing is passing a function
_as_ data -- not data inside a function, but rather a function as
data.

> - jn

-Wm

John Cowan — 2010-07-21 04:33:46

William Tanksley, Jr scripsit:

> No; C++ allows call by reference semantics (explicitly specified, of
> course) without being lazy at all. An early Fortran had implicit call
> by reference, again not lazy (but, I'm told, very dangerous). I don't
> use Perl, but I'm told it's call-by-reference.

The default calling strategy for Fortran is and always has been call by
reference. The dangerous bit was an implementation, not a specification,
error. Buggy compilers conflated all instances of identical constants
(thus there would be only one cell containing 5 no matter how many literal
5s there were in the code) without realizing that literals passed as
arguments could not be conflated in such a manner.

Perl is technically call-by-reference, but it is usual to provide lexically
scoped variables that are mapped to the values of the call-by-reference
parameters, and to assign them first thing in each procedure. The main
exception is the comparison procedure passed to "sort", which uses
by-reference arguments directly (always named "$a" and "$b") for speed.

--
John Cowan cowan@... http://ccil.org/~cowan
Assent may be registered by a signature, a handshake, or a click of a computer
mouse transmitted across the invisible ether of the Internet. Formality
is not a requisite; any sign, symbol or action, or even willful inaction,
as long as it is unequivocally referable to the promise, may create a contract.
--Specht v. Netscape

John Nowak — 2010-07-21 04:39:54

On 2010.07.21, at 12:18 AM, William Tanksley, Jr wrote:

> In order to pass a function to a procedure, the function has to be
> available as data. If it's not, you can't pass it.
>
> Now, if your language is lazy (or non-strict) this doesn't matter; you
> can avoid executing the function by never mentioning its parameter on
> an execution path.

In an eager language without first-class functions, the language implementor can offer a special 'delay' form that creates a thunk at runtime. A 'force' function can later force the computation to occur. Since the only real semantic issue with introducing 'delay' is due to strictness (e.g. 'delay (4 / 0)' would not cause an error until forced even though '4 / 0' does immediately), it's a relatively uncomplicated addition from a theoretical perspective.

> This allows languages that don't provide higher-order functions to still be very expressive.

You'd still need some way to parameterize functions by other functions even if functions aren't available as data at runtime. OBJ is an excellent example of how to do parameterized programming without first-class functions.

- jn

Don Groves — 2010-07-21 05:12:30

On 20 Jul 2010, at 21:18, William Tanksley, Jr wrote:

> Don Groves <dgroves@...> wrote:
>> William Tanksley, Jr wrote:
>>> Don Groves <dgroves@...> wrote:
>>>>>>> "William Tanksley, Jr" <wtanksleyjr@> wrote:
>>>>>>>> Laziness is one of the things that can't easily be imported
>>>>>>>> to a
>>>>>>>> concatenative language.
>>>> It seems to me that laziness can be implemented simply by
>>>> encapsulating all data in functions. If a particular function
>>>> is not called, that data is not evaluated. What am I missing?
>
>>> Data isn't supposed to be evaluated, of course; I think you meant
>>> encapsulating all functions in data rather than the other way
>>> around.
>
>> OK, guess I may need some hand-holding here:
>
> No... It's just unclear presuppositions. Thanks for making them clear
> by asking about them.

You're welcome and this is getting vert interesting ;-)

First, I should explain my background. When I was at UCLA in
the early 60s, there was no such field as computer science.
The closest thing to it was a math class on numerical methods
taught by a physics professor.

I was working as a Fortran programmer though and that's how
I came to know this stuff. I learned assembly languages by always
printing what Fortran compilers called the "second file" which had
the assembly code interspersed in the Fortran code. Those were
the good old days for me...


>> Isn't call-by-reference essentially equivalent to lazy evaluation?
>
> No; C++ allows call by reference semantics (explicitly specified, of
> course) without being lazy at all. An early Fortran had implicit call
> by reference, again not lazy (but, I'm told, very dangerous). I don't
> use Perl, but I'm told it's call-by-reference.

The reason Fortran's call by reference was so dangerous was that
numerical constants were also passed by reference and hence their
value could be changed by the called subroutine. This was lazy all
right, a lazy implementation. Passing the address of a function which
would yield the constant would have been far safer but, in those days,
the programmer was expected to do most of the work. Programmer
time was dirt cheap by comparison to machine time on an IBM 7094
that cost $2 million 1960 dollars!


> Wikipedia just reminded me that you're thinking of "call by name" or
> "call by need", but those are simply ways to achieve lazy evaluation.
>
>> If the called function doesn't need a certain parameter during a
>> particular call, that parameter will not be evaluated. So, parameters
>> (data) are only evaluated when needed, i.e., they are lazy.
>
> That IS what lazy evaluation is, yes.

OK, at least I've got a handle on that concept.


>> In a call-by-value environment, It seems to me that encapsulating
>> all data in functions yields the same effect as call-by-reference. If
>> a parameter is not needed, its evaluating function will not be
>> called.
>
> This gets confusing to me, so hold on tight... :-)
>
> In order to pass a function to a procedure, the function has to be
> available as data. If it's not, you can't pass it.

I don't understand this -- I do it all the time in my C code by using
the
address operator, &, and the same is easily done in Forth, et al.

It means, of course that the called function must be more intelligent
than is required in some languages. It must know the type values
of its parameters. As I've read on this list in the past, it seems there
has been a lot of research aimed at automating that very task.

As I said, a most interesting and enlightening discussion. Thanks
for taking part.

don



> Now, if your language is lazy (or non-strict) this doesn't matter; you
> can avoid executing the function by never mentioning its parameter on
> an execution path. This allows languages that don't provide
> higher-order functions to still be very expressive.
>
>> don
>
> -Wm
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>

John Nowak — 2010-07-21 05:27:59

On 2010.07.21, at 12:32 AM, William Tanksley, Jr wrote:

> John Nowak <john@...> wrote:
>
>> Nope. Call-by-reference is an abomination that shouldn't exist. Lazy evaluation is unrelated.
>
> Heh. John, is this because CBR hypothetically allows mutation of arbitrary items? I'm not sure that it shouldn't exist, but it should certainly be confined to explicitly declared OUT parameters.

I overstated my point. Call-by-reference is okay provided that it is not the default and that the language has sufficient support to keep things sane. Ada is a nice example of how to do it properly.

> WP's explanation of non-strictness on that page is pathetic. BUT, I'm puzzled... Their definition is clear, and it matches what the first pageful Google results seem to be using, and their definition utterly precludes yours... So I'm curious. How is Id both eager and non-strict?

A lazy language is prevented from evaluating more than is absolutely necessary. This is fatal to implicit parallelism; you can't kick off a parallel evaluation unless you're sure that the results of both evaluations are needed.

Id is an implicitly parallel language, and hence lazy evaluation was not an option. An eager, strict approach is possible, but it also limits parallelism to some degree. For example, if you have multiple evaluations running in parallel and you discover that one of them is not needed, you're not allowed to abort it. On the contrary, you need to continue running it simply to see if it evaluates to an error or not. In the worst case, one of these unneeded parallel evaluations goes into an infinite loop and the entire program cannot continue as a result.

A language that uses lenient evaluation is optimal for parallelism. It does this by both allowing speculative evaluation and by throwing out the requirement that strictness be respected. For example, the result of 'first (4/2, 4/0)' can be either '2' or an error in a lenient language. As such, it is an example of a non-deteriministic evaluation strategy (which is a downside).

Another nice advantage of lenient languages is that you can optimize without concern for ensuring termination. For example, the rewrite rule 'first (a, b) -> a' is a valid means of optimizing a lazy or lenient language (assuming they're pure), but not a valid means for optimizing an eager language (because the second element of the tuple may be _|_).

Here's a nice paper on lenient evaluation:
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.137.9885

In addition, you may want to look at optimistic evaluation with is also non-strict and (primarily) eager. Like lenient evaluation, it allows speculative evaluation. Unlike lenient evaluation however, it is deterministic. If a speculative evaluation encounters an error, it avoids signaling it until it is determined that the result is needed. If an evaluation takes too long (meaning it may be looping) or is otherwise progressing in an undesirable fashion, it is aborted. For this reason, optimistic evaluation was seen as a way of getting better performance out of Haskell. While optimistic evaluation doesn't have the downside of non-determinstism, it brings with it additional overhead and runtime complexity compared to eager evaluation.

Ennals and SPJ on optimistic evaluation:
http://research.microsoft.com/en-us/um/people/simonpj/Papers/optimistic/index.htm

I think languages of the future may well embrace lenient evaluation, although I'm less sure about optimistic evaluation. By easing the requirements around strictness, additional optimizations are possible and parallelism is easier to attain. The overhead of lenient evaluation can be eliminated by only employing it in ways that do not require additional boxing of values. As such, lenient evaluation allows for strictly better performance (no pun intended) than eager evaluation.

>> Concatenative languages do not have lambda abstractions, but you can achieve a similar result by placing a function on the stack and lifting values into it (e.g. via Factor's misnamed 'curry' function; it really should be called 'partial-apply' or similar, although that's not exactly right either).
>
> In a more general sense, what you're describing is passing a function
> _as_ data -- not data inside a function, but rather a function as
> data.

Correct.

- jn

Don Groves — 2010-07-21 05:57:50

On 20 Jul 2010, at 21:33, John Cowan wrote:

> William Tanksley, Jr scripsit:
>
>> No; C++ allows call by reference semantics (explicitly specified, of
>> course) without being lazy at all. An early Fortran had implicit call
>> by reference, again not lazy (but, I'm told, very dangerous). I don't
>> use Perl, but I'm told it's call-by-reference.
>
> The default calling strategy for Fortran is and always has been call
> by
> reference. The dangerous bit was an implementation, not a
> specification,
> error. Buggy compilers conflated all instances of identical constants
> (thus there would be only one cell containing 5 no matter how many
> literal
> 5s there were in the code) without realizing that literals passed as
> arguments could not be conflated in such a manner.

And there was also the problem of the called routine's ability to
change the value of a constant so passed. There were some pretty
neat obfuscations around due to this feature though...

don


> Perl is technically call-by-reference, but it is usual to provide
> lexically
> scoped variables that are mapped to the values of the call-by-
> reference
> parameters, and to assign them first thing in each procedure. The
> main
> exception is the comparison procedure passed to "sort", which uses
> by-reference arguments directly (always named "$a" and "$b") for
> speed.
>
> --
> John Cowan cowan@... http://ccil.org/~cowan
> Assent may be registered by a signature, a handshake, or a click of
> a computer
> mouse transmitted across the invisible ether of the Internet.
> Formality
> is not a requisite; any sign, symbol or action, or even willful
> inaction,
> as long as it is unequivocally referable to the promise, may create
> a contract.
> --Specht v. Netscape
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>

Don Groves — 2010-07-21 06:06:07

On 20 Jul 2010, at 21:32, William Tanksley, Jr wrote:

> John Nowak <john@...> wrote:
>> Don Groves wrote:
>>> Isn't call-by-reference essentially equivalent to lazy evaluation?
>> Nope. Call-by-reference is an abomination that shouldn't exist.
>> Lazy evaluation is unrelated.
>
> Heh. John, is this because CBR hypothetically allows mutation of
> arbitrary items? I'm not sure that it shouldn't exist, but it should
> certainly be confined to explicitly declared OUT parameters.
>
>> A quick run through here should help:
>> http://en.wikipedia.org/wiki/Evaluation_strategy
>
> Yes, that's what I used after I wrote my first draft to make it look
> like I knew what I was talking about.
>
>> Caveat: It is possible to have an eager yet non-strict language.
>> Wikipedia conflates eagerness and strictness which is unfortunate,
>> although most existing eager languages are indeed strict. The Id
>> programming language is an example of an eager, non-strict language.
>
> WP's explanation of non-strictness on that page is pathetic. BUT, I'm
> puzzled... Their definition is clear, and it matches what the first
> pageful Google results seem to be using, and their definition utterly
> precludes yours... So I'm curious. How is Id both eager and
> non-strict?
>
>> Wikipedia is bad at precision.
>
> If this is true, its accuracy is more to be bemoaned than its
> precision.
>
> Usually its clarity is worse. Especially in highly technical matters.
>
>> Concatenative languages do not have lambda abstractions, but you
>> can achieve a similar result by placing a function on the stack and
>> lifting values into it (e.g. via Factor's misnamed 'curry'
>> function; it really should be called 'partial-apply' or similar,
>> although that's not exactly right either).
>
> In a more general sense, what you're describing is passing a function
> _as_ data -- not data inside a function, but rather a function as
> data.

What I'm describing is passing a function as a data source.

I feel data should be treated like any other (possibly) shared asset.
That is, assign a semaphore to it in the guise of a function that is its
one and only evaluator. Laziness comes along for the ride...

don


>
>> - jn
>
> -Wm
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>

John Cowan — 2010-07-21 13:48:07

John Nowak scripsit:

> In an eager language without first-class functions, the language
> implementor can offer a special 'delay' form that creates a thunk
> at runtime. A 'force' function can later force the computation to
> occur. Since the only real semantic issue with introducing 'delay'
> is due to strictness (e.g. 'delay (4 / 0)' would not cause an error
> until forced even though '4 / 0' does immediately), it's a relatively
> uncomplicated addition from a theoretical perspective.

Note, however, that delay and force don't play well with tail recursion:
see SRFI 45 <http://srfi.schemers.org/srfi-45/srfi-45.html> for a
proposed resolution.

--
If I read "upcoming" in [the newspaper] John Cowan
once more, I will be downcoming http://www.ccil.org/~cowan
and somebody will be outgoing. cowan@...

pml060912 — 2010-07-22 15:55:59

--- In concatenative@yahoogroups.com, Don Groves <dgroves@...> wrote:
>
> On 20 Jul 2010, at 21:18, William Tanksley, Jr wrote:
>
> > Don Groves <dgroves@...> wrote:
.
.
.
> >> In a call-by-value environment, It seems to me that encapsulating
> >> all data in functions yields the same effect as call-by-reference. If
> >> a parameter is not needed, its evaluating function will not be
> >> called.
> >
> > This gets confusing to me, so hold on tight... :-)
> >
> > In order to pass a function to a procedure, the function has to be
> > available as data. If it's not, you can't pass it.
>
> I don't understand this -- I do it all the time in my C code by using
> the
> address operator, &, and the same is easily done in Forth, et al.

That's pretty much the same indirection approach I use in Furphy (see http://users.beagle.com.au/peterl/furphy.html), to achieve the same effect with a workaround even when what actually happens is done eagerly. I even describe it there with an analogy to what you are thinking of in C:-

Conditional code is mostly achieved by IF and a complementary pair of things, freezing and thawing. This is a bit like using indirection in C to pass parameters by name instead of the default by value. The analogy here is that IF does eager evaluation, and freezing and thawing change the behaviour to lazy evaluation. IF has this behaviour, using Forth documenting standards: ( true/false_flag true_option false_option --- result ).

FREEZE and THAW behave like an anonymous label (if that isn't a contradiction in terms - it really returns early and pushes a re-entry address to the stack) and a dynamic subroutine call, different from the Forth keyword EXECUTE that it resembles. Typically, you would put FREEZE at the beginning of each keyword that you might want to select with IF and then you would use THAW on whichever one got chosen, like this:-

FREEZE TRUE_STUFF TRUE_OPTION

FREEZE FALSE_STUFF FALSE_OPTION

GET_FLAG TRUE_OPTION FALSE_OPTION IF THAW DEMO

Later on I describe some syntactic sugar to make coding and using the indirection easier, too. P.M.Lawrence.

Don Groves — 2010-07-22 22:33:04

>
> .
>>>> In a call-by-value environment, It seems to me that encapsulating
>>>> all data in functions yields the same effect as call-by-
>>>> reference. If
>>>> a parameter is not needed, its evaluating function will not be
>>>> called.
>>>
>>> This gets confusing to me, so hold on tight... :-)
>>>
>>> In order to pass a function to a procedure, the function has to be
>>> available as data. If it's not, you can't pass it.
>>
>> I don't understand this -- I do it all the time in my C code by using
>> the
>> address operator, &, and the same is easily done in Forth, et al.
>
> That's pretty much the same indirection approach I use in Furphy
> (see http://users.beagle.com.au/peterl/furphy.html), to achieve the
> same effect with a workaround even when what actually happens is
> done eagerly. I even describe it there with an analogy to what you
> are thinking of in C:-
>
> Conditional code is mostly achieved by IF and a complementary pair
> of things, freezing and thawing. This is a bit like using
> indirection in C to pass parameters by name instead of the default
> by value. The analogy here is that IF does eager evaluation, and
> freezing and thawing change the behaviour to lazy evaluation. IF has
> this behaviour, using Forth documenting standards: ( true/
> false_flag true_option false_option --- result ).
>
> FREEZE and THAW behave like an anonymous label (if that isn't a
> contradiction in terms - it really returns early and pushes a re-
> entry address to the stack) and a dynamic subroutine call, different
> from the Forth keyword EXECUTE that it resembles. Typically, you
> would put FREEZE at the beginning of each keyword that you might
> want to select with IF and then you would use THAW on whichever one
> got chosen, like this:-
>
> FREEZE TRUE_STUFF TRUE_OPTION
>
> FREEZE FALSE_STUFF FALSE_OPTION
>
> GET_FLAG TRUE_OPTION FALSE_OPTION IF THAW DEMO
>
> Later on I describe some syntactic sugar to make coding and using
> the indirection easier, too. P.M.Lawrence.

Thanks. Do you feel your approach is equivalent to lazy evaluation?

don


> ------------------------------------
>
> Yahoo! Groups Links
>
>
>

pml060912 — 2010-07-23 09:56:10

--- In concatenative@yahoogroups.com, Don Groves <dgroves@...> wrote:
>
> >
> > .
> >>>> In a call-by-value environment, It seems to me that encapsulating
> >>>> all data in functions yields the same effect as call-by-
> >>>> reference. If
> >>>> a parameter is not needed, its evaluating function will not be
> >>>> called.
> >>>
> >>> This gets confusing to me, so hold on tight... :-)
> >>>
> >>> In order to pass a function to a procedure, the function has to be
> >>> available as data. If it's not, you can't pass it.
> >>
> >> I don't understand this -- I do it all the time in my C code by using
> >> the
> >> address operator, &, and the same is easily done in Forth, et al.
> >
> > That's pretty much the same indirection approach I use in Furphy
> > (see http://users.beagle.com.au/peterl/furphy.html), to achieve the
> > same effect with a workaround even when what actually happens is
> > done eagerly. I even describe it there with an analogy to what you
> > are thinking of in C:-
> >
> > Conditional code is mostly achieved by IF and a complementary pair
> > of things, freezing and thawing. This is a bit like using
> > indirection in C to pass parameters by name instead of the default
> > by value. The analogy here is that IF does eager evaluation, and
> > freezing and thawing change the behaviour to lazy evaluation. IF has
> > this behaviour, using Forth documenting standards: ( true/
> > false_flag true_option false_option --- result ).
> >
> > FREEZE and THAW behave like an anonymous label (if that isn't a
> > contradiction in terms - it really returns early and pushes a re-
> > entry address to the stack) and a dynamic subroutine call, different
> > from the Forth keyword EXECUTE that it resembles. Typically, you
> > would put FREEZE at the beginning of each keyword that you might
> > want to select with IF and then you would use THAW on whichever one
> > got chosen, like this:-
> >
> > FREEZE TRUE_STUFF TRUE_OPTION
> >
> > FREEZE FALSE_STUFF FALSE_OPTION
> >
> > GET_FLAG TRUE_OPTION FALSE_OPTION IF THAW DEMO
> >
> > Later on I describe some syntactic sugar to make coding and using
> > the indirection easier, too. P.M.Lawrence.
>
> Thanks. Do you feel your approach is equivalent to lazy evaluation?

We had that discussion earlier, I think actually on this thread. I feel it works out like this:-

- From the program's point of view, it is equivalent to lazy evaluation, because the behaviour it delivers is the same.

- From the programmer's point of view, it is not, because he or she has to do something explicit to achieve it. Before the syntactic sugar, quite a bit is needed to get things like recursion and loops, but even though that cuts it down it still doesn't eliminate it.

Taking all in all, I decided to document it as "change the behaviour to lazy evaluation", not to call it lazy evaluation as such. P.M.Lawrence.

Don Groves — 2010-07-23 21:29:37

On 23 Jul 2010, at 02:56, pml060912 wrote:

> --- In concatenative@yahoogroups.com, Don Groves <dgroves@...> wrote:
>>
>>>
>>> .
>>>>>> In a call-by-value environment, It seems to me that encapsulating
>>>>>> all data in functions yields the same effect as call-by-
>>>>>> reference. If
>>>>>> a parameter is not needed, its evaluating function will not be
>>>>>> called.
>>>>>
>>>>> This gets confusing to me, so hold on tight... :-)
>>>>>
>>>>> In order to pass a function to a procedure, the function has to be
>>>>> available as data. If it's not, you can't pass it.
>>>>
>>>> I don't understand this -- I do it all the time in my C code by
>>>> using
>>>> the
>>>> address operator, &, and the same is easily done in Forth, et al.
>>>
>>> That's pretty much the same indirection approach I use in Furphy
>>> (see http://users.beagle.com.au/peterl/furphy.html), to achieve the
>>> same effect with a workaround even when what actually happens is
>>> done eagerly. I even describe it there with an analogy to what you
>>> are thinking of in C:-
>>>
>>> Conditional code is mostly achieved by IF and a complementary pair
>>> of things, freezing and thawing. This is a bit like using
>>> indirection in C to pass parameters by name instead of the default
>>> by value. The analogy here is that IF does eager evaluation, and
>>> freezing and thawing change the behaviour to lazy evaluation. IF has
>>> this behaviour, using Forth documenting standards: ( true/
>>> false_flag true_option false_option --- result ).
>>>
>>> FREEZE and THAW behave like an anonymous label (if that isn't a
>>> contradiction in terms - it really returns early and pushes a re-
>>> entry address to the stack) and a dynamic subroutine call, different
>>> from the Forth keyword EXECUTE that it resembles. Typically, you
>>> would put FREEZE at the beginning of each keyword that you might
>>> want to select with IF and then you would use THAW on whichever one
>>> got chosen, like this:-
>>>
>>> FREEZE TRUE_STUFF TRUE_OPTION
>>>
>>> FREEZE FALSE_STUFF FALSE_OPTION
>>>
>>> GET_FLAG TRUE_OPTION FALSE_OPTION IF THAW DEMO
>>>
>>> Later on I describe some syntactic sugar to make coding and using
>>> the indirection easier, too. P.M.Lawrence.
>>
>> Thanks. Do you feel your approach is equivalent to lazy evaluation?
>
> We had that discussion earlier, I think actually on this thread. I
> feel it works out like this:-
>
> - From the program's point of view, it is equivalent to lazy
> evaluation, because the behaviour it delivers is the same.
>
> - From the programmer's point of view, it is not, because he or she
> has to do something explicit to achieve it. Before the syntactic
> sugar, quite a bit is needed to get things like recursion and loops,
> but even though that cuts it down it still doesn't eliminate it.
>
> Taking all in all, I decided to document it as "change the behaviour
> to lazy evaluation", not to call it lazy evaluation as such.
> P.M.Lawrence.
>

But if that behavior is built into the language, why does an
application programmer have to do anything explicit?

don


> ------------------------------------
>
> Yahoo! Groups Links
>
>
>

pml060912 — 2010-07-24 11:04:51

--- In concatenative@yahoogroups.com, Don Groves <dgroves@...> wrote:
>
> On 23 Jul 2010, at 02:56, pml060912 wrote:
>
> > --- In concatenative@yahoogroups.com, Don Groves <dgroves@> wrote:
.
.
.
> >> Thanks. Do you feel your approach is equivalent to lazy evaluation?
> >
> > We had that discussion earlier, I think actually on this thread. I
> > feel it works out like this:-
> >
> > - From the program's point of view, it is equivalent to lazy
> > evaluation, because the behaviour it delivers is the same.
> >
> > - From the programmer's point of view, it is not, because he or she
> > has to do something explicit to achieve it. Before the syntactic
> > sugar, quite a bit is needed to get things like recursion and loops,
> > but even though that cuts it down it still doesn't eliminate it.
> >
> > Taking all in all, I decided to document it as "change the behaviour
> > to lazy evaluation", not to call it lazy evaluation as such.
> > P.M.Lawrence.
> >
>
> But if that behavior is built into the language, why does an
> application programmer have to do anything explicit?

It's not built into the language, in that sense, any more than the fact that a gear lever and clutch pedal in a car make it have an automatic transmission. Rather, there are keywords the programmer can use to get the indirection that gives lazy behaviour to the code built with them even though those indirection keywords work eagerly - just as the car can change gears, but only when the driver works the controls for that. It's not enough to code branches with IF (say), the programmer also needs to put FREEZE into keywords for each branch (or use the [ ] pair in inline code) and then use THAW to make use of whatever was frozen (maybe with some of that hidden in yet other keywords). IF by itself only delivers eager behaviour. The programmer has to make explicit use of the indirection to achieve lazy behaviour, since the default is eager. P.M.Lawrence.

Don Groves — 2010-07-24 21:01:55

On 24 Jul 2010, at 04:04, pml060912 wrote:

> --- In concatenative@yahoogroups.com, Don Groves <dgroves@...> wrote:
>>
>> On 23 Jul 2010, at 02:56, pml060912 wrote:
>>
>>> --- In concatenative@yahoogroups.com, Don Groves <dgroves@> wrote:
> .
> .
> .
>>>> Thanks. Do you feel your approach is equivalent to lazy evaluation?
>>>
>>> We had that discussion earlier, I think actually on this thread. I
>>> feel it works out like this:-
>>>
>>> - From the program's point of view, it is equivalent to lazy
>>> evaluation, because the behaviour it delivers is the same.
>>>
>>> - From the programmer's point of view, it is not, because he or she
>>> has to do something explicit to achieve it. Before the syntactic
>>> sugar, quite a bit is needed to get things like recursion and loops,
>>> but even though that cuts it down it still doesn't eliminate it.
>>>
>>> Taking all in all, I decided to document it as "change the behaviour
>>> to lazy evaluation", not to call it lazy evaluation as such.
>>> P.M.Lawrence.
>>>
>>
>> But if that behavior is built into the language, why does an
>> application programmer have to do anything explicit?
>
> It's not built into the language, in that sense, any more than the
> fact that a gear lever and clutch pedal in a car make it have an
> automatic transmission. Rather, there are keywords the programmer
> can use to get the indirection that gives lazy behaviour to the code
> built with them even though those indirection keywords work eagerly
> - just as the car can change gears, but only when the driver works
> the controls for that. It's not enough to code branches with IF
> (say), the programmer also needs to put FREEZE into keywords for
> each branch (or use the [ ] pair in inline code) and then use THAW
> to make use of whatever was frozen (maybe with some of that hidden
> in yet other keywords). IF by itself only delivers eager behaviour.
> The programmer has to make explicit use of the indirection to
> achieve lazy behaviour, since the default is eager. P.M.Lawrence.

Now I've got it, thanks.

don

>
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>

William Tanksley, Jr — 2010-07-25 14:45:53

John Nowak <john@...> wrote:
> You'd still need some way to parameterize functions
> by other functions even if functions aren't available as
> data at runtime. OBJ is an excellent example of how
> to do parameterized programming without first-class
> functions.

Obviously, OBJ was invented long before Google searches :-). It took
me a long time to find a link that actually had anything to do with
the language. A better search string is "OBJ3". Unfortunately, I'm
still clueless; none of the available references explain what you
meant.

I'm curious... How is OBJ an excellent example of that? It sounds like
a useful property!

> - jn

-Wm

John Nowak — 2010-07-25 22:29:14

On 2010.07.25, at 10:45 AM, William Tanksley, Jr wrote:

> John Nowak <john@...> wrote:
>
>> You'd still need some way to parameterize functions
>> by other functions even if functions aren't available as
>> data at runtime. OBJ is an excellent example of how
>> to do parameterized programming without first-class
>> functions.
>
> I'm curious... How is OBJ an excellent example of that? It sounds like
> a useful property!

It's just parameterized modules a la Ada or ML. The values of the parameters can be resolved statically; module instantiation essentially happens before the program is run.

This should help:
http://cseweb.ucsd.edu/~goguen/pps/utyop.ps

- jn