sweetening concatenative syntax

View: New views
20 Messages — Rating Filter:   Alert me  
< Prev | 1 - 2 - 3 - 4 - 5 | Next >

sweetening concatenative syntax

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I've had some thoughts recently on syntactic additions to  
concatenative languages that seem potentially quite useful. At the  
very least, they would put an end to silly discussions over largely  
imagined deficiencies of postfix syntax and pointfree programming.  
This is perhaps crucial if we expect any significant uptake of  
concatenative languages.

Below are the four extensions that I'm proposing. I'm strongly  
considering one or more in the language I'm currently working on  
(named "Fifth" for what are likely obvious reasons). The first two  
should be fairly uncontroversial. The latter two are simple  
translations I've not seen discussed before that seem potentially very  
useful in practice.

I. Lambda Expressions

Assuming functions have the syntax '[<body>]', it seems useful to  
introduce the syntax '[<bindings> -> <body>], where bindings are  
ordered with the top of the stack towards the right as is familiar.  
For example, the following two functions are equivalent:

     foo :: -> [a b -> b a a]
     foo = [swap dup]
     foo = [a b -> b a a]

Note that the type syntax mimics the lambda expression syntax (or vice  
versa if you prefer). The type notation is similar to Cat's with minor  
alterations for the purposes of brevity. In particular, scalar  
variables are all lowercase, row variables are all uppercase, and  
concrete types are title-case and of at least two characters (to avoid  
confusion with row variables).

Variables should, of course, be lexically scoped. A translation of  
expressions with lambdas to efficient pointfree stack-based code is  
not especially difficult. This is the case regardless of if the  
language is linear (which introduces only small complications), typed,  
or "lacking" a retain stack. Of course, I elide the translation here  
for the sake of... space.

II. Local Function Variables

If you permit lambdas, it makes sense to offer a simple syntax for  
declaring functions with named variables. This is similar to how  
Scheme offers '(define (foo <bindings>) <body>)' to mean '(define foo  
(lambda (<bindings>) <body>))'. The declaration syntax proposed here  
mimics use. In particular, the name of the function being defined  
occurs *after* the arguments.

The following two statements are equivalent (where 'i' is the identity  
combinator with the type 'A (A -> B) -> B'):

     a b foo = b a a
     foo = [a b -> b a a] i

Here's another example in which 'unlist' is a deconstructor for a  
list; the first function provided handled the null case and the second  
handles the cons case:

     unlist :: A [b] (A -> C) (A b [b] -> C) -> C

     map :: [a] (a -> b) -> [b]
     list f map = list [] [x xs -> x f i xs f map cons] unlist

It should be noted that both lambda expressions and functions declared  
with the local variable syntax may use more items on the stack than  
they explicitly bind. For example, the following definition of map is  
equivalent to the above and likely preferable in practice:

     f map = [] [x xs -> x f i xs f map cons] unlist

III. Prefix Notation

It occurred to me recently that there exists a simple syntactic  
translation from prefix code (a la Scheme), where '(foo a b c)' is the  
application of 'foo' to three arguments, to postfix code. This  
translation can be done without any type information. The translation  
consists of simply removing the parenthesis and placing the first  
element at the end. If that first element is a local binding or a  
lambda expression, an 'i' is prepended to cause it to be evaluated.  
Here are some simple translation examples:

     (cons 1 null) => 1 null cons
     (+ (- 1 2) 3) => 1 2 - 3 +
     [f -> (f 10)] => [f -> 10 f i]
     f foo = (f 5) => f foo = 5 f i

Interestingly, you get partial application for free:

     double = (* 2)

One thing that may seem like a problem is that '(1 2 3)' will be  
translated to '(2 3 1)' when really an error should occur as '1' is  
not a function. However, that's not the case: '1' *is* a function.  
More precisely, it is a row polymorphic function that consumes a stack  
and yields a new stack (1 :: A -> A int). As such, the above syntactic  
translation is completely valid and it would be wrong to reject it.

Prefix notation may seem like a curiosity at first, perhaps existing  
only to lure over those familiar with prefix notation. However, I'd  
argue that prefix notation makes certain code objectively easier to  
read. This is especially the case when dealing with deconstructors  
like 'if' or 'unlist' as the higher order function of primary interest  
is "announced" earlier in the definition. In the following example,  
both functions are equivalent:

     f map = [] [x xs -> x f i xs f map cons] unlist

     f map =
       (unlist []
               [x xs -> x f i xs f map cons])

(Perhaps this is not the best example. Substitute a better one with  
nested ifs.)

Prefix notation also has the benefit that you can express "nested"  
code (as above) in a way that allows editors to automatically intent  
properly. This is not at all the case with purely postfix code where  
type information and perhaps some heuristics would need to be involved  
to produce acceptable indentation.

Here's a rewriting of map using *only* prefix notation in a way that  
most any Scheme or Haskell programmer would find comfortable:

     f map =
       (unlist []
               [x xs -> (cons (f x) (map xs f))])

IV. Indentation

Prefix notation is often criticized for being overly verbose or noisy.  
The Python solution is to use indentation instead of paired  
delimiters, but this introduces a number of problems: It's harder for  
machines to generate valid code, pasting between sources can be messy,  
editors can't really automatically re-indent code, plenty of people  
(myself included) hate it, etc.

The correct way to address this problem (if you are to address it at  
all) is to do what Haskell does and offer *optional* indentation-
sensitive syntax that gets translated to the more explicit form. This  
can be implemented as a relatively trivial pre-parser pass and does  
not require modification of the parser itself.

Indentation-sensitive syntax is introduced with a colon. Here are some  
simple examples showing how indentation-sensitive code is translated  
to prefix code which is then translated to "normal" code:

     a b c: d e f  =>  a b (c d e f)        =>  a b d e f c

     a b c:
       d e f:      =>  a b (c d e (f g h))  =>  a b d e g h f c
         g h

Finally, here again is our map example, this time even spiffier:

     f map = unlist:
       []
       [x xs -> cons: (f x) (map xs f)]

This looks quite close to the Haskell version, except that our  
programs are, after translation, still expressed only in terms of the  
functional forms of composition and quotation! (I was wrong in an  
earlier email when I started that composition is the only functional  
form in Joy. Quotation is also a functional form.) This definition is  
arguably easier to read than the postfix, pointfree alternative, as it  
clearly announces at the start that 'map' is a function that  
destructures a list, details the result of the destructization at the  
start of the relevant line, and indicates directly after that the  
result of the function will be a list via the prefixing of 'cons'.

- John

Re: sweetening concatenative syntax

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Minor issues with the previous email:

1. I fell back to a Cat-like syntax for type definitions. Here are the  
correct notations for 'i', 'map', and 'unlist':

     i      :: A [A -> B] -> B
     map    :: (List a) [a -> b] -> (List b)
     unlist :: A (List b) [A -> C] [A b (List b) -> C] -> C

Arguably there should be a type abbreviation for '(List a)'; perhaps  
'{a}'.

2. The phrase "allows editors to automatically intent" should read  
"allows editors to automatically INDENT".

3. By "it's harder for machines to generate valid code", I mean that  
it is harder to generate *source* code when compiling to a language  
with indentation-sensitive syntax. Explicit paired delimiters are  
easier to deal with.

4. It's too long. Unfortunately, I don't have the time to write a  
shorter version.

Re: sweetening concatenative syntax

by LittleDan :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

>  I. Lambda Expressions
>  II. Local Function Variables

Sure, these two seem reasonable seems reasonable. Factor and Cat
support these. The only thing I have to say is, they shouldn't be used
too often or for everything, since they can make code unnecessarily
more verbose and less factorable.

>  III. Prefix Notation
>  Prefix notation may seem like a curiosity at first, perhaps existing
>  only to lure over those familiar with prefix notation. However, I'd
>  argue that prefix notation makes certain code objectively easier to
>  read. This is especially the case when dealing with deconstructors
>  like 'if' or 'unlist' as the higher order function of primary interest
>  is "announced" earlier in the definition. In the following example,
>  both functions are equivalent:
>
>  f map = [] [x xs -> x f i xs f map cons] unlist
>
>  f map =
>  (unlist []
>  [x xs -> x f i xs f map cons])
>
>  (Perhaps this is not the best example. Substitute a better one with
>  nested ifs.)
>
>  Prefix notation also has the benefit that you can express "nested"
>  code (as above) in a way that allows editors to automatically intent
>  properly. This is not at all the case with purely postfix code where
>  type information and perhaps some heuristics would need to be involved
>  to produce acceptable indentation.
>
>  Here's a rewriting of map using *only* prefix notation in a way that
>  most any Scheme or Haskell programmer would find comfortable:
>
>  f map =
>  (unlist []
>  [x xs -> (cons (f x) (map xs f))])
>
Now this I have a very hard time understanding the motivation for.
Sure, when you're just learning concatenative languages, it can be a
little weird that "if" comes after the quotations, but humans don't
read completely linearly in normal circumstances, and it doesn't take
too much effort to look to the end. In a case where you have to look a
page ahead, I'd say the fault lies with the author of the code writing
things unreadably, rather than the language being unreadable. Also,
what happens if a function is given too many arguments? The result is
very counterintuitive, and a more useful translation would take into
account type information.

>  IV. Indentation
>
>  f map = unlist:
>  []
>  [x xs -> cons: (f x) (map xs f)]

I don't completely get it. This example doesn't take advantage of
indentation, it only uses modified prefix syntax. I guess the
indentation can make a difference if you have multiple arguments
following the word, and then something else after that that's not an
argument?

Sure, there are lots of translations that we could make efficiently
and accurately with concatenative languages, but I'm not sure if all
of these are essential or beneficial to the nature of the languages.
They make things more complicated and they remove all of the nice
properties of concatenativity, which is useful in building software
because it helps you factor code. We could easily create a translation
from Scheme to a concatenative language, but that doesn't mean that
we'd want to do all of our concatenative programming on a Scheme skin.

Re: sweetening concatenative syntax

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Mar 5, 2008, at 9:14 AM, Daniel Ehrenberg wrote:

> Sure, there are lots of translations that we could make efficiently
> and accurately with concatenative languages, but I'm not sure if all
> of these are essential or beneficial to the nature of the languages.
> They make things more complicated and they remove all of the nice
> properties of concatenativity, which is useful in building software
> because it helps you factor code.

I'm not convinced concatenativity offers a huge advantage in  
factoring. It's easy to write code in such a way that useful chunks  
can't be pulled out. It's also difficult to find useful abstractions  
unless you have some experience. For example, I'd guess the novice  
programmer looking to add two lists together wouldn't accidently write  
zip-with.

I think how factorable a language is primarily depends on its  
usefulness in building abstractions rather than any particular syntax.  
Languages with first class functions offer factorability that language  
like Forth don't offer. Haskell's type classes over a level of safe  
and structured factorability that's inaccessible to Joy. ML's functors  
offer a means of factoring I often miss when using Haskell. Types  
offer a way of verifying my abstractions in a way I miss when using  
Scheme. Scheme offers macros as a means of factoring that I miss when  
using Haskell. Et cetera.

If a syntax that is non-concatenative makes it any easier to use any  
of the other means of factoring, then it seems to me it's worth  
considering.

- John

Re: sweetening concatenative syntax

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Mar 5, 2008, at 3:14 PM, John Nowak wrote:

> I think how factorable a language is primarily depends on its
> usefulness in building abstractions rather than any particular syntax.
> Languages with first class functions offer factorability that language
> like Forth don't offer. Haskell's type classes over a level of safe
> and structured factorability that's inaccessible to Joy. ML's functors
> offer a means of factoring I often miss when using Haskell. Types
> offer a way of verifying my abstractions in a way I miss when using
> Scheme. Scheme offers macros as a means of factoring that I miss when
> using Haskell. Et cetera.

I forgot an obvious one: Joy's stack-based nature, which makes it  
possible to return multiple values from a function and then easily  
consume them in another, opens up many possibilities for factoring  
that I often miss when using other languages.

- John

Re: sweetening concatenative syntax

by stevan apter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@...>
Sent: Wednesday, March 05, 2008 3:39 PM
Subject: Re: [stack] sweetening concatenative syntax


> On Mar 5, 2008, at 3:14 PM, John Nowak wrote:
>
>> I think how factorable a language is primarily depends on its
>> usefulness in building abstractions rather than any particular syntax.
>> Languages with first class functions offer factorability that language
>> like Forth don't offer. Haskell's type classes over a level of safe
>> and structured factorability that's inaccessible to Joy. ML's functors
>> offer a means of factoring I often miss when using Haskell. Types
>> offer a way of verifying my abstractions in a way I miss when using
>> Scheme. Scheme offers macros as a means of factoring that I miss when
>> using Haskell. Et cetera.

temperament, taste, and further imponderable subjectivities.  perhaps a
useful distinction to make is between monoglot and polyglot programmers.
i'm an extreme case of the former, and you sound like you belong way
over on the other edge.  given the decision to code in a concatenative
language, i want my concatenativity neat, straight no chaser.  the
sharp, clear outlines of the basic syntax are no small part of what i
like about joy.  in contrast to dan, i think introducing variables in
any form should be resisted (although i've implemented both in my toy
languages, just for the heck of it.)  moreover, i agree with something
dr. tanksley has often said:  we don't yet know all the properties of
these languages in their pure form, since no one (as far as i can tell
from years on this list) has written any large-scale, multi-programmer,
long-lived applications in any concatenative language.  many small
examples, much tinkering under the hood, but nothing approaching the
scale of, say, ebay.
 
>
> I forgot an obvious one: Joy's stack-based nature, which makes it  
> possible to return multiple values from a function and then easily  
> consume them in another, opens up many possibilities for factoring  
> that I often miss when using other languages.
>
> - John
>

Parent Message unknown Re: sweetening concatenative syntax

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Mar 5, 2008, at 5:26 PM, Stevan Apter wrote:

> temperament, taste, and further imponderable subjectivities.  perhaps a
> useful distinction to make is between monoglot and polyglot programmers.
> i'm an extreme case of the former, and you sound like you belong way
> over on the other edge.

Heh, well, I'm largely playing devil's advocate here. My personal opinions
towards this sort of thing are largely the same as your own. I generally
consider myself a Scheme/Joy-pushing monoglot programmer. I do however
have a growing, nasty, type system addiction.

Still, I think there may be some value in permitting these things because
they can be completely translated away. If you don't want to use them,
then don't. No harm done. I think.

- John


Re: sweetening concatenative syntax

by Don Groves :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi, John --

Where can we read about Fifth? Several years ago, someone
announced a Fifth language, is yours a continuation of that effort,
or was that you?
--
don


On Mar 5, 2008, at 19:04 , John Nowak wrote:

> I've had some thoughts recently on syntactic additions to
> concatenative languages that seem potentially quite useful. At the
> very least, they would put an end to silly discussions over largely
> imagined deficiencies of postfix syntax and pointfree programming.
> This is perhaps crucial if we expect any significant uptake of
> concatenative languages.
>
> Below are the four extensions that I'm proposing. I'm strongly
> considering one or more in the language I'm currently working on
> (named "Fifth" for what are likely obvious reasons). The first two
> should be fairly uncontroversial. The latter two are simple
> translations I've not seen discussed before that seem potentially very
> useful in practice.
>
> I. Lambda Expressions
>
> Assuming functions have the syntax '[<body>]', it seems useful to
> introduce the syntax '[<bindings> -> <body>], where bindings are
> ordered with the top of the stack towards the right as is familiar.
> For example, the following two functions are equivalent:
>
>      foo :: -> [a b -> b a a]
>      foo = [swap dup]
>      foo = [a b -> b a a]
>
> Note that the type syntax mimics the lambda expression syntax (or vice
> versa if you prefer). The type notation is similar to Cat's with minor
> alterations for the purposes of brevity. In particular, scalar
> variables are all lowercase, row variables are all uppercase, and
> concrete types are title-case and of at least two characters (to avoid
> confusion with row variables).
>
> Variables should, of course, be lexically scoped. A translation of
> expressions with lambdas to efficient pointfree stack-based code is
> not especially difficult. This is the case regardless of if the
> language is linear (which introduces only small complications), typed,
> or "lacking" a retain stack. Of course, I elide the translation here
> for the sake of... space.
>
> II. Local Function Variables
>
> If you permit lambdas, it makes sense to offer a simple syntax for
> declaring functions with named variables. This is similar to how
> Scheme offers '(define (foo <bindings>) <body>)' to mean '(define foo
> (lambda (<bindings>) <body>))'. The declaration syntax proposed here
> mimics use. In particular, the name of the function being defined
> occurs *after* the arguments.
>
> The following two statements are equivalent (where 'i' is the identity
> combinator with the type 'A (A -> B) -> B'):
>
>      a b foo = b a a
>      foo = [a b -> b a a] i
>
> Here's another example in which 'unlist' is a deconstructor for a
> list; the first function provided handled the null case and the second
> handles the cons case:
>
>      unlist :: A [b] (A -> C) (A b [b] -> C) -> C
>
>      map :: [a] (a -> b) -> [b]
>      list f map = list [] [x xs -> x f i xs f map cons] unlist
>
> It should be noted that both lambda expressions and functions declared
> with the local variable syntax may use more items on the stack than
> they explicitly bind. For example, the following definition of map is
> equivalent to the above and likely preferable in practice:
>
>      f map = [] [x xs -> x f i xs f map cons] unlist
>
> III. Prefix Notation
>
> It occurred to me recently that there exists a simple syntactic
> translation from prefix code (a la Scheme), where '(foo a b c)' is the
> application of 'foo' to three arguments, to postfix code. This
> translation can be done without any type information. The translation
> consists of simply removing the parenthesis and placing the first
> element at the end. If that first element is a local binding or a
> lambda expression, an 'i' is prepended to cause it to be evaluated.
> Here are some simple translation examples:
>
>      (cons 1 null) => 1 null cons
>      (+ (- 1 2) 3) => 1 2 - 3 +
>      [f -> (f 10)] => [f -> 10 f i]
>      f foo = (f 5) => f foo = 5 f i
>
> Interestingly, you get partial application for free:
>
>      double = (* 2)
>
> One thing that may seem like a problem is that '(1 2 3)' will be
> translated to '(2 3 1)' when really an error should occur as '1' is
> not a function. However, that's not the case: '1' *is* a function.
> More precisely, it is a row polymorphic function that consumes a stack
> and yields a new stack (1 :: A -> A int). As such, the above syntactic
> translation is completely valid and it would be wrong to reject it.
>
> Prefix notation may seem like a curiosity at first, perhaps existing
> only to lure over those familiar with prefix notation. However, I'd
> argue that prefix notation makes certain code objectively easier to
> read. This is especially the case when dealing with deconstructors
> like 'if' or 'unlist' as the higher order function of primary interest
> is "announced" earlier in the definition. In the following example,
> both functions are equivalent:
>
>      f map = [] [x xs -> x f i xs f map cons] unlist
>
>      f map =
>        (unlist []
>                [x xs -> x f i xs f map cons])
>
> (Perhaps this is not the best example. Substitute a better one with
> nested ifs.)
>
> Prefix notation also has the benefit that you can express "nested"
> code (as above) in a way that allows editors to automatically intent
> properly. This is not at all the case with purely postfix code where
> type information and perhaps some heuristics would need to be involved
> to produce acceptable indentation.
>
> Here's a rewriting of map using *only* prefix notation in a way that
> most any Scheme or Haskell programmer would find comfortable:
>
>      f map =
>        (unlist []
>                [x xs -> (cons (f x) (map xs f))])
>
> IV. Indentation
>
> Prefix notation is often criticized for being overly verbose or noisy.
> The Python solution is to use indentation instead of paired
> delimiters, but this introduces a number of problems: It's harder for
> machines to generate valid code, pasting between sources can be messy,
> editors can't really automatically re-indent code, plenty of people
> (myself included) hate it, etc.
>
> The correct way to address this problem (if you are to address it at
> all) is to do what Haskell does and offer *optional* indentation-
> sensitive syntax that gets translated to the more explicit form. This
> can be implemented as a relatively trivial pre-parser pass and does
> not require modification of the parser itself.
>
> Indentation-sensitive syntax is introduced with a colon. Here are some
> simple examples showing how indentation-sensitive code is translated
> to prefix code which is then translated to "normal" code:
>
>      a b c: d e f  =>  a b (c d e f)        =>  a b d e f c
>
>      a b c:
>        d e f:      =>  a b (c d e (f g h))  =>  a b d e g h f c
>          g h
>
> Finally, here again is our map example, this time even spiffier:
>
>      f map = unlist:
>        []
>        [x xs -> cons: (f x) (map xs f)]
>
> This looks quite close to the Haskell version, except that our
> programs are, after translation, still expressed only in terms of the
> functional forms of composition and quotation! (I was wrong in an
> earlier email when I started that composition is the only functional
> form in Joy. Quotation is also a functional form.) This definition is
> arguably easier to read than the postfix, pointfree alternative, as it
> clearly announces at the start that 'map' is a function that
> destructures a list, details the result of the destructization at the
> start of the relevant line, and indicates directly after that the
> result of the function will be a list via the prefixing of 'cons'.
>
> - John
>
>
>
> Yahoo! Groups Links
>
>
>
>


Re: sweetening concatenative syntax

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Mar 5, 2008, at 5:26 PM, Stevan Apter wrote:

> temperament, taste, and further imponderable subjectivities.

Well, perhaps this is worth testing then. Here's how I'm suggesting one
could write a non-tail-recursive 'map' in terms of 'cons' and 'unlist':

f map = unlist:
  []
  [x xs -> (f x) (map xs f) cons]

> i want my concatenativity neat, straight no chaser.

How would you personally write this in a purely concatenative form? No
cheating by using combinators like 'fold'; you only get to use 'cons' (a
{a} -> {a}) and 'unlist' (A {b} [A -> C] [A b {b} -> C] -> C).

- John


Re: sweetening concatenative syntax

by stevan apter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


----- Original Message -----
From: <john@...>
To: <concatenative@...>
Sent: Wednesday, March 05, 2008 9:03 PM
Subject: Re: [stack] sweetening concatenative syntax


> On Mar 5, 2008, at 5:26 PM, Stevan Apter wrote:
>
>> temperament, taste, and further imponderable subjectivities.
>
> Well, perhaps this is worth testing then. Here's how I'm suggesting one
> could write a non-tail-recursive 'map' in terms of 'cons' and 'unlist':
>
> f map = unlist:
>  []
>  [x xs -> (f x) (map xs f) cons]
>
>> i want my concatenativity neat, straight no chaser.
>
> How would you personally write this in a purely concatenative form? No
> cheating by using combinators like 'fold'; you only get to use 'cons' (a
> {a} -> {a}) and 'unlist' (A {b} [A -> C] [A b {b} -> C] -> C).

my joy is so rusty these days.  i'm on the wagon.

in the g variant of my f language:

[[dup!count![]top!]dip!swap!
 [[uncons!]dip!dup!top!
  [cons!unit!. first!swons!]dipd!]
 do!pop!pop!|]                          map             "[..][f]map!"

and in XY:

; map [jump] [each* rot => map <= swons] [pop] ifte ;
; each* => uncons <= tuck 2slip ;

in both languages, all words are bottom out in symbolic primitives.  six or
seven for XY, rather more for g.

details here:  www.nsl.com.

>
> - John
>
>

Re: sweetening concatenative syntax

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Mar 5, 2008, at 8:34 PM, Don Groves wrote:

> Where can we read about Fifth?

At 5th.org, in a few weeks from now.

> Several years ago, someone
> announced a Fifth language, is yours a continuation of that effort,
> or was that you?

No, it wasn't me, nor is this a continuation of anything.

- John

Re: sweetening concatenative syntax

by Don Groves :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Mar 5, 2008, at 19:52 , John Nowak wrote:

>
> On Mar 5, 2008, at 8:34 PM, Don Groves wrote:
>
>> Where can we read about Fifth?
>
> At 5th.org, in a few weeks from now.

Thanks!


>
>> Several years ago, someone
>> announced a Fifth language, is yours a continuation of that effort,
>> or was that you?
>
> No, it wasn't me, nor is this a continuation of anything.

I just figured that out. My references to Fifth are 20 years old ;-)
--
don



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


Re: sweetening concatenative syntax

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Mar 5, 2008, at 9:34 PM, Stevan Apter wrote:

> ; map [jump] [each* rot => map <= swons] [pop] ifte ;
> ; each* => uncons <= tuck 2slip ;

So that, versus this:

f map = unlist:
   []
   [x xs -> (f x) (map xs f) cons]

It seems to me that the second version is objectively easier to  
understand. It's clear that 'map' destructures a list and it's clear  
how both possibilities (the null list and the non-null list) are  
handled. Even if we drop the prefix/indent syntax, it's still a good  
deal clearer:

f map = []
         [x xs -> x f i xs f map cons]
       unlist

Actually, I like that better.

At the very least, it seems that there's a strong argument for  
offering lambda expressions and a locals syntax.

It's less clear that the prefix syntax is worthwhile. In particular,  
point Dan made is valid:

> Also, what happens if a function is given too many arguments? The
> result is very counterintuitive, and a more useful translation would
> take into account type information.

I'd be strongly opposed to any translation that was not a purely  
syntactic preprocessing step as otherwise you truly are complicating  
the language. At the moment, my type system can't enforce that a given  
quotation isn't given "too many" arguments because this makes no  
sense; all functions operate on stacks. The type system can enforce  
that a given quotation will try to use no more than a given number of  
arguments when it is later evaluated, and this is critical for typing  
combinators like 'infra', but that isn't helpful here. It is therefore  
probably best to rule out prefix syntax.

- John

Re: sweetening concatenative syntax

by William Tanksley, Jr :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

John Nowak <john@...> wrote:
>  Daniel Ehrenberg wrote:
>  > Sure, there are lots of translations that we could make efficiently
>  > and accurately with concatenative languages, but I'm not sure if all
>  > of these are essential or beneficial to the nature of the languages.
>  > They make things more complicated and they remove all of the nice
>  > properties of concatenativity, which is useful in building software
>  > because it helps you factor code.

>  I'm not convinced concatenativity offers a huge advantage in
>  factoring.

Sure it does! The referential transparency alone is worth the price of
admission -- the famous "extract method" refactoring (considered by
many to be the first indication of "true" refactoring support in a
tool) is merely a cut-and-paste in any editor.

> It's easy to write code in such a way that useful chunks
>  can't be pulled out. It's also difficult to find useful abstractions
>  unless you have some experience. For example, I'd guess the novice
>  programmer looking to add two lists together wouldn't accidently write
>  zip-with.

You're totally right, but you're talking about a different subject.
The experienced programmer isn't going to implement zip-with either;
the experienced programmer knows it's already implemented. The
experienced programmer is going to implement something that hasn't
been done before, and notice that there's some redundancy, and if the
language allows, will factor it out. If the language makes it hard,
he'll tolerate a lot more redundancy.

An really good programmer will also notice *almost* redundancies --
places where the code would be the same if only things were a little
bit different. _That_ is the point that leads to the creation of truly
reusable tools like zip-with -- not a magic knowledge of what sort of
things will be reusable, but rather a repeated creation of the same
sort of thing, and progressive refinement of that thing until it can
actually be used in all of those cases.

A good library is rarely produced directly from pure theory. Usually
it's produced by painstaking experiments -- although if it's not
backed by a good clean conceptual model it's a bad library, and
experience won't save it.

>  I think how factorable a language is primarily depends on its
>  usefulness in building abstractions rather than any particular syntax.
>  Languages with first class functions offer factorability that language
>  like Forth don't offer.

100% agree, by the way. The problem of factoring can be addressed in
many dimensions.

>  If a syntax that is non-concatenative makes it any easier to use any
>  of the other means of factoring, then it seems to me it's worth
>  considering.

At this point in our understanding of concatenative languages, this is
a pure cop-out. Yes, we don't have a concatenative Haskell yet -- but
give us time.

>  - John

-Wm

Re: sweetening concatenative syntax

by William Tanksley, Jr :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Stevan Apter <sa@...> wrote:
> given the decision to code in a concatenative
>  language, i want my concatenativity neat, straight no chaser.

Heh. I'm the same way -- of course. That's a little bit of the
language researcher speaking rather than the true polyglot programmer,
though. Like the chemist that I was trained as, I'm always wondering
what would happen if I made a slightly purer extract. Well, I guess I
skip the step of blowing the resulting extract up in my face -- that's
the fun part of chemistry.

>  like about joy.  in contrast to dan, i think introducing variables in
>  any form should be resisted (although i've implemented both in my toy
>  languages, just for the heck of it.)

Yup.

> moreover, i agree with something dr. tanksley has often said:

Thank you, but I'm just Mr. Tanksley so far. I'm just starting on a
Master's degree, so perhaps you'll be able to address me as "dread
lord tanksley" in a while. (Is that the correct form of address?)

> we don't yet know all the properties of
>  these languages in their pure form, since no one (as far as i can tell
>  from years on this list) has written any large-scale, multi-programmer,
>  long-lived applications in any concatenative language.  many small
>  examples, much tinkering under the hood, but nothing approaching the
>  scale of, say, ebay.

I'm not so sure about that. Forth has been used in enormous
applications; see http://www.forth.com/resources/appNotes/index.html
(and that's just one Forth company). Postscript forms one of the
largest systems ever built (just about every computer is able to hook
up to just about every Postscript printer almost right out of the
box); it's not an integrated or monolithic system like eBay, but it's
phenomenally successful at what it's designed to do.

-Wm

Re: sweetening concatenative syntax

by William Tanksley, Jr :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

John Nowak <john@...> wrote:
> Stevan Apter