[stack] a minor change to cons

John Nowak <[email protected]> Mon, 7 Jun 2010 21:43:46 -0400
Newsgroups gmane.comp.lang.concatenative
Message-ID <[email protected]>
I've commented before that the map fusion rule for Haskell:

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

... has no nice analog in Joy. I now think this may not be because Joy is stack-based, but rather because it's not stack-based enough! 

Because I need to for this to work, I separate quotations ("[]"), lists ("{}"), and stacks ("()"; these are just first class stacks similar to lists but heterogeneous) into separate data types. I believe this is a good idea regardless; see previous posts.

Currently, 'cons' and 'uncons' have the following semantics (where "'a" denotes a single element of any type and "X" denotes zero or more elements):

      'a {X} cons  ==  {'a X}
    {'a X} uncons  == 'a {X}

I'm proposing a small change; the idea is that 'cons' can only add a stack to a list; it is an error to attempt to add anything else (e.g. "1 {} cons"):

     (Y) {X} cons  ==  {(Y) X}
   {(Y) X} uncons  ==  (Y) {X}

Because all lists now are composed of zero or more stacks, we can write a nice version of 'map' that uses the 'infra' combinator:

   (X) [F] infra  ==  (X F)
          infrad  ==  [infra] dip

   map = [swap null?]
         [drop]
         [[uncons] dip  [infrad] keep  map cons]
         ifte

This version of 'map' enjoys the following property:

   [G] map [F] map  ==  [G F] map

It's also strictly more useful. For example, we can now map a function across a list that returns more than one value (such as 'dup' to give a trivial example).

This "stack on the stack" approach can be used to clean up the semantics for the cleave and spread combinators as well. For example, if we redefine Factor's 'bi', 'bi@', and 'bi*' as such:

      X  [G] [F] bi   ==  X G (X F)
   Y (X) [G] [F] bi*  ==  Y G (X F)
      Y  (X) [F] bi@  ==  Y F (X F)

... we then enjoy the following laws:

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

This approach also eliminates the need for '2bi', '3bi', et cetera; the programmer can simply elect to push more values into the stack on top of the stack.

Final point: All of the above functions are easily typeable. First-class stacks cause no issues.

Thoughts?

- jn