function/object ambiguity + quotation alternative
John Nowak — 2009-02-26 14:34:38
I've hinted at this recently, but never properly explained it. It may
be entirely uninteresting or even an incorrect observation, so I'd
appreciate a whack on the hand if I'm off track. I think, however,
that it may be important in letting us further develop a firm
theoretical basis for concatenative languages.
In a concatenative language, all terms denote functions. I don't think
this is too contentious to say. For example, '42' denotes the function
that pushes the value 42 onto a stack. '[foo bar]' is a function that
pushes the quotation/list [foo bar] onto the stack. Et cetera.
This seem to be a unique property. I have never seen a non-
concatenative language in which every term denoted a function.
John Backus invented the term "function-level programming", of which
his language FP is the canonical example. In FP however, the term '42'
represents an object. You use the combining form '~', aka "constant",
to produce a function that returns the object 42 when passed any
value, e.g. '~42'.
Assume application is written ":", composition is written ".", a list
is written in angle brackets, and construction is written with square
brackets. Given this, the semantics of 'dup' in FP are as follows:
dup:X -> <X, X>
dup.~X == [~X, ~X]
The former gives the semantics of applying 'dup' to some object X. The
latter states an equivalence between composing 'dup' with the constant
function '~X' and construction over two functions that are both the
constant function '~X'.
With concatenative languages, we always use the second form for
expressing the semantics of some function, e.g. 'X dup == X X'. We do
this because we have no syntax for application and no way of
expressing objects. There's something strange here though. Because all
terms denote functions, 'X' must be a function. However, 'X' cannot be
any function; it must be a function that pushes a value (and always
the same value) onto a stack.
I'm now wondering if this function/object ambiguity in concatenative
languages is really a good thing. Take the Joy expression '[1 2 3]'.
We can say that '[1 2 3]' is a function that pushes the value [1 2 3]
onto a stack, but we also have to say that the term '1' within the
term '[1 2 3]' itself denotes a function. This seems strange because,
if you gets the first element of the list, you certainly can't use it
as a function; it's a number.
This strangeness is more evident in the following example. The
expression '[dup swap] head' yields 'dup', but 'dup != [dup swap]
head'! This is because the left side of the equality is the *function*
'dup', but the right side gets reduced to the *object* 'dup'. You
might think this problem is due to Joy's "open" quotations, but you
have the same problem in Factor. For example, 'sq != { sq } first'
despite the fact that '{ sq } first' yields sq!
I believe I know how to solve this problem: Get rid of the function/
object ambiguity.
To do this, we drop the requirement that all terms denote functions.
'42' will now denote the *number* 42. To push 42 onto the stack, we'll
write '`42', i.e. the push function '`' applied to the object 42.
The presence of '`' allows us to get rid of quotation. Instead of '[+]
map', we would write '`+ map'; the '`' function applied to the '+'
function yields a new function that pushes the '+' function onto a
stack. We'd also need to add parentheses for grouping; you'd write
'`(foo bar)' instead of '[foo bar]'.
This allows us a solution to the problem with Factor's array literals
shown above: Array literals would only be allowed to contain terms
representing objects. For example, '`{ 42 `42 }' would be a function
that pushes an array onto a stack where the first element of the array
is the number 42 and the second element is a function that pushes 42
onto a stack.
I would also suggest adding an additional array syntax that takes
*functions* that evaluate to single objects. If 'x' is defined as '1 2
drop', then we could write '{| x |}' to push an array of one element
containing the number 1 onto the stack. Alternatively, because we now
have parentheses for grouping, we could replace 'x' with its
definition and write '{| ( 1 2 drop ) |}' instead.
In Joy, where creating multiple "copies" of the stack is cheap, you
could use my parallel "banana" combinator to construct lists. For
example, you could write the function '2 3 (|+ -|)' which would be
equivalent to the function '`{5 -1}' (assuming we also adopted closed
quotations for Joy and added '{ }' as a list literal syntax).
Since we have lost the object/function ambiguity, we can now express
the semantics of our functions in terms of application. I propose the
syntax '<object>:<function>' to indicate an application. Assuming our
concatenative language is stack-based, the object will always be a
stack.
I represent the stack as a null-terminated list (using angle brackets)
which is necessary to show how the function in question handles the
"rest of the stack". The right-most element of a list is the "top"
element. '<>' denotes the null list.
Here are the semantics of six functions in terms of application:
R:`42 -> <R, 42>
<R,X>:dup -> <R,<X,X>>
<R,<X,Y>>:swap -> <R,<Y,X>>
<R,F>:i -> R:F
<R,<X,F>>:dip -> <R:F, X>
R:clear -> <>
Alternatively, we can express our semantics in terms of equivalencies.
This isn't as generally useful as the application form above (e.g. you
can't express 'clear' in this form), but it is perhaps a bit easier to
read:
`42 == `42
`X dup == `X `X
`X `Y swap == `Y `X
`F i == F
`X `F dip == F `X
If you made it this far, thank you. Any thoughts would be appreciated.
- John
Stevan Apter — 2009-02-26 15:21:30
> This strangeness is more evident in the following example. The
> expression '[dup swap] head' yields 'dup', but 'dup != [dup swap]
> head'! This is because the left side of the equality is the *function*
> 'dup', but the right side gets reduced to the *object* 'dup'.
i've always assumed that [dup swap] head is the function dup. e.g. in XY:
2 [dup swap] first i
2 2
that is, [dup swap] first leaves the function dup on the stack.
then i moves the top item of the stack to the head of the queue.
what am i missing?
----- Original Message -----
From: "John Nowak" <john@...>
To: "concatenative" <concatenative@yahoogroups.com>
Sent: Thursday, February 26, 2009 9:34 AM
Subject: [stack] function/object ambiguity + quotation alternative
> I've hinted at this recently, but never properly explained it. It may
> be entirely uninteresting or even an incorrect observation, so I'd
> appreciate a whack on the hand if I'm off track. I think, however,
> that it may be important in letting us further develop a firm
> theoretical basis for concatenative languages.
>
> In a concatenative language, all terms denote functions. I don't think
> this is too contentious to say. For example, '42' denotes the function
> that pushes the value 42 onto a stack. '[foo bar]' is a function that
> pushes the quotation/list [foo bar] onto the stack. Et cetera.
>
> This seem to be a unique property. I have never seen a non-
> concatenative language in which every term denoted a function.
>
> John Backus invented the term "function-level programming", of which
> his language FP is the canonical example. In FP however, the term '42'
> represents an object. You use the combining form '~', aka "constant",
> to produce a function that returns the object 42 when passed any
> value, e.g. '~42'.
>
> Assume application is written ":", composition is written ".", a list
> is written in angle brackets, and construction is written with square
> brackets. Given this, the semantics of 'dup' in FP are as follows:
>
> dup:X -> <X, X>
> dup.~X == [~X, ~X]
>
> The former gives the semantics of applying 'dup' to some object X. The
> latter states an equivalence between composing 'dup' with the constant
> function '~X' and construction over two functions that are both the
> constant function '~X'.
>
> With concatenative languages, we always use the second form for
> expressing the semantics of some function, e.g. 'X dup == X X'. We do
> this because we have no syntax for application and no way of
> expressing objects. There's something strange here though. Because all
> terms denote functions, 'X' must be a function. However, 'X' cannot be
> any function; it must be a function that pushes a value (and always
> the same value) onto a stack.
>
> I'm now wondering if this function/object ambiguity in concatenative
> languages is really a good thing. Take the Joy expression '[1 2 3]'.
> We can say that '[1 2 3]' is a function that pushes the value [1 2 3]
> onto a stack, but we also have to say that the term '1' within the
> term '[1 2 3]' itself denotes a function. This seems strange because,
> if you gets the first element of the list, you certainly can't use it
> as a function; it's a number.
>
> This strangeness is more evident in the following example. The
> expression '[dup swap] head' yields 'dup', but 'dup != [dup swap]
> head'! This is because the left side of the equality is the *function*
> 'dup', but the right side gets reduced to the *object* 'dup'. You
> might think this problem is due to Joy's "open" quotations, but you
> have the same problem in Factor. For example, 'sq != { sq } first'
> despite the fact that '{ sq } first' yields sq!
>
> I believe I know how to solve this problem: Get rid of the function/
> object ambiguity.
>
> To do this, we drop the requirement that all terms denote functions.
> '42' will now denote the *number* 42. To push 42 onto the stack, we'll
> write '`42', i.e. the push function '`' applied to the object 42.
>
> The presence of '`' allows us to get rid of quotation. Instead of '[+]
> map', we would write '`+ map'; the '`' function applied to the '+'
> function yields a new function that pushes the '+' function onto a
> stack. We'd also need to add parentheses for grouping; you'd write
> '`(foo bar)' instead of '[foo bar]'.
>
> This allows us a solution to the problem with Factor's array literals
> shown above: Array literals would only be allowed to contain terms
> representing objects. For example, '`{ 42 `42 }' would be a function
> that pushes an array onto a stack where the first element of the array
> is the number 42 and the second element is a function that pushes 42
> onto a stack.
>
> I would also suggest adding an additional array syntax that takes
> *functions* that evaluate to single objects. If 'x' is defined as '1 2
> drop', then we could write '{| x |}' to push an array of one element
> containing the number 1 onto the stack. Alternatively, because we now
> have parentheses for grouping, we could replace 'x' with its
> definition and write '{| ( 1 2 drop ) |}' instead.
>
> In Joy, where creating multiple "copies" of the stack is cheap, you
> could use my parallel "banana" combinator to construct lists. For
> example, you could write the function '2 3 (|+ -|)' which would be
> equivalent to the function '`{5 -1}' (assuming we also adopted closed
> quotations for Joy and added '{ }' as a list literal syntax).
>
> Since we have lost the object/function ambiguity, we can now express
> the semantics of our functions in terms of application. I propose the
> syntax '<object>:<function>' to indicate an application. Assuming our
> concatenative language is stack-based, the object will always be a
> stack.
>
> I represent the stack as a null-terminated list (using angle brackets)
> which is necessary to show how the function in question handles the
> "rest of the stack". The right-most element of a list is the "top"
> element. '<>' denotes the null list.
>
> Here are the semantics of six functions in terms of application:
>
> R:`42 -> <R, 42>
> <R,X>:dup -> <R,<X,X>>
> <R,<X,Y>>:swap -> <R,<Y,X>>
> <R,F>:i -> R:F
> <R,<X,F>>:dip -> <R:F, X>
> R:clear -> <>
>
> Alternatively, we can express our semantics in terms of equivalencies.
> This isn't as generally useful as the application form above (e.g. you
> can't express 'clear' in this form), but it is perhaps a bit easier to
> read:
>
> `42 == `42
> `X dup == `X `X
> `X `Y swap == `Y `X
> `F i == F
> `X `F dip == F `X
>
> If you made it this far, thank you. Any thoughts would be appreciated.
>
> - John
>
John Nowak — 2009-02-26 15:47:41
On Feb 26, 2009, at 10:21 AM, Stevan Apter wrote:
>> This strangeness is more evident in the following example. The
>> expression '[dup swap] head' yields 'dup', but 'dup != [dup swap]
>> head'! This is because the left side of the equality is the
>> *function*
>> 'dup', but the right side gets reduced to the *object* 'dup'.
>
> i've always assumed that [dup swap] head is the function dup. e.g.
> in XY:
>
> 2 [dup swap] first i
> 2 2
>
> that is, [dup swap] first leaves the function dup on the stack.
> then i moves the top item of the stack to the head of the queue.
>
> what am i missing?
I don't know XY, but XY does not work the same way as Joy and Factor
apparently.
Factor:
>> 5 { dup } first call
Generic word call does not define a method for the word class.
Dispatching on object: dup
Joy:
>> 5 [dup] first i
run time error: quotation as top parameter needed for i
This is the problem I'm referring to.
What XY does seems even worse though! Here's why:
1. [dup] i == [dup] first i (given, as per your example)
2. [dup] == [dup] first (removing shared 'i' suffix)
3. id == first (removing share '[dup]' prefix)
Certainly first cannot be the identity function! Is the algebra of XY
broken? Am I missing something?
- John
John Nowak — 2009-02-26 16:21:14
On Feb 26, 2009, at 10:47 AM, John Nowak wrote:
> This is the problem I'm referring to.
Maybe this is an even simpler way of stating the problem in Joy and
Factor, where '->' means "evaluates to":
1. [X] first -> X (given)
2. [dup] first -> dup (valid, following from 1)
3. [dup] first != dup (given, in contradiction to 2)
The problem is that the 'dup' on the right side of 2 is a function
reified as an object, but the 'dup' on the right side of 3 is a
function. 'dup' "the function" is not the same as 'dup' "the function
object", yet they are represented by the same term. This causes
problems with algebraic manipulation of programs as plainly seen
above. My solution is to get rid of this ambiguity.
- John
Stevan Apter — 2009-02-26 17:22:22
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, February 26, 2009 10:47 AM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> On Feb 26, 2009, at 10:21 AM, Stevan Apter wrote:
>
>>> This strangeness is more evident in the following example. The
>>> expression '[dup swap] head' yields 'dup', but 'dup != [dup swap]
>>> head'! This is because the left side of the equality is the
>>> *function*
>>> 'dup', but the right side gets reduced to the *object* 'dup'.
>>
>> i've always assumed that [dup swap] head is the function dup. e.g.
>> in XY:
>>
>> 2 [dup swap] first i
>> 2 2
>>
>> that is, [dup swap] first leaves the function dup on the stack.
>> then i moves the top item of the stack to the head of the queue.
>>
>> what am i missing?
>
> I don't know XY, but XY does not work the same way as Joy and Factor
> apparently.
>
> Factor:
>
> >> 5 { dup } first call
> Generic word call does not define a method for the word class.
> Dispatching on object: dup
>
> Joy:
>
> >> 5 [dup] first i
> run time error: quotation as top parameter needed for i
>
> This is the problem I'm referring to.
>
> What XY does seems even worse though! Here's why:
>
> 1. [dup] i == [dup] first i (given, as per your example)
> 2. [dup] == [dup] first (removing shared 'i' suffix)
no. dup = [dup] first.
> 3. id == first (removing share '[dup]' prefix)
>
> Certainly first cannot be the identity function! Is the algebra of XY
> broken? Am I missing something?
>
> - John
>
William Tanksley — 2009-02-26 17:44:54
Stevan Apter wrote:
> From: "John Nowak" <john@...>
> > What XY does seems even worse though! Here's why:
> > 1. [dup] i == [dup] first i (given, as per your example)
> > 2. [dup] == [dup] first (removing shared 'i' suffix)
>> 3. id == first (removing share '[dup]' prefix)
> no. dup = [dup] first.
You're right, but he's also right -- you should always be able to remove
identical suffixes from both sides of the equation, and in this case you
can't do that.
-Wm
John Nowak — 2009-02-26 17:45:45
On Feb 26, 2009, at 12:22 PM, Stevan Apter wrote:
>> 2. [dup] == [dup] first (removing shared 'i' suffix)
>
> no. dup = [dup] first.
You're missing the point. I know that 'dup = [dup] first', but I can
prove '[dup] == [dup] first'!
I'll start with your example (minus the swap); after each step, I'll
indicate how I came upon the given equality:
1. 2 [dup] first i == 2 2 (given)
2. [F] i == F (given)
3 2 dup == 2 2 (given)
4. 2 [dup] i == 2 2 (2+3: substituted '[dup] i' for 'dup')
5. 2 [dup] i == 2 [dup] first i (1+4: both equal '2 2')
6. i == first i (5: removed shared '2 [dup]' prefix)
7. id == first (6: removed 'i' suffix)
To repeat, I know this is a bogus conclusion; that's my point. The
problem arises from the fact that '[dup] i' functions identically to
'[dup] first i' in XY; or, at least that's my understanding.
If my little derivation is wrong, please correct me. If not, I believe
this to be a problem in XY's semantics.
- John
John Nowak — 2009-02-26 17:53:44
On Feb 26, 2009, at 12:45 PM, John Nowak wrote:
>
> On Feb 26, 2009, at 12:22 PM, Stevan Apter wrote:
>
>>> 2. [dup] == [dup] first (removing shared 'i' suffix)
>>
>> no. dup = [dup] first.
>
> You're missing the point. I know that 'dup = [dup] first', but I can
> prove '[dup] == [dup] first'!
To be more precise, I know '[dup] first' evaluates to the function
object 'dup'. I assume this is what you mean.
That said, the function 'dup' does *not* equal the function '[dup]
first'. Obviously '2 dup' yields a different answer than '2 [dup]
first'. This is the initial problem I brought up and it may affect XY;
I know it affects Joy and Factor. The ability to prove '[dup] == [dup]
first' is an additional issue that is XY-specific.
- John
Stevan Apter — 2009-02-26 18:06:09
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, February 26, 2009 12:45 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> On Feb 26, 2009, at 12:22 PM, Stevan Apter wrote:
>
>>> 2. [dup] == [dup] first (removing shared 'i' suffix)
>>
>> no. dup = [dup] first.
>
> You're missing the point. I know that 'dup = [dup] first', but I can
> prove '[dup] == [dup] first'!
>
> I'll start with your example (minus the swap); after each step, I'll
> indicate how I came upon the given equality:
>
> 1. 2 [dup] first i == 2 2 (given)
> 2. [F] i == F (given)
> 3 2 dup == 2 2 (given)
> 4. 2 [dup] i == 2 2 (2+3: substituted '[dup] i' for 'dup')
> 5. 2 [dup] i == 2 [dup] first i (1+4: both equal '2 2')
> 6. i == first i (5: removed shared '2 [dup]' prefix)
> 7. id == first (6: removed 'i' suffix)
>
> To repeat, I know this is a bogus conclusion; that's my point. The
> problem arises from the fact that '[dup] i' functions identically to
> '[dup] first i' in XY; or, at least that's my understanding.
>
> If my little derivation is wrong, please correct me. If not, I believe
> this to be a problem in XY's semantics.
i see the problem. in XY, [X]first != [X]i.
i moves the top of the stack to the head of the queue, whence it executes,
immediately.
first replaces the top of the stack with the first element of the top of
the stack.
to make things just a little more confusing, there is no stack underflow
in XY, so e.g.
[dup] first i = [{ a \ a \ a }]
and:
10 20 [dup] [i] infra = 10 20 [[{ a \ a \ a }]]
which is the unit list of dup's "object code". repeatedly applying i
to this has no effect.
but:
[dup] first i 10 swap i = 10 10
but this is an implementation detail. an underflow exception could be
thrown instead.
>
> - John
>
Stevan Apter — 2009-02-26 18:09:25
----- Original Message -----
From: "William Tanksley" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, February 26, 2009 12:44 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
> Stevan Apter wrote:
>> From: "John Nowak" <john@...>
>> > What XY does seems even worse though! Here's why:
>> > 1. [dup] i == [dup] first i (given, as per your example)
>> > 2. [dup] == [dup] first (removing shared 'i' suffix)
>>> 3. id == first (removing share '[dup]' prefix)
>
>> no. dup = [dup] first.
>
> You're right, but he's also right -- you should always be able to remove
> identical suffixes from both sides of the equation, and in this case you
> can't do that.
1 is true, 2 is false.
first != i != id
i don't see the problem.
>
> -Wm
>
>
>
Stevan Apter — 2009-02-26 18:13:17
if you search way back in the archives, you'll see that this problem
surfaced fairly early on. billy and i batted around the idea that the
evaluator could be made "voracious":
2 [dup] first -> 2 2
i.e. the evaluator would not abide naked functions on the stack; it
would apply repeatedly until that was not the case.
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, February 26, 2009 12:53 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> On Feb 26, 2009, at 12:45 PM, John Nowak wrote:
>
>>
>> On Feb 26, 2009, at 12:22 PM, Stevan Apter wrote:
>>
>>>> 2. [dup] == [dup] first (removing shared 'i' suffix)
>>>
>>> no. dup = [dup] first.
>>
>> You're missing the point. I know that 'dup = [dup] first', but I can
>> prove '[dup] == [dup] first'!
>
> To be more precise, I know '[dup] first' evaluates to the function
> object 'dup'. I assume this is what you mean.
>
> That said, the function 'dup' does *not* equal the function '[dup]
> first'. Obviously '2 dup' yields a different answer than '2 [dup]
> first'. This is the initial problem I brought up and it may affect XY;
> I know it affects Joy and Factor. The ability to prove '[dup] == [dup]
> first' is an additional issue that is XY-specific.
>
> - John
>
John Nowak — 2009-02-26 18:22:08
On Feb 26, 2009, at 1:06 PM, Stevan Apter wrote:
> i see the problem. in XY, [X]first != [X]i.
> i moves the top of the stack to the head of the queue, whence it
> executes,
> immediately.
> first replaces the top of the stack with the first element of the
> top of
> the stack.
Your assessment that the problem is '[X]first != [X]i' is incorrect.
Currently in XY, this is true:
[dup] first i == [dup] i
The problem is that this means I can prove that 'first' is the
identity function.
If we change XY such that '[dup] first == [dup] i', I can take this:
[dup] first i == [dup] i
And substitute '[dup] i' for '[dup] first':
[dup] i i == [dup] i
And hence I can prove 'i i == i', and then from there prove that 'i'
is the identity function.
The only way to solve this problem is to have '[dup] first i' cause an
error. 'i' must be correctly restricted to performing "dequotation" as
in Joy, Factor, Cat, etc.
- John
Christopher Diggins — 2009-02-26 19:57:15
>> the evaluator would not abide naked functions on the stack; it would apply repeatedly until that was not the case.
Then you have a first order language. I don't really see what that
buys you. Why not allow functions on the stack? It seems to me that
higher-order languages are only a positive thing. Joy and its ilk are
only interesting to me because they allow functions on the stack.
John Nowak — 2009-02-26 20:08:19
On Feb 26, 2009, at 2:57 PM, Christopher Diggins wrote:
>>> the evaluator would not abide naked functions on the stack; it
>>> would apply repeatedly until that was not the case.
>
> Then you have a first order language. I don't really see what that
> buys you. Why not allow functions on the stack?
I think you're misunderstanding what Stevan was saying. Stevan is not
suggesting anything that would eliminate quotations or make a language
first order.
Joy and XY have "open" quotations. You can modify them as if they were
lists. This causes two problems.
The first is that the language becomes non-extensional. For example,
say you know that 'a b == c d'. In Joy and XY, it does not follow that
'[a b] == [c d]' as it does in Cat.
The second problem is that, because you can take "things" out of a
quotation, you end up with weird problems. Factor has this problem too
with its array literals; or at least I'm pretty sure it does.
Take the program '5 [dup] first' for example. The result of this is '5
dup'. However, the 'dup' here is *not* a function the same way 'first'
was; it's a function object. Accordingly, the result of evaluation '5
[dup]' is a stack with two elements; the first is the value 5 and the
second is the value 'dup'.
What Stevan was suggesting was to evaluate "unquoted function objects"
like 'dup' in the example above. In this system '5 [dup] first' would
reduce to '5 dup', but then further reduce to '5 5' without the need
to explicitly re-quote 'dup' and call 'i'.
> It seems to me that higher-order languages are only a positive thing.
Certain forms of reasoning are a lot easier without higher order
functions. It's a tradeoff.
- John
Stevan Apter — 2009-02-26 20:11:20
i don't disagree. so in cat, what's on the stack after evaluating [dup]first?
----- Original Message -----
From: "Christopher Diggins" <cdiggins@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, February 26, 2009 2:57 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>>> the evaluator would not abide naked functions on the stack; it would apply repeatedly until that was not the case.
>
> Then you have a first order language. I don't really see what that
> buys you. Why not allow functions on the stack? It seems to me that
> higher-order languages are only a positive thing. Joy and its ilk are
> only interesting to me because they allow functions on the stack.
>
John Nowak — 2009-02-26 20:10:27
On Feb 26, 2009, at 3:08 PM, John Nowak wrote:
> Accordingly, the result of evaluation '5 [dup]' is a stack with two
> elements; the first is the value 5 and the second is the value 'dup'.
Sorry, that should read as follows:
"Accordingly, the result of evaluation '5 [dup] first' is a stack with
two elements; the first is the value 5 and the second is the value dup."
- John
John Nowak — 2009-02-26 20:15:06
On Feb 26, 2009, at 3:11 PM, Stevan Apter wrote:
>>> the evaluator would not abide naked functions on the stack; it
>>> would apply repeatedly until that was not the case.
>>
>> Then you have a first order language. I don't really see what that
>>
>> buys you. Why not allow functions on the stack? It seems to me that
>> higher-order languages are only a positive thing. Joy and its ilk are
>> only interesting to me because they allow functions on the stack.
>
> i don't disagree.
You should disagree unless you're suggesting that '5 [dup]' evaluate
to '5 5'. Are you? I wasn't under the impression that you meant to
make that point. If you weren't suggesting that, the language would
remain higher-order.
> so in cat, what's on the stack after evaluating [dup]first?
This is not possible in Cat because quotations are not manipulatable
as such. This is necessary to preserve extensionality (e.g. '[1 2
drop] == [1]') and to allow the language to be typed.
- John
Stevan Apter — 2009-02-26 20:25:00
well, it's worth pointing out that
5 [dup] first i
5 [dup] i
both evaluate to 5 5 in XY because 'i' doesn't quite mean
disquotation. it means "take the top of the stack and
prepend it to the head of the queue." where Q is the queue
and S is the stack, the k code which implements this is
roughly:
Q:(last S),Q
S:-1_S
x,y is concatenation, x_y is drop from the head or tail.
suppose last S = dup, then Q = dup,Q. suppose last S = [dup],
then Q = [dup],Q. but Q is the same in both cases. concatenation
of a unit list is the same as the concatenation of atom.
if i can remember what i was thinking at the time, it was
something like this: i want to be able to put naked functions
on the stack. but i also want to be able to evaluate them
once they're there. so reinterpret 'i' to mean 'move to the
queue'.
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, February 26, 2009 3:08 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> On Feb 26, 2009, at 2:57 PM, Christopher Diggins wrote:
>
>>>> the evaluator would not abide naked functions on the stack; it
>>>> would apply repeatedly until that was not the case.
>>
>> Then you have a first order language. I don't really see what that
>> buys you. Why not allow functions on the stack?
>
> I think you're misunderstanding what Stevan was saying. Stevan is not
> suggesting anything that would eliminate quotations or make a language
> first order.
>
> Joy and XY have "open" quotations. You can modify them as if they were
> lists. This causes two problems.
>
> The first is that the language becomes non-extensional. For example,
> say you know that 'a b == c d'. In Joy and XY, it does not follow that
> '[a b] == [c d]' as it does in Cat.
>
> The second problem is that, because you can take "things" out of a
> quotation, you end up with weird problems. Factor has this problem too
> with its array literals; or at least I'm pretty sure it does.
>
> Take the program '5 [dup] first' for example. The result of this is '5
> dup'. However, the 'dup' here is *not* a function the same way 'first'
> was; it's a function object. Accordingly, the result of evaluation '5
> [dup]' is a stack with two elements; the first is the value 5 and the
> second is the value 'dup'.
>
> What Stevan was suggesting was to evaluate "unquoted function objects"
> like 'dup' in the example above. In this system '5 [dup] first' would
> reduce to '5 dup', but then further reduce to '5 5' without the need
> to explicitly re-quote 'dup' and call 'i'.
>
>> It seems to me that higher-order languages are only a positive thing.
>
> Certain forms of reasoning are a lot easier without higher order
> functions. It's a tradeoff.
>
> - John
>
Stevan Apter — 2009-02-26 20:33:39
i meant that i don't disagree that functions on the stack are a
good thing.
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, February 26, 2009 3:15 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> On Feb 26, 2009, at 3:11 PM, Stevan Apter wrote:
>
>>>> the evaluator would not abide naked functions on the stack; it
>>>> would apply repeatedly until that was not the case.
>>>
>>> Then you have a first order language. I don't really see what that
>>>
>>> buys you. Why not allow functions on the stack? It seems to me that
>>> higher-order languages are only a positive thing. Joy and its ilk are
>>> only interesting to me because they allow functions on the stack.
>>
>> i don't disagree.
>
> You should disagree unless you're suggesting that '5 [dup]' evaluate
> to '5 5'. Are you? I wasn't under the impression that you meant to
> make that point. If you weren't suggesting that, the language would
> remain higher-order.
>
>> so in cat, what's on the stack after evaluating [dup]first?
>
> This is not possible in Cat because quotations are not manipulatable
> as such. This is necessary to preserve extensionality (e.g. '[1 2
> drop] == [1]') and to allow the language to be typed.
>
> - John
>
Don Groves — 2009-02-26 21:04:33
On Feb 26, 2009, at 11:57 AM, Christopher Diggins wrote:
>>> the evaluator would not abide naked functions on the stack; it
>>> would apply repeatedly until that was not the case.
>
> Then you have a first order language. I don't really see what that
> buys you. Why not allow functions on the stack? It seems to me that
> higher-order languages are only a positive thing. Joy and its ilk are
> only interesting to me because they allow functions on the stack.
I'll second that.
--
don
William Tanksley — 2009-02-26 21:19:41
Don Groves wrote:
> Christopher Diggins wrote:
> >>> the evaluator would not abide naked functions on the stack; it
> >>> would apply repeatedly until that was not the case.
> > Then you have a first order language. I don't really see what that
> > buys you. Why not allow functions on the stack? It seems to me that
> > higher-order languages are only a positive thing. Joy and its ilk are
> > only interesting to me because they allow functions on the stack.
> I'll second that.
You guys must be high(er order). (Don, I'm not sure if that pun was
actually intended. If it wasn't, sorry I misunderstood you, and my reply
was a pun.)
Higher order languages are cool, but first order languages can be useful
too; FP (AFAIK) is a first-order language, as is (I believe) APL, J, and
NIAL. So's Forth, although being impure it doesn't try to restrict that
in any way (you can kind of play with function references).
> don
-Wm
Don Groves — 2009-02-26 21:25:56
On Feb 26, 2009, at 1:19 PM, William Tanksley wrote:
> Don Groves wrote:
>> Christopher Diggins wrote:
>>>>> the evaluator would not abide naked functions on the stack; it
>>>>> would apply repeatedly until that was not the case.
>
>>> Then you have a first order language. I don't really see what that
>>> buys you. Why not allow functions on the stack? It seems to me that
>>> higher-order languages are only a positive thing. Joy and its ilk
>>> are
>>> only interesting to me because they allow functions on the stack.
>
>> I'll second that.
>
> You guys must be high(er order). (Don, I'm not sure if that pun was
> actually intended. If it wasn't, sorry I misunderstood you, and my
> reply
> was a pun.)
It was, and it wasn't -- I left it up to the reader.
Yours definitely was -- and a good one too ;-)
> Higher order languages are cool, but first order languages can be
> useful
> too; FP (AFAIK) is a first-order language, as is (I believe) APL, J,
> and
> NIAL. So's Forth, although being impure it doesn't try to restrict
> that
> in any way (you can kind of play with function references).
I did a lot of that in Forth, but it's much nicer to have it part
of the language rather than a kludge.
--
don
>> don
>
> -Wm
>
>
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>
Chris Double — 2009-02-26 22:12:39
On Fri, Feb 27, 2009 at 9:25 AM, Stevan Apter <
sa@...> wrote:
> if i can remember what i was thinking at the time, it was
> something like this: i want to be able to put naked functions
> on the stack. but i also want to be able to evaluate them
> once they're there. so reinterpret 'i' to mean 'move to the
> queue'.
This is one of the features I like about XY.That function objects are
executable using 'i' just like quotations. It's a feature that has
been discussed to be added to factor occasionally, that of making word
objects callable (ie. make call a generic that works on quotations and
words).
Chris.
--
http://www.bluishcoder.co.nz
zallambo — 2009-02-27 00:02:32
--- In
concatenative@yahoogroups.com, William Tanksley
<wtanksleyjr@...> wrote:
>
> Stevan Apter wrote:
> > From: "John Nowak" <john@...>
> > > What XY does seems even worse though! Here's why:
> > > 1. [dup] i == [dup] first i (given, as per your example)
> > > 2. [dup] == [dup] first (removing shared 'i' suffix)
> >> 3. id == first (removing share '[dup]' prefix)
>
> > no. dup = [dup] first.
>
> You're right, but he's also right -- you should always be able to remove
> identical suffixes from both sides of the equation, and in this case you
> can't do that.
>
> -Wm
>
(2) does not follow from (1) and (3) does not follow from (2). In
general, you *cannot* remove common prefixes and suffixes. For instance,
If you could remove prefixes, then
[dup] dup == [dup] [dup]
=> dup == [dup].
If you could remove suffixes, then
2 [id] i == 1 [inc] i
=> 2 [id] == 1 [inc].
There's nothing special about concatenative languages in this regard;
as long as there are functions that aren't reversible (i.e. are not
one-to-one and onto), f(a) = f(b) does not imply a = b. And f(a) =
g(a) does not imply f = g.
I think John has a point though. We need a way to distinguish between
the function 'dup' and the object 'dup'. I suggest putting a single
quote in front of objects, like symbols in Scheme. So
[dup] i == dup
[dup] first == 'dup
However, we *don't* need to distinguish the number 42 from the
function 42. Pushing the number 42 on the stack is exactly the same as
applying the function 42 to the stack. So
[42] i == [42] first
42 == '42
Does anyone have another syntax for writing 'dup? I cannot recall
seeing any.
Justin
Adam — 2009-02-27 02:58:10
John,
Are you aware of the word execute
(
http://docs.factorcode.org/content/word-execute,words.html)?
If I understand Chris, the current call/execute distinction reveals your
problem but I'm not sure.
( scratchpad ) 5 { dup } first execute . .
5
5
( scratchpad ) 5 { dup } >quotation call . .
5
5
( scratchpad ) 5 [ dup ] first execute . .
5
5
( scratchpad ) 5 [ dup ] call . .
5
5
( scratchpad ) 5 \ dup execute . .
5
5
-Adam
--- In
concatenative@yahoogroups.com, Chris Double <chris.double@...>
wrote:
>
> On Fri, Feb 27, 2009 at 9:25 AM, Stevan Apter sa@... wrote:
> > if i can remember what i was thinking at the time, it was
> > something like this: i want to be able to put naked functions
> > on the stack. but i also want to be able to evaluate them
> > once they're there. so reinterpret 'i' to mean 'move to the
> > queue'.
>
> This is one of the features I like about XY.That function objects are
> executable using 'i' just like quotations. It's a feature that has
> been discussed to be added to factor occasionally, that of making word
> objects callable (ie. make call a generic that works on quotations and
> words).
>
> Chris.
> --
> http://www.bluishcoder.co.nz
>
John Cowan — 2009-02-27 05:05:18
John Nowak scripsit:
> In a concatenative language, all terms denote functions. I don't think
> this is too contentious to say. For example, '42' denotes the function
> that pushes the value 42 onto a stack. '[foo bar]' is a function that
> pushes the quotation/list [foo bar] onto the stack. Et cetera.
For Joy, at least, you can go further: *everything* is simultaneously a
value and a function. Thus '+' is both a value (a symbol, specifically)
and the addition function. The difference between symbols as values
and all other values is that they do something as functions other than
"push self on stack". So you can't just say its name to get the 'first'
symbol on the stack; instead, you must use a circumlocution such as
'"first" intern' or '[first] first' instead.
Furthermore, by what I consider a bug in Joy, second-order functions
will only handle lists of symbols rather than single symbols, so you
cannot say "[1 2 3] "first" intern i" to get 1 on the stack.
--
John Cowan
http://ccil.org/~cowan cowan@...
Mr. Henry James writes fiction as if it were a painful duty. --Oscar Wilde
John Cowan — 2009-02-27 05:10:46
Adam scripsit:
> Are you aware of the word execute
> (http://docs.factorcode.org/content/word-execute,words.html)?
I don't know the details of Factor, only of Joy.
--
John Cowan
cowan@... http://ccil.org/~cowan
I must confess that I have very little notion of what [s. 4 of the British
Trade Marks Act, 1938] is intended to convey, and particularly the sentence
of 253 words, as I make them, which constitutes sub-section 1. I doubt if
the entire statute book could be successfully searched for a sentence of
equal length which is of more fuliginous obscurity. --MacKinnon LJ, 1940
John Nowak — 2009-02-27 12:31:24
On Feb 26, 2009, at 7:02 PM, zallambo wrote:
> William Tanksley wrote:
>
>> Stevan Apter wrote:
>>
>>> John Nowak wrote:
>>>
>>>> What XY does seems even worse though! Here's why:
>>>> 1. [dup] i == [dup] first i (given, as per your example)
>>>> 2. [dup] == [dup] first (removing shared 'i' suffix)
>>>> 3. id == first (removing share '[dup]' prefix)
>>>
>>> no. dup = [dup] first.
>>
>> You're right, but he's also right -- you should always be able to
>> remove
>> identical suffixes from both sides of the equation, and in this
>> case you
>> can't do that.
>
> (2) does not follow from (1) and (3) does not follow from (2).
You're right. You can add prefixes/suffixes but not necessarily remove
them. I got my wires crossed somewhere along the way. My apologies.
> I think John has a point though. We need a way to distinguish between
> the function 'dup' and the object 'dup'. I suggest putting a single
> quote in front of objects, like symbols in Scheme. So
>
> [dup] i == dup
> [dup] first == 'dup
I think we need to be careful here. If we intend both sides of the
equality to be a function, then 'dup cannot be an object.
> Does anyone have another syntax for writing 'dup? I cannot recall
> seeing any.
In the initial email, I proposed using `dup to indicate the constant
function that returns dup. This would allow you to state the following:
[dup] first == `dup
It's important to note that '[dup] != `dup'. I proposed getting rid of
quotation to solve this issue. It obviously would be a big change for
Joy and XY. For something like Cat, it would be minor.
- John
John Nowak — 2009-02-27 12:33:06
On Feb 26, 2009, at 9:58 PM, Adam wrote:
> Are you aware of the word execute
> (http://docs.factorcode.org/content/word-execute,words.html)?
Yes.
> If I understand Chris, the current call/execute distinction reveals
> your
> problem but I'm not sure.
More the other way around. I think it's important to treat the object
[dup] and the object dup differently.
- John
John Nowak — 2009-02-27 12:36:57
On Feb 27, 2009, at 12:05 AM, John Cowan wrote:
> Furthermore, by what I consider a bug in Joy, second-order functions
> will only handle lists of symbols rather than single symbols, so you
> cannot say "[1 2 3] "first" intern i" to get 1 on the stack.
I don't consider this a bug. The rule for 'i' is as follows:
[F] i -> F
Changing 'i' to also operate on symbols would complicate things.
Perhaps it's a tradeoff.
- John
Stevan Apter — 2009-02-27 13:24:57
in f, which is modelled on the 'false' language, i distinguish
primitives like + and defined words like 'dup'.
dup is defined as the shuffle 'a-aa, where ' is syntactic quotation.
a primitive executes immediately, a defined word pushes its definition
onto the stack. ! (i) moves the top of the stack to the head of the
queue.
so e.g. we have
G>dup
'a-aa
G>2
'a-aa 2
G>swap
'a-aa 2 'ab-ba
G>!
2 'a-aa
G>!
2 2
G>
and
G>[dup swap]first
[dup swap] [uncons ! pop !]
G>!
dup
G>!
'a-aa
G>
a primitive is identical to its definition:
G>2 3 [+]first!
2 3 +
G>!
5
G>
www.nsl.com/k/f/f.htm
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, February 27, 2009 7:36 AM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> On Feb 27, 2009, at 12:05 AM, John Cowan wrote:
>
>> Furthermore, by what I consider a bug in Joy, second-order functions
>> will only handle lists of symbols rather than single symbols, so you
>> cannot say "[1 2 3] "first" intern i" to get 1 on the stack.
>
> I don't consider this a bug. The rule for 'i' is as follows:
>
> [F] i -> F
>
> Changing 'i' to also operate on symbols would complicate things.
> Perhaps it's a tradeoff.
>
> - John
>
>
William Tanksley — 2009-02-27 16:52:10
John Nowak wrote:
> More the other way around. I think it's important to treat the object
> [dup] and the object dup differently.
Why? In what way are they different? Won't they do the same thing when
executed? Is there any function that should return a different result
when called on one than another?
> - John
-Wm
John Nowak — 2009-02-27 18:13:27
On Feb 27, 2009, at 11:52 AM, William Tanksley wrote:
> John Nowak wrote:
>
>> More the other way around. I think it's important to treat the object
>> [dup] and the object dup differently.
>
> Why? In what way are they different?
I'll be using Joy for all of these examples.
dup and [dup] are not the same object. I'd hope you'd agree that this
holds:
[dup] != [dup] first
If the above functions produced the same object, then this would be
true:
[dup] first == [dup] first first
And obviously it is not; the right side will cause an error.
> Is there any function that should return a different result when
> called on one than another?
Sure. Calling 'quote' on the object dup should yield the object [dup].
Calling it on the object [dup] should yield the object [[dup]]. I'm
sure you'd agree that [dup] and [[dup]] are not the same object, and
hence it follows that dup and [dup] are not the same object.
The whole problem is that, in my opinion, "open" quotations suck. For
example, this rule always holds true in Cat:
quote i == id (note: Cat renames 'i' to 'apply')
However, this does not hold in Joy. Here's an example of why taken
straight from the Joy REPL (where 'quote' is defined as '[] cons'):
5 [dup] first stack .
[dup 5]
5 [dup] first quote i stack .
[5 5]
If you were to disallow open quotations, the 'quote i == id' rule
would hold in Joy.
That's not really the main reason open quotations suck; the real
problems are:
1. You can no longer factor code indiscriminately.
2. You have these ugly naked function objects to deal with!
Forgive me for continuing to make this same point. It is the cause of
this naked function dilemma however, so I can't really avoid
mentioning it.
- John
William Tanksley — 2009-02-27 18:29:31
John Nowak wrote:
> William Tanksley wrote:
> > John Nowak wrote:
> >> More the other way around. I think it's important to treat the object
> >> [dup] and the object dup differently.
> > Why? In what way are they different?
> I'll be using Joy for all of these examples.
> The whole problem is that, in my opinion, "open" quotations suck. For
> example, this rule always holds true in Cat:
So you want to treat them differently because treating them differently
sucks? :-)
Yes, I agree that open quotations are a problem. If you don't have them,
[dup] and `dup are equivalent by any test.
> That's not really the main reason open quotations suck; the real
> problems are:
> 1. You can no longer factor code indiscriminately.
> 2. You have these ugly naked function objects to deal with!
Quotations, whether open or closed, ARE "naked function objects".
> - John
-Wm
Stevan Apter — 2009-02-27 18:46:46
----- Original Message -----
From: "William Tanksley" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, February 27, 2009 1:29 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
> John Nowak wrote:
>> William Tanksley wrote:
>> > John Nowak wrote:
>> >> More the other way around. I think it's important to treat the object
>> >> [dup] and the object dup differently.
>> > Why? In what way are they different?
>
>> I'll be using Joy for all of these examples.
>
>> The whole problem is that, in my opinion, "open" quotations suck. For
>> example, this rule always holds true in Cat:
>
> So you want to treat them differently because treating them differently
> sucks? :-)
>
> Yes, I agree that open quotations are a problem. If you don't have them,
> [dup] and `dup are equivalent by any test.
>
>> That's not really the main reason open quotations suck; the real
>> problems are:
>> 1. You can no longer factor code indiscriminately.
i don't want to spoil the party, but the fact is, i'm rarely in
the position where i *want* to factor my code. my building blocks
are not words or code-fragments, they're *functions*. typically,
a function evolves over time in response to demands from the
environment by acquiring new arguments and/or producing results
with new components. the large-scale "topography" of the system
is relatively stable. (come to think of it, this is a pretty
good metaphor for how systems grow in the real world.) if i have
to dig into the functions and move snippets of code around into
a new functional architecture, then my original design was pitched
at the wrong "level." the kind of refactoring i want to avoid
(because it is the most painful kind) is *renaming*. going from
a set of several hundred functions a,b,c,... to a different set
x,y,z,... . code is committed to a function with a name when
a judgement is made that that functionality will endure for the
lifetime of the system, and that changes will for the most part
be extensions rather than revisions.
>> 2. You have these ugly naked function objects to deal with!
i guess i don't see the problem here. f, [f], [[f]], ... seems
quite intuitive to me.
>
> Quotations, whether open or closed, ARE "naked function objects".
>
>> - John
>
> -Wm
>
>
>
Stevan Apter — 2009-02-27 18:56:38
at this point i'd just like to reflect on the cost one pays
for designing a language which tries to (i'm not sure how to
say this) maximize refactorability. i'm tempted to say that
such a language will send the programmer down a path where
refactoring is more often required, even in cases where the
necessary changes are trivial. this is probably not a good
thing. to give the first reason that occurs to me: refactoring
(and i've done a fair bit of it in developing examples for
the various concatenative languages i've written over the
years) seems to me to be inherently error-prone, more so than
adding new arguments to functions or modifying what they
return. now it may be that most of the problems i've encountered
in refactoring exercises are due to the fact that my languages
use "open quotation," but if so, that isn't immediately obvious
to me.
----- Original Message -----
From: "Stevan Apter" <sa@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, February 27, 2009 1:46 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> ----- Original Message -----
> From: "William Tanksley" <wtanksleyjr@...>
> To: <concatenative@yahoogroups.com>
> Sent: Friday, February 27, 2009 1:29 PM
> Subject: Re: [stack] function/object ambiguity + quotation alternative
>
>
>> John Nowak wrote:
>>> William Tanksley wrote:
>>> > John Nowak wrote:
>>> >> More the other way around. I think it's important to treat the object
>>> >> [dup] and the object dup differently.
>>> > Why? In what way are they different?
>>
>>> I'll be using Joy for all of these examples.
>>
>>> The whole problem is that, in my opinion, "open" quotations suck. For
>>> example, this rule always holds true in Cat:
>>
>> So you want to treat them differently because treating them differently
>> sucks? :-)
>>
>> Yes, I agree that open quotations are a problem. If you don't have them,
>> [dup] and `dup are equivalent by any test.
>>
>>> That's not really the main reason open quotations suck; the real
>>> problems are:
>>> 1. You can no longer factor code indiscriminately.
>
> i don't want to spoil the party, but the fact is, i'm rarely in
> the position where i *want* to factor my code. my building blocks
> are not words or code-fragments, they're *functions*. typically,
> a function evolves over time in response to demands from the
> environment by acquiring new arguments and/or producing results
> with new components. the large-scale "topography" of the system
> is relatively stable. (come to think of it, this is a pretty
> good metaphor for how systems grow in the real world.) if i have
> to dig into the functions and move snippets of code around into
> a new functional architecture, then my original design was pitched
> at the wrong "level." the kind of refactoring i want to avoid
> (because it is the most painful kind) is *renaming*. going from
> a set of several hundred functions a,b,c,... to a different set
> x,y,z,... . code is committed to a function with a name when
> a judgement is made that that functionality will endure for the
> lifetime of the system, and that changes will for the most part
> be extensions rather than revisions.
>
>>> 2. You have these ugly naked function objects to deal with!
>
> i guess i don't see the problem here. f, [f], [[f]], ... seems
> quite intuitive to me.
>
>>
>> Quotations, whether open or closed, ARE "naked function objects".
>>
>>> - John
>>
>> -Wm
>>
>>
>>
>
John Nowak — 2009-02-27 18:56:38
On Feb 27, 2009, at 1:29 PM, William Tanksley wrote:
>> The whole problem is that, in my opinion, "open" quotations suck. For
>> example, this rule always holds true in Cat:
>
> So you want to treat them differently because treating them
> differently
> sucks? :-)
It's more that I want to be consistent in treating them differently
since we *must* treat them differently once we have open quotations.
In Joy, no function treats them similarly that I know of. In XY, only
'i' seems to treat them similarly. I think this exception is a mistake.
> Yes, I agree that open quotations are a problem. If you don't have
> them,
> [dup] and `dup are equivalent by any test.
My main point though is that, without open quotations, you never need
to see the object dup on the stack in the first place! It avoids the
entire issue. Cat is a good example of how this can work.
> Quotations, whether open or closed, ARE "naked function objects".
By naked function objects, I specifically meant functions not in a
quotation. They cause the problem that '[dup] first' looks like it
would return 'dup', but '[dup] first' is not the same function as
'dup'. This causes problems when manipulating programs and is
precisely what I was trying to address in my initial email. Zallambo
has understood my point, but I think his solution of using (') to
denote an object is unsatisfactory because objects cannot be composed
and something like "'dup 42" looks like a function composition.
- John
John Nowak — 2009-02-27 19:03:22
On Feb 27, 2009, at 1:46 PM, Stevan Apter wrote:
> i guess i don't see the problem here. f, [f], [[f]], ... seems
> quite intuitive to me.
Last try. Please don't take the list format below as exasperation,
condescension, or anything of the like; I just want to convince you
I'm not nuts at this point:
1. '[dup] first' returns the *function object* 'dup'.
2. '[dup] first != dup' because the 'dup' on the right is a *function*
and not a *function object* as it was in 1.
3. I want a notation to avoid this confusion.
4. I have proposed '[dup] first == `dup', where '`dup' is a *function*
that pushes the *function object* 'dup' onto the stack.
5. '`dup' must be a *function*, not a *function object*, because all
terms in a concatenative language denote functions; if they weren't
functions, you couldn't concatenate them to compose them.
- John
John Nowak — 2009-02-27 19:05:39
On Feb 27, 2009, at 1:56 PM, Stevan Apter wrote:
> at this point i'd just like to reflect on the cost one pays
> for designing a language which tries to (i'm not sure how to
> say this) maximize refactorability. i'm tempted to say that
> such a language will send the programmer down a path where
> refactoring is more often required, even in cases where the
> necessary changes are trivial.
I think you're absolutely right. Forth and friends *demand* short
words, and consequently, demand factoring. A language like Scheme
makes it harder to factor, but the nesting and locals means much
larger definitions are readable, and consequently, much less factoring
is required.
- John
Stevan Apter — 2009-02-27 19:37:55
i appreciate your forebearance. i may have a useful analogy from
planet k.
consider
2+3
we say that this expression is an nvn -- a noun-verb-noun -- which
is a proposition about the grammar of an expression.
in the context of expressions like 2+3 "+" is a verb. speaking
loosely, in the context of 2+3, "+" denotes the addition function,
although strictly speaking, it doesn't really denote anything.
now, when i assign a name to + as in:
a:+
or pass + to a function as in:
f[+;10]
then i say that + is a piece of data, and has the grammatical form
of a noun. the verb has been "nominalized."
why a noun? because to apply the function + i say:
a . 10 20
30
which is an nvn - a noun a, a verb ., and a noun 10 20. a is clearly
not a verb, since i can't say
2 a 3
so, i would say, when you claim that
[dup] first != dup
you're equivocating between two contexts, one in which the function
is a piece of data on the stack (what you call a function object), and
one in which "dup" a term in an expression.
in other words, this equivocation is leading you to reify a categorial
distinction between syntax and semantics as a semantic distinction between
two kinds of objects, functions and function-objects. this is similar to
the predicament k programmers get themselves into by asking why they can't
say:
a:+
2 a 3
or
+ . 2 3
(in the first case, the grammar is nnn, in the second it's vvnn)
in XY it's very clear that what's on the stack after [dup]first is
evaluated, what's on the queue when dup is moved from the stack to
to the queue by i, and what's on the stack after the user enters
2 dup, is the very same object. call it a function, call it a function-
object, call it a symbol - it doesn't really matter.
apologies if this only adds to the confusion.
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, February 27, 2009 2:03 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> On Feb 27, 2009, at 1:46 PM, Stevan Apter wrote:
>
>> i guess i don't see the problem here. f, [f], [[f]], ... seems
>> quite intuitive to me.
>
> Last try. Please don't take the list format below as exasperation,
> condescension, or anything of the like; I just want to convince you
> I'm not nuts at this point:
>
> 1. '[dup] first' returns the *function object* 'dup'.
> 2. '[dup] first != dup' because the 'dup' on the right is a *function*
> and not a *function object* as it was in 1.
> 3. I want a notation to avoid this confusion.
> 4. I have proposed '[dup] first == `dup', where '`dup' is a *function*
> that pushes the *function object* 'dup' onto the stack.
> 5. '`dup' must be a *function*, not a *function object*, because all
> terms in a concatenative language denote functions; if they weren't
> functions, you couldn't concatenate them to compose them.
>
> - John
>
John Nowak — 2009-02-27 21:13:37
On Feb 27, 2009, at 2:37 PM, Stevan Apter wrote:
> i appreciate your forebearance. i may have a useful analogy from
> planet k.
Thanks, that does make sense. Maybe I can put it in those terms.
Take this expression:
2 [dup] first
Here, '2' is a verb. So are '[dup]' and 'first'. Indeed, everything is
a verb in a concatenative *program* (very careful emphasis on
"program" there) because all terms denote functions.
The problem occurs when you *reduce* the program. Let's go step by step:
2 [dup] first (initial state)
2 [dup] first (after evaluating the function '2')
2 [dup] first (after evaluating the function '[dup]')
2 dup (after evaluating the function 'first')
The end of this evaluation is '2 dup'. The important thing here though
is now '2' and 'dup' are nouns. You cannot further reduce '2 dup' to
'2 2' even though the syntax makes it look like you should be able to.
It's this syntactic confusion I want to address. I want to be able to
do reductions like I did above--that is, without keeping an explicit
stack--while making it clear which things are verbs and which things
are nouns.
To reiterate, nouns do not occur in the programs you write, but they
*do* occur when reducing those programs.
One way to solve this problem is to use some symbol, perhaps a quote
as zallambo suggested, to indicate which things are nouns. Using this
technique, we can do our reduction again:
2 [dup] first (initial state)
'2 [dup] first (after evaluating the function '2')
'2 '[dup] first (after evaluating the function '[dup]')
'2 'dup (after evaluating the function 'first')
In this case, we end up with "'2 'dup". It's clear from looking at the
expression that we're done reducing the program. It avoids the
confusion of looking at '2 dup' and not being sure if we're supposed
to do reduce it to '2 2' or not.
(Hopefully the issue I want to address is clear now. It may get fuzzy
from this point on...)
Unfortunately, I take issue with zallambo's proposal. My problem with
it--if I understand it correctly--is that it creates two languages,
one for writing programs and another to use when reducing them.
Perhaps I'm being overly pedantic, but he said 'foo denotes an
*object*, and objects are not part of the term language when writing a
program. "All terms denote functions."
My proposal differs from zallambo's in that I prefer to add a "push"
functional form to the language itself so that you do not have a two
syntaxes. Instead, you use the same syntax for writing a program and
for reducing a program.
The "push" form would be written with a back-tick and would indicate a
function that pushes itself onto the stack; essentially, it self-
evaluates like a quoted expression does in Scheme. Also as in Scheme,
it would not be necessary to quote things which always evaluate to
themselves (like numbers); only things that don't (namely functions).
To make this model compatible with open quotations, quotation would
apply "push" to every *noun* within it to lift them to verbs. For
example, the following would be equivalent:
`foo `bar [] cons cons
[foo bar]
Here's the same reduction again with my proposal:
2 [dup] first (initial state)
2 [dup] first (after evaluating the function '2')
2 [dup] first (after evaluating the function '[dup]')
2 `dup (after evaluating the function 'first')
(Remember that quotation applies "push" to everything with it, so
'`dup' really is the first element of '[dup]'.)
This syntax has the advantages of zallambo's, but it has an important
additional benefit; each step of the reduction is itself a valid
program. To repeat, the syntax we use for the reduction can also be
used for writing programs.
- John
Don Groves — 2009-02-27 22:22:48
It's one thing to say that everything is a function in concatenative
languages
but confusion can arise if we don't distinguish between functions like
dup,
which manipulate objects already on the stack, and literals, whose
function
is to push themselves onto the stack.
In Joy, for example, there are three kinds of literals -- numbers,
text strings,
and lists. The function performed by all three is to push themselves
onto
the stack. Once on the stack, they are no longer functions, they are
data,
presumably to be operated on by some function in the future.
So, both [dup] and dup are functions but clearly have separate
semantics.
All languages have this problem and all handle it similarly using
quotations.
I don't see it as a problem the programmer has to worry about.
Even natural languages have the concept of "use" vs. "mention." When we
mention a word in writing, we put quotes around it to call attention
to the
fact that this particular word is not to parsed in the usual manner
(as in the
previous sentence). We even do this when speaking by using our fingers
to make air quotes. When we mean to use the word in it's usual context,
we omit the quotes.
Does this idea clear up possible confusion in this thread? If not,
sorry for
the waste of bandwidth.
--
don
On Feb 27, 2009, at 1:13 PM, John Nowak wrote:
>
> On Feb 27, 2009, at 2:37 PM, Stevan Apter wrote:
>
>> i appreciate your forebearance. i may have a useful analogy from
>> planet k.
>
> Thanks, that does make sense. Maybe I can put it in those terms.
>
> Take this expression:
>
> 2 [dup] first
>
> Here, '2' is a verb. So are '[dup]' and 'first'. Indeed, everything is
> a verb in a concatenative *program* (very careful emphasis on
> "program" there) because all terms denote functions.
>
> The problem occurs when you *reduce* the program. Let's go step by
> step:
>
> 2 [dup] first (initial state)
> 2 [dup] first (after evaluating the function '2')
> 2 [dup] first (after evaluating the function '[dup]')
> 2 dup (after evaluating the function 'first')
>
> The end of this evaluation is '2 dup'. The important thing here though
> is now '2' and 'dup' are nouns. You cannot further reduce '2 dup' to
> '2 2' even though the syntax makes it look like you should be able to.
>
> It's this syntactic confusion I want to address. I want to be able to
> do reductions like I did above--that is, without keeping an explicit
> stack--while making it clear which things are verbs and which things
> are nouns.
>
> To reiterate, nouns do not occur in the programs you write, but they
> *do* occur when reducing those programs.
>
> One way to solve this problem is to use some symbol, perhaps a quote
> as zallambo suggested, to indicate which things are nouns. Using this
> technique, we can do our reduction again:
>
> 2 [dup] first (initial state)
> '2 [dup] first (after evaluating the function '2')
> '2 '[dup] first (after evaluating the function '[dup]')
> '2 'dup (after evaluating the function 'first')
>
> In this case, we end up with "'2 'dup". It's clear from looking at the
> expression that we're done reducing the program. It avoids the
> confusion of looking at '2 dup' and not being sure if we're supposed
> to do reduce it to '2 2' or not.
>
> (Hopefully the issue I want to address is clear now. It may get fuzzy
> from this point on...)
>
> Unfortunately, I take issue with zallambo's proposal. My problem with
> it--if I understand it correctly--is that it creates two languages,
> one for writing programs and another to use when reducing them.
> Perhaps I'm being overly pedantic, but he said 'foo denotes an
> *object*, and objects are not part of the term language when writing a
> program. "All terms denote functions."
>
> My proposal differs from zallambo's in that I prefer to add a "push"
> functional form to the language itself so that you do not have a two
> syntaxes. Instead, you use the same syntax for writing a program and
> for reducing a program.
>
> The "push" form would be written with a back-tick and would indicate a
> function that pushes itself onto the stack; essentially, it self-
> evaluates like a quoted expression does in Scheme. Also as in Scheme,
> it would not be necessary to quote things which always evaluate to
> themselves (like numbers); only things that don't (namely functions).
>
> To make this model compatible with open quotations, quotation would
> apply "push" to every *noun* within it to lift them to verbs. For
> example, the following would be equivalent:
>
> `foo `bar [] cons cons
> [foo bar]
>
> Here's the same reduction again with my proposal:
>
> 2 [dup] first (initial state)
> 2 [dup] first (after evaluating the function '2')
> 2 [dup] first (after evaluating the function '[dup]')
> 2 `dup (after evaluating the function 'first')
>
> (Remember that quotation applies "push" to everything with it, so
> '`dup' really is the first element of '[dup]'.)
>
> This syntax has the advantages of zallambo's, but it has an important
> additional benefit; each step of the reduction is itself a valid
> program. To repeat, the syntax we use for the reduction can also be
> used for writing programs.
>
> - John
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>
Stevan Apter — 2009-02-27 23:00:36
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, February 27, 2009 4:13 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
everything on the stack is a noun, everything on the queue is a verb.
so we can rewrite your reduction, without ambiguity, as stack | queue:
| 2 [dup] first
2 | [dup] first
2 [dup] | first
2 dup |
but 'verb' and 'noun' are syntactic categories, not kinds of things.
so i would again say that all occurrences of "dup" above denote the
same thing, viz. the dup function.
> 2 [dup] first (initial state)
> 2 [dup] first (after evaluating the function '2')
> 2 [dup] first (after evaluating the function '[dup]')
> 2 dup (after evaluating
>
> On Feb 27, 2009, at 2:37 PM, Stevan Apter wrote:
>
>> i appreciate your forebearance. i may have a useful analogy from
>> planet k.
>
> Thanks, that does make sense. Maybe I can put it in those terms.
>
> Take this expression:
>
> 2 [dup] first
>
> Here, '2' is a verb. So are '[dup]' and 'first'. Indeed, everything is
> a verb in a concatenative *program* (very careful emphasis on
> "program" there) because all terms denote functions.
>
> The problem occurs when you *reduce* the program. Let's go step by step:
>
> 2 [dup] first (initial state)
> 2 [dup] first (after evaluating the function '2')
> 2 [dup] first (after evaluating the function '[dup]')
> 2 dup (after evaluating the function 'first')
>
> The end of this evaluation is '2 dup'. The important thing here though
> is now '2' and 'dup' are nouns. You cannot further reduce '2 dup' to
> '2 2' even though the syntax makes it look like you should be able to.
>
> It's this syntactic confusion I want to address. I want to be able to
> do reductions like I did above--that is, without keeping an explicit
> stack--while making it clear which things are verbs and which things
> are nouns.
>
> To reiterate, nouns do not occur in the programs you write, but they
> *do* occur when reducing those programs.
>
> One way to solve this problem is to use some symbol, perhaps a quote
> as zallambo suggested, to indicate which things are nouns. Using this
> technique, we can do our reduction again:
>
> 2 [dup] first (initial state)
> '2 [dup] first (after evaluating the function '2')
> '2 '[dup] first (after evaluating the function '[dup]')
> '2 'dup (after evaluating the function 'first')
>
> In this case, we end up with "'2 'dup". It's clear from looking at the
> expression that we're done reducing the program. It avoids the
> confusion of looking at '2 dup' and not being sure if we're supposed
> to do reduce it to '2 2' or not.
>
> (Hopefully the issue I want to address is clear now. It may get fuzzy
> from this point on...)
>
> Unfortunately, I take issue with zallambo's proposal. My problem with
> it--if I understand it correctly--is that it creates two languages,
> one for writing programs and another to use when reducing them.
> Perhaps I'm being overly pedantic, but he said 'foo denotes an
> *object*, and objects are not part of the term language when writing a
> program. "All terms denote functions."
>
> My proposal differs from zallambo's in that I prefer to add a "push"
> functional form to the language itself so that you do not have a two
> syntaxes. Instead, you use the same syntax for writing a program and
> for reducing a program.
>
> The "push" form would be written with a back-tick and would indicate a
> function that pushes itself onto the stack; essentially, it self-
> evaluates like a quoted expression does in Scheme. Also as in Scheme,
> it would not be necessary to quote things which always evaluate to
> themselves (like numbers); only things that don't (namely functions).
>
> To make this model compatible with open quotations, quotation would
> apply "push" to every *noun* within it to lift them to verbs. For
> example, the following would be equivalent:
>
> `foo `bar [] cons cons
> [foo bar]
>
> Here's the same reduction again with my proposal:
>
> 2 [dup] first (initial state)
> 2 [dup] first (after evaluating the function '2')
> 2 [dup] first (after evaluating the function '[dup]')
> 2 `dup (after evaluating the function 'first')
>
> (Remember that quotation applies "push" to everything with it, so
> '`dup' really is the first element of '[dup]'.)
>
> This syntax has the advantages of zallambo's, but it has an important
> additional benefit; each step of the reduction is itself a valid
> program. To repeat, the syntax we use for the reduction can also be
> used for writing programs.
>
> - John
>
Stevan Apter — 2009-02-27 23:09:44
i think what may be clouding the issue here is the very property of
concatenative languages we like, viz. that a syntactically well-formed
program cannot be distinguished from the contents of the stack. but
simply because the syntax of the program is *minimal* doesn't mean
that it has *no* syntax. indeed, every joy program has the syntactic
form
v v v v v ....
a concatenation of verbs, and every stack has the syntactic form
n n n n n ....
the queue, or program, 2 dup, is not identical to the stack consisting
of 2 dup, even though the elements of the program are identical to the
elements on the stack. in fact, this amgiguity, or asymmetry, is at the
heart of the XY evaluator semantics.
----- Original Message -----
From: "Stevan Apter" <sa@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, February 27, 2009 6:00 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> ----- Original Message -----
> From: "John Nowak" <john@...>
> To: <concatenative@yahoogroups.com>
> Sent: Friday, February 27, 2009 4:13 PM
> Subject: Re: [stack] function/object ambiguity + quotation alternative
>
>
> everything on the stack is a noun, everything on the queue is a verb.
> so we can rewrite your reduction, without ambiguity, as stack | queue:
>
> | 2 [dup] first
> 2 | [dup] first
> 2 [dup] | first
> 2 dup |
>
> but 'verb' and 'noun' are syntactic categories, not kinds of things.
> so i would again say that all occurrences of "dup" above denote the
> same thing, viz. the dup function.
>
>
>> 2 [dup] first (initial state)
>> 2 [dup] first (after evaluating the function '2')
>> 2 [dup] first (after evaluating the function '[dup]')
>> 2 dup (after evaluating
>>
>> On Feb 27, 2009, at 2:37 PM, Stevan Apter wrote:
>>
>>> i appreciate your forebearance. i may have a useful analogy from
>>> planet k.
>>
>> Thanks, that does make sense. Maybe I can put it in those terms.
>>
>> Take this expression:
>>
>> 2 [dup] first
>>
>> Here, '2' is a verb. So are '[dup]' and 'first'. Indeed, everything is
>> a verb in a concatenative *program* (very careful emphasis on
>> "program" there) because all terms denote functions.
>>
>> The problem occurs when you *reduce* the program. Let's go step by step:
>>
>> 2 [dup] first (initial state)
>> 2 [dup] first (after evaluating the function '2')
>> 2 [dup] first (after evaluating the function '[dup]')
>> 2 dup (after evaluating the function 'first')
>>
>> The end of this evaluation is '2 dup'. The important thing here though
>> is now '2' and 'dup' are nouns. You cannot further reduce '2 dup' to
>> '2 2' even though the syntax makes it look like you should be able to.
>>
>> It's this syntactic confusion I want to address. I want to be able to
>> do reductions like I did above--that is, without keeping an explicit
>> stack--while making it clear which things are verbs and which things
>> are nouns.
>>
>> To reiterate, nouns do not occur in the programs you write, but they
>> *do* occur when reducing those programs.
>>
>> One way to solve this problem is to use some symbol, perhaps a quote
>> as zallambo suggested, to indicate which things are nouns. Using this
>> technique, we can do our reduction again:
>>
>> 2 [dup] first (initial state)
>> '2 [dup] first (after evaluating the function '2')
>> '2 '[dup] first (after evaluating the function '[dup]')
>> '2 'dup (after evaluating the function 'first')
>>
>> In this case, we end up with "'2 'dup". It's clear from looking at the
>> expression that we're done reducing the program. It avoids the
>> confusion of looking at '2 dup' and not being sure if we're supposed
>> to do reduce it to '2 2' or not.
>>
>> (Hopefully the issue I want to address is clear now. It may get fuzzy
>> from this point on...)
>>
>> Unfortunately, I take issue with zallambo's proposal. My problem with
>> it--if I understand it correctly--is that it creates two languages,
>> one for writing programs and another to use when reducing them.
>> Perhaps I'm being overly pedantic, but he said 'foo denotes an
>> *object*, and objects are not part of the term language when writing a
>> program. "All terms denote functions."
>>
>> My proposal differs from zallambo's in that I prefer to add a "push"
>> functional form to the language itself so that you do not have a two
>> syntaxes. Instead, you use the same syntax for writing a program and
>> for reducing a program.
>>
>> The "push" form would be written with a back-tick and would indicate a
>> function that pushes itself onto the stack; essentially, it self-
>> evaluates like a quoted expression does in Scheme. Also as in Scheme,
>> it would not be necessary to quote things which always evaluate to
>> themselves (like numbers); only things that don't (namely functions).
>>
>> To make this model compatible with open quotations, quotation would
>> apply "push" to every *noun* within it to lift them to verbs. For
>> example, the following would be equivalent:
>>
>> `foo `bar [] cons cons
>> [foo bar]
>>
>> Here's the same reduction again with my proposal:
>>
>> 2 [dup] first (initial state)
>> 2 [dup] first (after evaluating the function '2')
>> 2 [dup] first (after evaluating the function '[dup]')
>> 2 `dup (after evaluating the function 'first')
>>
>> (Remember that quotation applies "push" to everything with it, so
>> '`dup' really is the first element of '[dup]'.)
>>
>> This syntax has the advantages of zallambo's, but it has an important
>> additional benefit; each step of the reduction is itself a valid
>> program. To repeat, the syntax we use for the reduction can also be
>> used for writing programs.
>>
>> - John
>>
>
John Nowak — 2009-02-28 00:03:07
On Feb 27, 2009, at 6:00 PM, Stevan Apter wrote:
> everything on the stack is a noun, everything on the queue is a verb.
> so we can rewrite your reduction, without ambiguity, as stack | queue:
>
> | 2 [dup] first
> 2 | [dup] first
> 2 [dup] | first
> 2 dup |
Indeed you can. Or you can rewrite it without ambiguity using an
explicit stack.
The trouble is that both approaches make it impossible to reduce in an
arbitrary order and at arbitrary levels. For example, I'd like to be
able to do the following:
1 2 3 + [[dup] first] [swap] first 5 2 +
1 2 3 + [`dup] [swap] first 5 2 +
1 2 3 + [`dup] [swap] first 7
1 2 3 + [`dup] `swap 7
1 5 [`dup] `swap 7
This is not possible if you use an explicit stack/queue for your
reduction. You need to make sure each step in the reduction is itself
a valid program.
- John
Stevan Apter — 2009-02-28 13:21:22
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, February 27, 2009 7:03 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> On Feb 27, 2009, at 6:00 PM, Stevan Apter wrote:
>
>> everything on the stack is a noun, everything on the queue is a verb.
>> so we can rewrite your reduction, without ambiguity, as stack | queue:
>>
>> | 2 [dup] first
>> 2 | [dup] first
>> 2 [dup] | first
>> 2 dup |
>
> Indeed you can. Or you can rewrite it without ambiguity using an
> explicit stack.
>
> The trouble is that both approaches make it impossible to reduce in an
> arbitrary order and at arbitrary levels. For example, I'd like to be
> able to do the following:
>
> 1 2 3 + [[dup] first] [swap] first 5 2 +
> 1 2 3 + [`dup] [swap] first 5 2 +
> 1 2 3 + [`dup] [swap] first 7
> 1 2 3 + [`dup] `swap 7
> 1 5 [`dup] `swap 7
>
> This is not possible if you use an explicit stack/queue for your
> reduction. You need to make sure each step in the reduction is itself
> a valid program.
sure. but `dup is just notation. it's a method of recording that dup
has changed its role in the reduction.
>
> - John
>
John Nowak — 2009-02-28 16:04:30
On Feb 28, 2009, at 8:21 AM, Stevan Apter wrote:
>> The trouble is that both approaches make it impossible to reduce in
>> an
>> arbitrary order and at arbitrary levels. For example, I'd like to be
>> able to do the following:
>>
>> 1 2 3 + [[dup] first] [swap] first 5 2 +
>> 1 2 3 + [`dup] [swap] first 5 2 +
>> 1 2 3 + [`dup] [swap] first 7
>> 1 2 3 + [`dup] `swap 7
>> 1 5 [`dup] `swap 7
>
> sure. but `dup is just notation. it's a method of recording that dup
> has changed its role in the reduction.
*explodes*
The whole point is that dup would not just serve that role! You could
write `dup and it would be a valid program or you could write `(dup
swap) and it would be a valid program. See my original email and this
comment in my other response to you:
> Unfortunately, I take issue with zallambo's proposal. My problem with
> it--if I understand it correctly--is that it creates two languages,
> one for writing programs and another to use when reducing them.
> Perhaps I'm being overly pedantic, but he said 'foo denotes an
> *object*, and objects are not part of the term language when writing a
> program. "All terms denote functions."
> ...
> This syntax has the advantages of zallambo's, but it has an important
> additional benefit; each step of the reduction is itself a valid
> program. To repeat, the syntax we use for the reduction can also be
> used for writing programs.
I don't see how I can be more clear. Perhaps you've just lost interest
awhile back. I know I have.
Time for my morning coffee...
- John
William Tanksley — 2009-03-02 18:36:35
Stevan Apter wrote:
> i don't want to spoil the party, but the fact is, i'm rarely in
> the position where i *want* to factor my code.
Yeah, well, you're a party-spoiler anyhow. :-)
> my building blocks
> are not words or code-fragments, they're *functions*.
I do not understand this. "Word" is the Forth term for what other
languages call "functions". And most of what other languages call
"functions" are built of code-fragments.
Being able to put together and take apart functions/words is pretty
useful to programming. That's what the functional programming mavens
talk about with their "referential transparency"... and concatenative
languages are a bit better at that.
In brief, I don't understand why you'd "complain" about being _able_ to
refactor code more easily. I understand that you want to analyse
requirements, architect solutions, and detail-design perfectly the first
time... But since none of these are possible, it makes sense to write
code that's as mutable as possible.
> typically,
> a function evolves over time in response to demands from the
> environment by acquiring new arguments and/or producing results
> with new components. the large-scale "topography" of the system
> is relatively stable.
> (come to think of it, this is a pretty
> good metaphor for how systems grow in the real world.)
Good for you -- I'm not so lucky. For me there are two ways that a
system changes: either we fix problems in the code that implemented the
old requirements, or new requirements get added. In both cases the
architecture usually has to change at least a bit.
> if i have
> to dig into the functions and move snippets of code around into
> a new functional architecture, then my original design was pitched
> at the wrong "level."
It probably _was_ pitched at the wrong level. This happens all the time.
> the kind of refactoring i want to avoid
> (because it is the most painful kind) is *renaming*. going from
> a set of several hundred functions a,b,c,... to a different set
> x,y,z,... .
I would classify renaming as the simplest kind of refactoring -- it can
usually be done with a simple search and replace, and then let the
compiler tell you if you missed anything. I wouldn't make a million name
changes at the same time, of course.
> code is committed to a function with a name when
> a judgement is made that that functionality will endure for the
> lifetime of the system, and that changes will for the most part
> be extensions rather than revisions.
I expect that from code that's been in production use for 10 years.
Nothing younger.
-Wm
William Tanksley — 2009-03-02 18:44:41
John Nowak wrote:
> I think you're absolutely right. Forth and friends *demand* short
> words, and consequently, demand factoring.A language like Scheme
> makes it harder to factor, but the nesting and locals means much
> larger definitions are readable, and consequently, much less factoring
> is required.
There's truth in what you're saying, but you're missing an important
fact: short definitions are easier to read in _any_ language. I admit
that it's important to be able to express longer words.
I do agree that Forth is a system language, with poor facilities for any
higher-level work. I don't concede anything :-) about "Forth and
friends", since I don't know what languages you're refering to.
It's good to be free from the restrictions of Forth. It's also good to
keep the benefits Forth has historically enjoyed, one of which is easy
refactoring.
Factor's EMACS mode, "Fuel", supports automated refactoring.
> - John
-Wm
John Nowak — 2009-03-02 20:55:38
On Mar 2, 2009, at 1:44 PM, William Tanksley wrote:
> John Nowak wrote:
>
>> I think you're absolutely right. Forth and friends *demand* short
>> words, and consequently, demand factoring.A language like Scheme
>> makes it harder to factor, but the nesting and locals means much
>> larger definitions are readable, and consequently, much less
>> factoring
>> is required.
>
> There's truth in what you're saying, but you're missing an important
> fact: short definitions are easier to read in _any_ language. I admit
> that it's important to be able to express longer words.
You're right; I didn't mean to imply otherwise. Ideally, a language
should make writing long definitions possible (which I often find
useful in Scheme when prototyping and experimenting) and short
definitions elegant (which concatenative languages do well). I don't
know of a language that satisfies both of these criteria to the degree
I'd like, although some do come fairly close.
> I do agree that Forth is a system language, with poor facilities for
> any
> higher-level work. I don't concede anything :-) about "Forth and
> friends", since I don't know what languages you're refering to.
I was referring to stack-based languages in general; the average
length of a function definition needs to be shorter than in a language
like Scheme to achieve the same level of readability. The nesting in
Scheme helps visually and things remain mostly understandable even if
you don't know the arities of all the functions involved. That's only
my opinion of course; I've not launched a study to determine if this
goes for most people, although I suspect it does.
> Factor's EMACS mode, "Fuel", supports automated refactoring.
Aye, something like this likely helps by reducing the incentive to
write large definitions in the first place. It's a shame I've never
managed to convince myself to learn Emacs.
- John
William Tanksley — 2009-03-03 17:06:49
John Nowak wrote:
> Ideally, a language
> should make writing long definitions possible (which I often find
> useful in Scheme when prototyping and experimenting) and short
> definitions elegant (which concatenative languages do well).
That's certainly true. A long definition that's in a considerable state
of flux _can_ be rather hard to juggle around in a concatenative
language. That's one time when I'd personally forgive the use of local
variables :-), although I'd rather see the programmer spend some time
back at the drawing board (or experimenting with primitives) first.
Let's face it: if you don't know what you're doing yet, you're picking a
poor time to commit to code. The overhead of stack manipulations is only
a part of the problem you're going to face.
I admit that I've done a decent amount of exploratory programming in
concatenative languages; and I don't think they're any more unpleasant
in practice (although clearly in theory they're worse). You WILL wind up
with some nasty stack shuffles, but I think that's part of the penalty
of exploratory programming (leftover and/or misnamed temporary variables
are essentially the same thing). If you're careful to think at every
step you'll minimize the shuffling, and often by the end you'll
understand the problem well enough to do a complete rewrite _correctly_.
> > I do agree that Forth is a system language, with poor facilities for
> > any
> > higher-level work. I don't concede anything :-) about "Forth and
> > friends", since I don't know what languages you're refering to.
> I was referring to stack-based languages in general; the average
> length of a function definition needs to be shorter than in a language
> like Scheme to achieve the same level of readability.
If your only measurement is how a syntax helps you see arities, then
applicative languages win by acclamation. But there's more to reading a
program than knowing the arities of the functions. Concatenative
languages can reveal or conceal a lot more about dataflow, and that can
help (or hinder) the reading as well. Depends on what tools the language
has, and which ones the programmer uses.
> The nesting in
> Scheme helps visually and things remain mostly understandable even if
> you don't know the arities of all the functions involved. That's only
> my opinion of course; I've not launched a study to determine if this
> goes for most people, although I suspect it does.
Forth is the only concatenative language we've discussed that lacks
nesting. Why would you list "nesting" as an advantage of associative
languages?
> > Factor's EMACS mode, "Fuel", supports automated refactoring.
> Aye, something like this likely helps by reducing the incentive to
> write large definitions in the first place. It's a shame I've never
> managed to convince myself to learn Emacs.
Heh. I'm a vim guy myself. But after so much time waiting for a C
refactorer, it's amusing to see one pop up for Factor so quickly. It's
actually becoming reasonably competent.
> - John
-Wm
John Nowak — 2009-03-03 19:29:07
On Mar 3, 2009, at 12:06 PM, William Tanksley wrote:
>> The nesting in
>> Scheme helps visually and things remain mostly understandable even if
>> you don't know the arities of all the functions involved. That's only
>> my opinion of course; I've not launched a study to determine if this
>> goes for most people, although I suspect it does.
>
> Forth is the only concatenative language we've discussed that lacks
> nesting. Why would you list "nesting" as an advantage of associative
> languages?
I was being sloppy. I've certainly proposed "concatenative" languages
with as much nesting as you find in Scheme.
I really was meaning to compare idiomatic ways of writing code in
concatenative languages. Take the following code for example:
foo bar [baz] dip quux
You will, in practice, get concatenative code like this. Now compare
to what might be the applicative equivalent:
(let ([(x y z) (foo input)]
[(b c) (baz y z)])
(quux (bar x) b c))
Here, it's clear that foo returns three values, bar only cares about
one, baz only cares about two, bar uses the first result of foo, baz
uses the second two, baz is independent of bar, quuz uses one result
from bar and two from baz, etc.
Getting all of this information out of the concatenative version isn't
possible. Does bar use one value? Two? More? What about quux? Does baz
depend on the result of bar? All of this is clear in the applicative
version but not the stack-based concatenative version.
Again, in practice, I occasionally find this difficult when writing
code quickly to prototype something. If you don't, then I certainly
won't call you a liar. Perhaps I just have Scheme burned into my head.
- John
Stevan Apter — 2009-03-03 19:57:31
let me register some dissenting opinions here (no surprise, right?)
these have been buzzing around in my head since billy's response to
my "anti-factoring" post.
i expect i'll meet with some resistance here, since i can't provide
an overwhelmingly persuasive argument (that's why they're opinions.)
if they spark some ideas, that's enough.
i can say the same thing in several ways: compared to other languages
i know, k call-trees are smaller, there are fewer names in play, and
a greater proportion of the tokens in a k program are primitives. (you
can see an example of this in the game-of-life function i posted a few
days ago.)
the question whether one should write long or short programs hardly
ever arises. a program -- a function with a name -- should represent
"a thought". of course, x+y is a thought, and so is x+y*z, so this
maxim isn't precise enough to be useful. perhaps we get into the
habit of adjusting the way we think about problems to the language
we're used to, and therefore thinking in ways that experience has
taught us are "best" for that language. probably this is true to
some extent.
in k, programs will call other programs, but there's not a whole lot
of complexity at that level. when you look inside a program, mostly
what you see are primitives and the arguments to the program. (i'm
not sure this is as true in j, especially the way masters of that
language like roger hui write code.)
the primitives are individually powerful and they fit together in ways
that allow for great expressiveness. usually it's possible to express
"a thought" -- something you can give a name to, and use and remember
easily -- in a line or two or three. short enough to be "surveyable",
not so short that you need to look at calling-context to understand
meaning. another way of saying this is that the k programmer spends
most of his time *in primitives*.
i think factoring becomes useful to the programmer when the primitives
are so weak that certain phrases, certain ways of combining them,
appear repeatedly in different contexts. so then you wind up abstracting
those phrases and giving them names. and you wind up with lots and
lots of these things, and deep, complicated call-hierarchies. it's
as though you didn't have + in the language, and you find it natural
to define different special cases of addition for different contexts.
since we are often called upon to work with aggregates of different
kinds -- sets, bags, lists, vectors, dictionaries, tables, &c. --
and our primitives are for the most part scalar (again, often of
different kinds) we wind up writing lots of code for different
special cases. inevitably, this will result in situations where
subtle variations on a single idea will manifest as similar code-
fragments, and we'll go on to factor, combine, rename, &c. that's
a lot of complexity to manage, and i think it arises just because
the primitives are too weak and their combinations are insufficiently
expressive.
this situation simply does not arise in k, or rather, it arises much
less frequently. this is because certain mathematical relations
exist between different aggregate types, and the primitives are
designed to exploit those relations. (e.g. the transpose of a
dictionary of lists is a table, that is, a list of similar atomic
dictionaries.) so x+y works predictably (and usefully!) on all
(conformable) aggregates.
i've had this suspicion for quite a while -- i don't know how to prove
it. if you were enumerate all combinations of k primitives in 1, 2, and 3
variables (x, y, and z), you'd find a greater density of "useful"
and "interesting" programs in the set of combinations of length < 10.
an example i've used before is the remarkable function {x y z x}, which
can be used for both converging boolean selection on a table and multi-
column table sorting. who would have thought? this and other examples
had led me to think that programming k was like taking a botanical field
trip to the amazon in search of exotic new flora.
this is also why i think enchilada is an extremely promising language.
robbert has designed a handful of very powerful primitives, which
interact in ways which even he, the language designer, can't fully
predict. hence the different proposals over the last two years, each
of which has made use of discoveries made in the course of solving
problems in the previous version. i interpret this "fruitfulness",
where application of the language generates insights into the *set
of primitives*, as a sign that enchilada offers a new way of thinking
about problems. i mean to contrast this with the way i've seen most
languages evolve, which is to add control structures, datatypes, "OO",
compiler technologies, &c., and to ignore the primitives.
anyway, these are just some thoughts off the top of my head.
----- Original Message -----
From: "William Tanksley" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Tuesday, March 03, 2009 12:06 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
> John Nowak wrote:
>> Ideally, a language
>> should make writing long definitions possible (which I often find
>> useful in Scheme when prototyping and experimenting) and short
>> definitions elegant (which concatenative languages do well).
>
> That's certainly true. A long definition that's in a considerable state
> of flux _can_ be rather hard to juggle around in a concatenative
> language. That's one time when I'd personally forgive the use of local
> variables :-), although I'd rather see the programmer spend some time
> back at the drawing board (or experimenting with primitives) first.
>
> Let's face it: if you don't know what you're doing yet, you're picking a
> poor time to commit to code. The overhead of stack manipulations is only
> a part of the problem you're going to face.
>
> I admit that I've done a decent amount of exploratory programming in
> concatenative languages; and I don't think they're any more unpleasant
> in practice (although clearly in theory they're worse). You WILL wind up
> with some nasty stack shuffles, but I think that's part of the penalty
> of exploratory programming (leftover and/or misnamed temporary variables
> are essentially the same thing). If you're careful to think at every
> step you'll minimize the shuffling, and often by the end you'll
> understand the problem well enough to do a complete rewrite _correctly_.
>
>> > I do agree that Forth is a system language, with poor facilities for
>> > any
>> > higher-level work. I don't concede anything :-) about "Forth and
>> > friends", since I don't know what languages you're refering to.
>
>> I was referring to stack-based languages in general; the average
>> length of a function definition needs to be shorter than in a language
>> like Scheme to achieve the same level of readability.
>
> If your only measurement is how a syntax helps you see arities, then
> applicative languages win by acclamation. But there's more to reading a
> program than knowing the arities of the functions. Concatenative
> languages can reveal or conceal a lot more about dataflow, and that can
> help (or hinder) the reading as well. Depends on what tools the language
> has, and which ones the programmer uses.
>
>> The nesting in
>> Scheme helps visually and things remain mostly understandable even if
>> you don't know the arities of all the functions involved. That's only
>> my opinion of course; I've not launched a study to determine if this
>> goes for most people, although I suspect it does.
>
> Forth is the only concatenative language we've discussed that lacks
> nesting. Why would you list "nesting" as an advantage of associative
> languages?
>
>> > Factor's EMACS mode, "Fuel", supports automated refactoring.
>> Aye, something like this likely helps by reducing the incentive to
>> write large definitions in the first place. It's a shame I've never
>> managed to convince myself to learn Emacs.
>
> Heh. I'm a vim guy myself. But after so much time waiting for a C
> refactorer, it's amusing to see one pop up for Factor so quickly. It's
> actually becoming reasonably competent.
>
>> - John
>
> -Wm
>
>
>
John Nowak — 2009-03-03 20:30:20
On Mar 3, 2009, at 2:57 PM, Stevan Apter wrote:
> this situation simply does not arise in k, or rather, it arises much
> less frequently. this is because certain mathematical relations
> exist between different aggregate types, and the primitives are
> designed to exploit those relations. (e.g. the transpose of a
> dictionary of lists is a table, that is, a list of similar atomic
> dictionaries.) so x+y works predictably (and usefully!) on all
> (conformable) aggregates.
Is Q similar to K in this regard? Where would you suggest one look to
get a deeper understanding of what you're talking about? I've gone
through Q for Mortals but I don't feel like I've gotten the full
picture.
Also, how important to do think it is that primitives in K work on
aggregates rather than scalars? For example, would it somehow not work
as well if it were required to indicate that you intended to apply a
function to each element (using some concise syntax) instead of having
it happen "automatically"? I ask because something like this would be
necessary for typing the sort of things you do in K, and
unfortunately, I'm not interested if I can't type it. Boring, I know.
- John
Stevan Apter — 2009-03-04 00:13:21
----- Original Message -----
From: "John Nowak" <john@...>
To: <concatenative@yahoogroups.com>
Sent: Tuesday, March 03, 2009 3:30 PM
Subject: Re: [stack] function/object ambiguity + quotation alternative
>
> On Mar 3, 2009, at 2:57 PM, Stevan Apter wrote:
>
>> this situation simply does not arise in k, or rather, it arises much
>> less frequently. this is because certain mathematical relations
>> exist between different aggregate types, and the primitives are
>> designed to exploit those relations. (e.g. the transpose of a
>> dictionary of lists is a table, that is, a list of similar atomic
>> dictionaries.) so x+y works predictably (and usefully!) on all
>> (conformable) aggregates.
>
> Is Q similar to K in this regard? Where would you suggest one look to
> get a deeper understanding of what you're talking about? I've gone
> through Q for Mortals but I don't feel like I've gotten the full
> picture.
q is k with keywords for unary primitives, e.g. 'count x' in q for
'#x' in k. i use q, only a few people use k. the distinction makes
no difference for the topic at hand.
unfortunately, i don't know what to suggest short of full immersion
in a large-scale k project. i suppose it's possible to contemplate
a large set of one-liners (e.g. eugene mcdonnell's finger exercises),
or the occasional tour de force from arthur whitney (sudoku), and
find there some evidence for my opinions about factoring.
>
> Also, how important to do think it is that primitives in K work on
> aggregates rather than scalars? For example, would it somehow not work
> as well if it were required to indicate that you intended to apply a
> function to each element (using some concise syntax) instead of having
> it happen "automatically"? I ask because something like this would be
> necessary for typing the sort of things you do in K, and
> unfortunately, I'm not interested if I can't type it. Boring, I know.
i don't know if it is necessary for typing. i've heard lots of claims
over the years that this and that are impossible in k, and of course that's
true ... until someone actually does this and that. can k be typed?
if not, how close can you get to k with a typed language? is that close
enough?
anyway, my goal wasn't to shift the conversation to k per se, but to
suggest that this concern with factoring might be an artifact of certain
types of languages rather than a universal desideratum.
>
> - John
>
William Tanksley — 2009-03-04 00:22:34
John Nowak wrote:
> William Tanksley wrote:
> >> The nesting in
> >> Scheme helps visually and things remain mostly understandable even if
> >> you don't know the arities of all the functions involved. That's only
> I really was meaning to compare idiomatic ways of writing code in
> concatenative languages. Take the following code for example:
> foo bar [baz] dip quux
> You will, in practice, get concatenative code like this. Now compare
> to what might be the applicative equivalent:
> (let ([(x y z) (foo input)]
> [(b c) (baz y z)])
> (quux (bar x) b c))
> Here, it's clear that foo returns three values, bar only cares about
> one, baz only cares about two, bar uses the first result of foo, baz
> uses the second two, baz is independent of bar, quuz uses one result
> from bar and two from baz, etc.
Okay, I agree. But there's another angle to look at this.
In many problem domains, clear and communicative names can be chosen
rather than "foo" and "bar". If that is done (and it normally IS a goal,
even if one sometimes honored in the breach), which code chunk "wins"?
I'd say it's pretty simple -- the code chunk that looks like it's
communicating in the problem domain (with one apparantly random word,
"dip"). The applicative language may use a little of the vocabulary of
the problem domain, but it's obscured in a tangle of parentheses that
may document to the programmer what parameters go where, but don't mean
much to the domain expert who should be able to check at least your
top-level algorithm.
> Getting all of this information out of the concatenative version isn't
> possible. Does bar use one value? Two? More? What about quux? Does baz
> depend on the result of bar? All of this is clear in the applicative
> version but not the stack-based concatenative version.
The naive assumption is that every word depends on its predecessor --
that the order is utterly critical. This assumption won't let you down,
but it won't give you the best performance, either (and it certainly
won't help you analyse the code when there are bugs). To do better, you
need a smart enough development environment to tell you the signature of
each word -- or you flip open a glossary and look at the definitions of
the words. It's an extra step, but it's worth it.
> Again, in practice, I occasionally find this difficult when writing
> code quickly to prototype something. If you don't, then I certainly
> won't call you a liar. Perhaps I just have Scheme burned into my head.
I think you meant "when reading code". When writing, you either remember
or you have to look it up; the parens won't help you, because you're the
one who's writing them. With that assumption:
Remembering stack effects without a reference handy is not natural for
humans. No argument. Because of that, names and parens DO give an assist
which _is_ useful. But those are only useful for programmers in that one
context; they're an obstruction in all other contexts, much like stack
shuffle words are. The difference is that stack shuffle words are agreed
to be bad, and are avoided by all; parens and variables are claimed to
be unavoidable or even beneficial.
They're not unavoidable (concatenative languages don't need 'em), and
although they are beneficial in some circumstances, they aren't in all.
Of course, the ideal solution isn't permanent syntax; it's a tool that
underlays the code with syntactic cues.
> - John
-Wm
John Nowak — 2009-03-04 00:51:49
On Mar 3, 2009, at 7:13 PM, Stevan Apter wrote:
> i don't know if it is necessary for typing. i've heard lots of claims
> over the years that this and that are impossible in k, and of course
> that's
> true ... until someone actually does this and that. can k be typed?
> if not, how close can you get to k with a typed language? is that
> close
> enough?
I realize those are rhetorical questions, but...
The language prototype I'm working on now does take some vague
inspiration from Q. For example, you can define a 'map' function named
(') as such:
' = -x(n,x.{F,'F})
In the above definition, '-x' is a combining form that takes two
functions. The first says what to do if the list passed to it is null
(return null), and the second says what to do if passed a cons (apply
some function F to the head, apply 'F to the tail, then cons the
result).
You can use the map function as such:
'sq.[1,2,3,4,5] # [1,4,9,16,15]
Another example would be fold:
/ = +x(i,/F.[c,F.t2])
Here, '+x' is like '-x' except it takes additional values that are
passed to both functions. The first argument to '+x' returns the
additional values ('i' is identity) and the second folds some function
F over tail of the list and F applied to the accumulated value and
head of the list.
An example:
/+.[[1,2,3,4,5],0] # 15
Alright, it's not that much like Q. For one, it works on cons cells
instead of arrays. It does have a nice advantage though: It's
statically typed.
Just a little brain dump...
- John
John Nowak — 2009-03-04 01:02:09
On Mar 3, 2009, at 7:51 PM, John Nowak wrote:
> ' = -x(n,x.{F,'F})
> / = +x(i,/F.[c,F.t2])
I forgot to post them with the alternate infix syntax which is kind of
cute. I'll give a better explanation as well.
'a b c d e' groups to 'a b (c d e)' and is basically another way of
writing the construction form (this is a lot like J); '.' is
composition if that wasn't obvious; 'c' selects the third element in a
list ('a' is the first, 'b' is second, etc); 't2' takes the first two
elements of the list passed to it; 'x' is cons; {} is "spread":
' = n -x x.{F,'F}
/ = i +x c /F F.t2
I think they're a lot better than the equivalent definitions in Joy or
Factor. Certainly less unrelated words needed.
- John
William Tanksley — 2009-03-04 22:03:41
John Nowak wrote:
> I've hinted at this recently, but never properly explained it. It may
> be entirely uninteresting or even an incorrect observation, so I'd
> appreciate a whack on the hand if I'm off track. I think, however,
> that it may be important in letting us further develop a firm
> theoretical basis for concatenative languages.
I think you're dead-on, and your solution is something that I've
suggested in the past. However, I also admire the ambiguity between
functions and literals that you first described to us, so I'd like to
also think of a solution that might save it.
I'm not sure that I've succeeded, but I need to send this someday.
> This seem to be a unique property. I have never seen a non-
> concatenative language in which every term denoted a function.
Agreed.
> With concatenative languages, we always use the second form for
> expressing the semantics of some function, e.g. 'X dup == X X'. We do
> this because we have no syntax for application and no way of
> expressing objects. There's something strange here though. Because all
> terms denote functions, 'X' must be a function. However, 'X' cannot be
> any function; it must be a function that pushes a value (and always
> the same value) onto a stack.
Yes. This is why Kerby always places his stack variable notations in
brackets:
[X] dup == [X] [X]
I've often suggested adding a notation to quote only a single symbol
(without enclosing it in a list). You follow this approach below.
> I believe I know how to solve this problem: Get rid of the function/
> object ambiguity.
I see your point.
The problem is, we both like the function/object ambiguity. (x) denotes
a function or an object, no real difference except that the object
always changes the stack in the exact same way -- so the object is like
a function.
> To do this, we drop the requirement that all terms denote functions.
> '42' will now denote the *number* 42. To push 42 onto the stack, we'll
> write '`42', i.e. the push function '`' applied to the object 42.
This is something I've proposed before. I didn't think of making it
apply to numbers; I only wanted it for functions, because:
[dip] first == `dip
Making it apply to groupings is an obvious enhancement which improves
the algebra:
[dip dup] == `(dip dup)
Making it apply to numbers and literals in general is needless; it'll
work, but it should be a noop.
> The presence of '`' allows us to get rid of quotation. Instead of '[+]
> map', we would write '`+ map'; the '`' function applied to the '+'
> function yields a new function that pushes the '+' function onto a
> stack. We'd also need to add parentheses for grouping; you'd write
> '`(foo bar)' instead of '[foo bar]'.
I'm quite happy with this. It makes sense to be able to distinguish
between grouping and quotation.
> This allows us a solution to the problem with Factor's array literals
> shown above: Array literals would only be allowed to contain terms
> representing objects. For example, '`{ 42 `42 }' would be a function
> that pushes an array onto a stack where the first element of the array
> is the number 42 and the second element is a function that pushes 42
> onto a stack.
Yes, I think I see why this is needed. It's nice to note that (`) is
distributive over grouping:
`(42 `42) == (`42 ``42) == `42 ``42 compose == 42 42 compose
> I would also suggest adding an additional array syntax that takes
> *functions* that evaluate to single objects.
Why only for arrays? It would make more sense to me to make a special
group syntax that forces the evaluation of the group at the time it's
constructed rather than the usual runtime. So an array might look like:
{ ,(2 1 drop) }
(I'm using , because it seems like the mirror of `.)
It's an ordinary array, with a dequoted group inside it. You can also
make an ordinary quoted group with a dequoted group (or even a dequoted
function) inside it, if you want.
> In Joy, where creating multiple "copies" of the stack is cheap, you
> could use my parallel "banana" combinator to construct lists. For
> example, you could write the function '2 3 (|+ -|)' which would be
> equivalent to the function '`{5 -1}' (assuming we also adopted closed
> quotations for Joy and added '{ }' as a list literal syntax).
I agree that this would be a great way to structure the banana
combinator. I don't know why this would only work on Joy -- your banana
combinator doesn't ever copy the stack, or at least it doesn't require a
stack copy. Or perhaps I'm thinking of a slightly modified version that
I talked about -- I forget the details now. I'd say that copying the
stack is a really, really bad habit... or at best, a habit that there's
no good reason for and much good reason against.
> Since we have lost the object/function ambiguity, we can now express
> the semantics of our functions in terms of application. I propose the
> syntax '<object>:<function>' to indicate an application. Assuming our
> concatenative language is stack-based, the object will always be a
> stack.
Wait, why couldn't this be done in the past? I don't see how this is any
different from what we'd done before. We didn't use that specific
notation, but we used similar notation.
> Alternatively, we can express our semantics in terms of equivalencies.
> This isn't as generally useful as the application form above (e.g. you
> can't express 'clear' in this form), but it is perhaps a bit easier to
> read:
> `42 == `42
> `X dup == `X `X
> `X `Y swap == `Y `X
> `F i == F
> `X `F dip == F `X
Equivalencies have some nice properties of their own... A function which
can't be thus expressed has some usability problems.
Actually, that's kind of an interesting statement. I wonder why it seems
true to me? Nothing about the definition of a concatenative language
seem
> - John
-Wm
John Nowak — 2009-03-04 23:04:48
On Mar 4, 2009, at 5:03 PM, William Tanksley wrote:
> Why only for arrays? It would make more sense to me to make a special
> group syntax that forces the evaluation of the group at the time it's
> constructed rather than the usual runtime. So an array might look
> like:
>
> { ,(2 1 drop) }
You're right, this is a better approach. Essentially, this would be
the same thing as quasiquote in Scheme, and with the same syntax to
boot. You've probably already realized this. For example, this:
`(+ ,(+ 1 2) (+ 3 4))
reduces to this:
(+ 3 (+ 3 4))
> I agree that this would be a great way to structure the banana
> combinator. I don't know why this would only work on Joy -- your
> banana
> combinator doesn't ever copy the stack, or at least it doesn't
> require a
> stack copy.
It does require a stack copy in a naive implementation because an
identical stack is given to every function. Factor can typically avoid
the stack copy thanks to its stack inference.
> I'd say that copying the stack is a really, really bad habit... or
> at best, a habit that there's no good reason for and much good
> reason against.
Copying it if it's expensive is bad; if it's cheap, it's very useful.
In Factor, each conditional in 'cond' can alter the stack for the next
conditional. You need to use a lot of superfluous 'dup' functions to
avoid problems, and it removes a guarantee that you take for granted
in most languages (provided that you're not mutating any values in
your conditional).
Things like this are the reason I'm not using Factor; I don't find
that sort of thing acceptable. Maybe Factor's stack inference could be
used to implement a 'cond' without these problems, but that's beyond
me unfortunately. Then again, I'm not a fan of smart combinators anyway.
>> Since we have lost the object/function ambiguity, we can now express
>> the semantics of our functions in terms of application. I propose the
>> syntax '<object>:<function>' to indicate an application. Assuming our
>> concatenative language is stack-based, the object will always be a
>> stack.
>
> Wait, why couldn't this be done in the past?
My thinking was that if it's clear what is an object and what is a
function, then we can put only objects on the left and only functions
on the right. Without a way to represent objects, you can't really
represent application. This is not a problem in practice; Factor
certainly has a REPL that shows a stack with objects on it. I just
wanted to make things a bit more clear.
- John
William Tanksley — 2009-03-05 18:03:13
John Nowak wrote:
> William Tanksley wrote:
> > I agree that this would be a great way to structure the banana
> > combinator. I don't know why this would only work on Joy -- your
> > banana
> > combinator doesn't ever copy the stack, or at least it doesn't
> > require a stack copy.
> It does require a stack copy in a naive implementation because an
> identical stack is given to every function. Factor can typically avoid
> the stack copy thanks to its stack inference.
I'd simply not perform the stack copy -- if the programmer wants to
preserve the stack, they can do that by writing programs that have the
inputs and outputs that the combinator documents as required. That's a
good habit. In fact, NOT doing it is such a disastrously bad habit that
I don't understand why we're desiring to protect programmers from the
consequences thereof.
It's a very, very easy thing to ask for, and extremely easy to inference
(if you want to do that). What do we gain by not asking for it?
> > I'd say that copying the stack is a really, really bad habit... or
> > at best, a habit that there's no good reason for and much good
> > reason against.
> Copying it if it's expensive is bad; if it's cheap, it's very useful.
I concede that copying the stack is not "a bad habit". I overspoke. I
disagree that it's useful, except for allowing programmers to write code
that is bad in other ways (essentially, for producing side effects that
the compiler promises to throw away).
> In Factor, each conditional in 'cond' can alter the stack for the next
> conditional. You need to use a lot of superfluous 'dup' functions to
> avoid problems, and it removes a guarantee that you take for granted
> in most languages (provided that you're not mutating any values in
> your conditional).
If you want that guarantee, you can use 'case' instead of 'cond'. If
you're using 'cond' you're going to either use 'dup' (although I'd argue
that there's nothing superfluous, and certainly not "a lot") or you're
working your way through the stack for some other reason.
> Things like this are the reason I'm not using Factor; I don't find
> that sort of thing acceptable. Maybe Factor's stack inference could be
> used to implement a 'cond' without these problems, but that's beyond
> me unfortunately. Then again, I'm not a fan of smart combinators anyway.
This is precisely why I don't like stack copying combinators: they claim
to be smart about removing "accidental" side effects. How does it know
which effects are accidental? Read the documentation. Well, strangely
enough, that's exactly what I'd have to do in order to write code
WITHOUT accidental side effects.
> >> Since we have lost the object/function ambiguity, we can now express
> >> the semantics of our functions in terms of application. I propose the
> >> syntax '<object>:<function>' to indicate an application. Assuming our
> >> concatenative language is stack-based, the object will always be a
> >> stack.
> > Wait, why couldn't this be done in the past?
> My thinking was that if it's clear what is an object and what is a
> function, then we can put only objects on the left and only functions
> on the right. Without a way to represent objects, you can't really
> represent application. This is not a problem in practice; Factor
> certainly has a REPL that shows a stack with objects on it. I just
> wanted to make things a bit more clear.
I do like the notation you propose. I hope we see it regularly. The
current equivalent is quoted single letter, which is good enough if you
assume/require closed quotations.
> - John
-Wm
John Nowak — 2009-03-05 22:09:18
On Mar 5, 2009, at 1:03 PM, William Tanksley wrote:
> John Nowak wrote:
>
>> William Tanksley wrote:
>>
>>> I agree that this would be a great way to structure the banana
>>> combinator. I don't know why this would only work on Joy -- your
>>> banana combinator doesn't ever copy the stack, or at least it
>>> doesn't
>>> require a stack copy.
>>
>> It does require a stack copy in a naive implementation because an
>> identical stack is given to every function. Factor can typically
>> avoid
>> the stack copy thanks to its stack inference.
>
> I'd simply not perform the stack copy -- if the programmer wants to
> preserve the stack, they can do that by writing programs that have the
> inputs and outputs that the combinator documents as required.
The banana combinator gives all functions access to the "same" stack.
You have no choice but to copy the stack or to infer the stack effects
of all functions in the combinator and pass them the appropriate sub-
stacks.
> That's a good habit. In fact, NOT doing it is such a disastrously
> bad habit that I don't understand why we're desiring to protect
> programmers from the consequences thereof.
I'm not solely suggesting you do it to protect programmers from
mistakes; I'd also suggest you do it because it allows for more things
to be conveniently expressed.
> I concede that copying the stack is not "a bad habit". I overspoke.
> I disagree that it's useful
I'm going to be irritating and claim that you're not being imaginative
enough. Here's a function I often use in Joy programming:
S [x0..xN] [F] map/w == S [[S x0 F] first .. [S xN F] first]
Essentially, each application of the function gets access to one
element of the list and the entire stack. Unlike the 'map' combinator
in Factor however, the function does not need to be careful to copy
things around on the stack so that successive invocations of the
function get the same view of the stack. Not only does this make
reasoning easier, but you also can do nice things like this:
5 [1 2 3] [+] map/w == 5 [6 7 8]
This is just one example of course. You can probably think of others.
> If you want that guarantee, you can use 'case' instead of 'cond'.
Case is less general than cond.
> If you're using 'cond' you're going to either use 'dup' (although
> I'd argue
> that there's nothing superfluous, and certainly not "a lot") or you're
> working your way through the stack for some other reason.
Yeah, you can make it work. I'd prefer to just have it guaranteed.
Having one conditional alter what the next one sees is never
desirable. If you want that, use nested conditionals and make it
explicit. Ruling out undesirable behavior is a good thing. If you're
using a language like Joy where "copying" the stack is cheap, I can't
see why you *wouldn't* want a 'cond' that works such a way.
> This is precisely why I don't like stack copying combinators: they
> claim
> to be smart about removing "accidental" side effects. How does it know
> which effects are accidental?
That's not what I'd say their claim is; see the 'map/w' example above
or the banana combinator:
4 3 2 (| + , - |) == 4 5 1
- John