# Re: [stack] Fw: the unary combinator

## wtanksleyjr@cox.net — 2003-05-21 15:19:50

    From: "stevan apter" <sa@...>

     >i have a question about the unary combinator.

     Yes -- I don't blame you. It's ugly and non-optimal.

    But when you know what you're doing, it can be

    useful.

     >i'm not sure how to phrase my question.  i suppose it

    >boils down to whether this is good behavior for a

    >primitive, returning just part of a result.  (or

    >perhaps not, since x / y loses information too.)

     It's neither good nor bad; it's just odd.

    There are a lot of operators in Joy that I class as

    odd; in essence, anything with a number in its name

    is not optimal. That doesn't mean, though, that

    making an identical function which takes the number

    as a parameter would fix the problem.

     >the programmer who uses unary (and similar

    >combinators) had better know how many results its

    >program argument will return: if just one, he'll

    >get the whole result; if more, he'll get

    >just the last part.

     No doubt.

    -Billy

## sa@dfa.com — 2003-05-21 15:37:10

    it's worse (if that's the right word for it) than i thought -

         [1 2 3][dup]map

      [1 2 3]

    which means, i suppose, that you need to design [p] knowing

    that it will be mapped:

         [1 2 3][dup [] cons cons] map

      [[1 1][2 2][3 3]]

    i'm not suggesting that this is wrong, just noting that my

    expectations were confounded.

    --

    From: "stevan apter" <sa@...>

     >i have a question about the unary combinator.

     Yes -- I don't blame you. It's ugly and non-optimal.

    But when you know what you're doing, it can be

    useful.

     >i'm not sure how to phrase my question.  i suppose it

    >boils down to whether this is good behavior for a

    >primitive, returning just part of a result.  (or

    >perhaps not, since x / y loses information too.)

     It's neither good nor bad; it's just odd.

    There are a lot of operators in Joy that I class as

    odd; in essence, anything with a number in its name

    is not optimal. That doesn't mean, though, that

    making an identical function which takes the number

    as a parameter would fix the problem.

     >the programmer who uses unary (and similar

    >combinators) had better know how many results its

    >program argument will return: if just one, he'll

    >get the whole result; if more, he'll get

    >just the last part.

     No doubt.

    -Billy

    To unsubscribe from this group, send an email to:

    concatenative-unsubscribe@egroups.com

    Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/

## wtanksleyjr@cox.net — 2003-05-21 16:20:49

    From: sa@...

     >which means, i suppose, that you need to design

    >[p] knowing that it will be mapped:

    >      [1 2 3][dup [] cons cons] map

    >   [[1 1][2 2][3 3]]

    >i'm not suggesting that this is wrong, just noting that my

    >expectations were confounded.

     What were your expectations?

    I'm guessing that you would expect

      [1 2 3] [dup] map

    to return

      [[1 1][2 2][3 3]].

    Correct? This is reasonable and quite workable, IMO;

    it's what I would expect as well. It's just a bit hard

    to work with using Joy as it stands, but it's easy for

    cK to handle.

    -Billy

## Nick Forde — 2003-05-21 16:39:42

    wtanksleyjr@... writes:

      > From: sa@...

     > >which means, i suppose, that you need to design

     > >[p] knowing that it will be mapped:

     > >      [1 2 3][dup [] cons cons] map

     > >   [[1 1][2 2][3 3]]

     >

     > >i'm not suggesting that this is wrong, just noting that my

     > >expectations were confounded.

     >

     > What were your expectations?

     >

     > I'm guessing that you would expect

     >   [1 2 3] [dup] map

     > to return

     >   [[1 1][2 2][3 3]].

     >

     > Correct? This is reasonable and quite workable, IMO;

     > it's what I would expect as well. It's just a bit hard

     > to work with using Joy as it stands, but it's easy for

     > cK to handle.

     More generally I guess all combinators could push their temporary

    stacks as lists if their length was greater than intended

    (expected). However, this would place the responsibility of cleaning

    temporary stacks onto the user... possibly not a bad thing.

    Nick.

## wtanksleyjr@cox.net — 2003-05-21 17:55:55

    From: Nick Forde <nickf@...>

     >More generally I guess all combinators could push

    >their temporary stacks as lists if their length

    >was greater than intended (expected). However,

    >this would place the responsibility of cleaning

    >temporary stacks onto the user... possibly not a

    >bad thing.

     Possibly. However, here's another possibility:

      4 5 6 [1 2 3] [+] map

    What happens?

    Are the cases parallel?

     > Nick.

     -Billy

## sa@dfa.com — 2003-05-21 17:56:42

    no no - i'm not suggesting any change at all in the behavior of joy

    or in the primitives as they stand.  i'm simply noting (in a public

    way) the "dangerous curves" of the language as i'm learning to use

    it.

    for example, i tend to think of 'map' as taking a unary function and

    applying it to each item of a list.  but that's misleading, as you can

    see from

         2 [3 4 5] [+] map

       2 [5 6 7]

    i hope you would agree that it's less than desireable to present the

    definition of a language (especially a functional one) in operational

    terms, e.g. "map takes a program p, a list l, and the remainder of the

    stack s, then loops over elements l[i], computing the result-stack t

    of s,l[i],p, takes the last element of t and appends it to the output

    list o,..." etc.  but how else to nail down the precise behavior?  not,

    i think, with standard stack notation (although perhaps i'm not fully

    aware of the conventions people have evolved to deal with this kind of

    thing.)

    i'm getting the feeling that the problem (if it is one) is the result

    of having these two degrees of freedom:  an operation can (will) eat

    as much of the stack as it needs to do its thing, and an operation

    can return more than one result.  when you throw in the ability to

    create arbitrarily complex constructions through concatenation, you

    can't just look at a program in isolation and immediately understand

    its behavior on arbitrary stacks, or under the control of arbitrary

    combinators.  but then, i guess that's just the flip side of the power

    of joy.

    so, in answer to your question billy, no, i don't think it is easy

    to tame this kind of thing.  dup always returns 2 things, + always

    eats two things, and map (for example) can't know these sorts of

    facts about the program it's applying.

    --

                        Nick Forde

                        <nickf@cadenc        To:     concatenative@yahoogroups.com

                        e.com>               cc:

                                             Subject:     Re: Re: [stack] Fw: the unary combinator

                        05/21/03

                        12:39 PM

                        Please

                        respond to

                        concatenative

    wtanksleyjr@... writes:

      > From: sa@...

     > >which means, i suppose, that you need to design

     > >[p] knowing that it will be mapped:

     > >      [1 2 3][dup [] cons cons] map

     > >   [[1 1][2 2][3 3]]

     >

     > >i'm not suggesting that this is wrong, just noting that my

     > >expectations were confounded.

     >

     > What were your expectations?

     >

     > I'm guessing that you would expect

     >   [1 2 3] [dup] map

     > to return

     >   [[1 1][2 2][3 3]].

     >

     > Correct? This is reasonable and quite workable, IMO;

     > it's what I would expect as well. It's just a bit hard

     > to work with using Joy as it stands, but it's easy for

     > cK to handle.

     More generally I guess all combinators could push their temporary

    stacks as lists if their length was greater than intended

    (expected). However, this would place the responsibility of cleaning

    temporary stacks onto the user... possibly not a bad thing.

    Nick.

    To unsubscribe from this group, send an email to:

    concatenative-unsubscribe@egroups.com

    Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/

## John Cowan — 2003-05-21 19:54:47

    Nick Forde scripsit:

     > More generally I guess all combinators could push their temporary

    > stacks as lists if their length was greater than intended

    > (expected). However, this would place the responsibility of cleaning

    > temporary stacks onto the user... possibly not a bad thing.

     In fact both the C and the Scheme implementations use a list representation

    for the stack.

    --

    And through this revolting graveyard of the universe the muffled, maddening

    beating of drums, and thin, monotonous whine of blasphemous flutes from

    inconceivable, unlighted chambers beyond Time; the detestable pounding

    and piping whereunto dance slowly, awkwardly, and absurdly the gigantic

    tenebrous ultimate gods --  the blind, voiceless, mindless gargoyles whose soul

    is Nyarlathotep. (Lovecraft) John Cowan|jcowan@...|ccil.org/~cowan

## John Cowan — 2003-05-21 20:30:13

    wtanksleyjr@... scripsit:

     > Possibly. However, here's another possibility:

    >

    >   4 5 6 [1 2 3] [+] map

    >

    > What happens?

     The reference implementation says the stack is:

    	[7 8 9] 6 5 4

    (that's from top to bottom, of course).  IOW only the 6 is ever used,

    because the stack is reset between each invocation of +.

    Joy.ss 0.4 has a bug that makes it output the top value incorrectly,

    but the structure of the stack is the same.

    --

    You are a child of the universe no less         John Cowan

    than the trees and all other acyclic            http://www.reutershealth.com

    graphs; you have a right to be here.            http://www.ccil.org/~cowan

      --DeXiderata by Sean McGrath                  jcowan@...

## phimvt@lurac.latrobe.edu.au — 2003-05-23 04:16:01

    On Wed, 21 May 2003 sa@... wrote:

     > for example, i tend to think of 'map' as taking a unary function and

    > applying it to each item of a list.

     Indeed, that is the "normal"/"standard"/"conventional" view and

    definition. Most (all?) other languages would define it that way.

    In most (all?) other languages a function only returns one value,

    that is what "function" means. I think you will agree in these

    cases Joy's map does exactly the right thing. This includes case

    where the quotation is any of the following:

      [succ]  [dup *]  [[] cons]  [dup [] cons cons]

    But what about binary operators used with map? One attitude would

    be to forbid it outright. That is what most (all?) other languages

    would _have_ to do because the list elements are singletons. In Joy

    there is another possibility: take the extra element from the stack.

    That is a non-trivial extension of the usual conception of map, and

    it may or may not be useful. But it also needs some decision as to

    what to do with the extra element: will it be available for the

    other elements in the list?

    I concluded that binary, ternary .. operators should be allowed

    because such an extension is indeed useful. I also concluded that

    the "extra" element should not be consumed but remain available.

    I present two arguments:

    1. Efficiency. Suppose we have a very very long list of numbers,

    "VVLLN". We want to multiply each by, say, 42. Multiplication is binary,

    multiplication by a constant is unary, so that is fine. Suppose VVLLN is

    already on the stack, to map it we write

          [42 *]  map

    That is correct. But note that 42 gets pushed very very many times

    because VVLLN is very very long. We could save those pushes by

    allowing map to take binary operators and writing instead one of the

    following:

         [42]  dip  [*]  map  popd

          42  swap  [*]  map  popd

    So we have a small constant overhead because of the dip or swap,

    and because of the popd, but it is worth it for long lists.

    (The final popd is needed to remove the 42 when all is done.)

    2. Further flexibility: Suppose you have a number N on the stack,

    and you want a list of a) its sucessor, b) its square, c) its

    factorial, d) twice its fib, ...  A very natural solution in Joy is

       [ [succ]  [dup *]  [fact]  [fib 2 *]  ... ]   [i]  map  popd

    Now, the i combinator is indeed unary, as the "standard" conception

    of map would require, but here it is made to work on unary

    functions which all require the same further value N. But that

    value is precisely the number on the stack, below the list of

    quotations. So map should again not consume items below the list.

    One might argue that the map combinator used with binary

    operators should do the final popd  automatically, and when

    used with a ternary operator it should do two popd's and so on.

    Moreover, when map is used with a combinator quotation such

    as [i] it should then make its automatic actions of popd's

    depend on the arity of the quoted functions [succ] [fact] ..

    But these arities need not be the same - so should the

    number of automatic popd's be the maximum or the minimum of

    the arities? And is the reader of the program supposed to

    go through the possibly long list of quotations to find out

    how many popd's Joy will execute automatically? No, better

    none at all, and let the pogrammer say it explicitly.

    [..]

     > i hope you would agree that it's less than desireable to present the

    > definition of a language (especially a functional one) in operational

    > terms, e.g. "map takes a program p, a list l, and the remainder of the

    > stack s, then loops over elements l[i], computing the result-stack t

    > of s,l[i],p, takes the last element of t and appends it to the output

    > list o,..." etc.  but how else to nail down the precise behavior?  not,

    > i think, with standard stack notation (although perhaps i'm not fully

    > aware of the conventions people have evolved to deal with this kind of

    > thing.)

     Operational explanations often work well psychologically: people

    understand them. A non-operational definition in denotational

    semantics is much harder psychologically, but especially for

    functional languages not too complex. An implementation of a functional

    language such as Joy in a good standard functions language

    is just about identical with the denotational definition.

     > i'm getting the feeling that the problem (if it is one) is the result

    > of having these two degrees of freedom:  an operation can (will) eat

    > as much of the stack as it needs to do its thing, and an operation

    > can return more than one result.

     I have not addressed the multiple result "problem", I know. I'm not

    at all sure it is a problem. But here is a related example:

       [1 2 3]   [[dup] [dup dup dup] [] [+] [+ *]]   [infra]   map

    If your expectation of what this should do coincides with what

    Joy in fact does, then Joy's map combinator does seem right,

    doesn't it ?

    [..]

    Thank you all for the discusssion.

      - Manfred

## stevan apter — 2003-05-23 14:36:42

    ----- Original Message -----

    From: <phimvt@...>

    >

    > I concluded that binary, ternary .. operators should be allowed

    > because such an extension is indeed useful. I also concluded that

    > the "extra" element should not be consumed but remain available.

    > I present two arguments:

    >

    > 1. Efficiency. Suppose we have a very very long list of numbers,

    > "VVLLN". We want to multiply each by, say, 42. Multiplication is binary,

    > multiplication by a constant is unary, so that is fine. Suppose VVLLN is

    > already on the stack, to map it we write

    >       [42 *]  map

    > That is correct. But note that 42 gets pushed very very many times

    > because VVLLN is very very long. We could save those pushes by

    > allowing map to take binary operators and writing instead one of the

    > following:

    >      [42]  dip  [*]  map  popd

    >       42  swap  [*]  map  popd

    > So we have a small constant overhead because of the dip or swap,

    > and because of the popd, but it is worth it for long lists.

    > (The final popd is needed to remove the 42 when all is done.)

    this is persuasive.  i will note that extending primitives like *

    to general lists would make this a less compelling argument, since

            vvlln 42 *

    would leave a list result on the stack.  i think map should

    continue to work as it does, and so 42 vvlln [*] map would remain

    available as an efficient alternative to vvlln [42 *] map, but

    where the program to be mapped has the property of atomic extension,

    the programmer can avoid the entire issue by applying it directly

    to the list operand.  e.g. [42 * 3 + neg] is atomic, so one can

    simply write vvlln [42 * 3 + neg] i.

    in general, i think your arguments for the current behavior are

    compelling.  but then, i wasn't complaining - just noting that

    'map', 'unary', and similar combinators were "deeper" than i had

    assumed.  in other words, it's a documentation problem.  once

    the behavior is understood, its rightness is apparent.