# Joy and code readability

## fabio_cevasco — 2009-03-31 11:37:59

    Hello everyone,

    This is my first post here, so I thought I'd introduce myself a little

    bit: I'm a Ruby programmer (I program for fun, not for profit), and I

    recently became interested of the Joy language, especially of its

    minimalism.

    I also developed a small Ruby DSL <http://www.h3rald.com/concatenative>

    which implements some of Joy's combinators, just to see what's it like

    to work with a concatenative language

    <http://www.h3rald.com/articles/concatenative-programming-in-ruby>  .

    After playing around a little bit, I just wanted to ask you guys how you

    cope with the (generally speaking) lack of readability of concatenative

    (but I'm referring mainly to Joy here) code.

    Although I really like everything concatenative programming offers, I'm

    somehow overwhelmened by it, in the sense that I find it more difficult

    to read (and mentally execute) concatenative code without local

    variables.

    How do you cope with that?

    Example (the famous quicksort in Joy):

    [small]

    []

    [uncons [>] split]

    [[swap] dip cons concat]

    binrec

    To improve readability, the only thing I can think of is labeling each

    quoted program, using definitions, so:

    DEFINE

    remove_pivot_and_split_list == uncons [>] split

    insert_pivot_and_merge_lists == [swap] dip cons concat

    So we have:

    [small]

    []

    [remove_pivot_and_split_list]

    [insert_pivot_and_merge_lists]

    binrec

    Definitely more descriptive and closer to a black box: each name

    identifies, roughly, how many items the program pops from the stack at

    the start and how many items pushes back at the end.

    Is there anything better than this? How do you keep track of what's

    going on the stack in a language with no variable assignments?

    Thanks,

    Fabio

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

## John Cowan — 2009-03-31 12:27:53

    fabio_cevasco scripsit:

     > Although I really like everything concatenative programming offers, I'm

    > somehow overwhelmened by it, in the sense that I find it more difficult

    > to read (and mentally execute) concatenative code without local

    > variables.

     You've identified two of the three techniques: write very short words,

    and put a comment on each word saying what it does.  The comment doesn't

    actually have to be the name, however.  The third technique is to use

    stack comments, in the style of the Joy manual, showing what's expected

    on the stack and what winds up on the stack, with names chosen where

    possible to show the expected types.  In some concatenative languages,

    the compiler actually verifies these comments, but not in Joy.

    --

    John Cowan    http://ccil.org/~cowan    cowan@...

    Economists were put on this planet to make astrologers look good.

            --Leo McGarry

## William Tanksley — 2009-03-31 18:40:50

    fabio_cevasco wrote:

     > After playing around a little bit, I just wanted to ask you guys how you

    > cope with the (generally speaking) lack of readability of concatenative

    > (but I'm referring mainly to Joy here) code.

     Reading -- and writing -- concatenative code is _different_. You have to

    learn some new thought patterns, and it'll take some time.

     > Although I really like everything concatenative programming offers, I'm

    > somehow overwhelmened by it, in the sense that I find it more difficult

    > to read (and mentally execute) concatenative code without local

    > variables.

    > How do you cope with that?

     You're on the right path; good naming is important. Forth programmers

    are urged to keep a thesaurus handy; terse, appropriate names for words

    are very useful to improve readability (long-winded names are better

    than a lack of factoring, but there are advantages to terseness as

    well). Being very willing to factor common sequences out into a word of

    their own also tends to be very helpful, so long as the result doesn't

    hide your intended algorithm.

    One of the biggest things, I think, is realizing that first,

    concatenative languages express algorithms, NOT expressions. Most

    languages are decent at expressing both sequences of actions (AKA

    processes, procedures, or algorithms) and expressions (AKA math

    formulas). Concatenative languages are specialized to express sequences

    of actions.

     > To improve readability, the only thing I can think of is labeling each

    > quoted program, using definitions, so:

    > DEFINE

    > remove_pivot_and_split_list == uncons [>] split

    > insert_pivot_and_merge_lists == [swap] dip cons concat

     Great! Get bolder and make two definitions for each of those:

    "remove_pivot", "split_list", "insert_pivot", and "merge_lists".

     > Is there anything better than this? How do you keep track of what's

    > going on the stack in a language with no variable assignments?

     Well, we do have a few rules. Nothing hard and fast, but most of them

    should make sense. Don't assume that one of the rules is more important

    than another one -- apply them to your situation.

    The first rule is to keep it simple. Try to write words that use three

    or fewer parameters, that take the parameters in a natural order (so you

    don't have to shuffle), that are short. A single line is ideal; three

    lines is excellent; eight lines is long.

    The second rule is to keep it algorithmic. An algorithm is "a finite set

    of unambiguous instructions performed in sequence to achieve a

    goal" (dictionary.com). Breaking this down, we see some sub-rules:

       * Keep your goal in mind and make it evident (probably in comments).

       * Keep your steps in mind and make them evident (probably in word

    names).

       * Keep your steps unambiguous (in choice of word names).

       * Show the sequence of steps clearly (probably by making one specific

    word contain ONLY the words that each implement each step in your

    algorithm).

    The third rule is to try to keep your stack effects clear; the

    programmer should be able to know what your code does without having to

    execute it in his head. It's possible to write code that consumes a

    variable number of items off the stack, but it's usually a bad idea.

    It's possible to write code that depends on knowing how deep the stack

    is... But likewise, usually a bad idea. It's easy to write code that

    does a lot of stack juggling, but modern concatenative languages provide

    combinators like cleave and spread that allow you to express your

    intentions in terms of how the data flows rather than how you want the

    stack to be juggled right now.

    The rest of the rules you'll find in any guide to good design -- you

    should seek highly coherent design, low coupling, clear purpose... These

    are just the ones that apply especially strongly to concatenative

    languages. (Yes, most languages work better with good algorithm

    expression, but it's an especially strong requirement with concatenative

    ones; and I think concatenative languages have some strength on the

    topic, since they are read in strict sequence left-to-right.)

     > Fabio

     -Wm

## fabio_cevasco — 2009-04-01 09:25:20

    Could you elaborate a bit more on stack comments? You mean writing (in Joy) something like:

    (* A -> A A *)

    To say that the function (dup) consumes one item and puts two on the stack?

    Which concatenative languages verify these comments? Do they expect comments after the code they refer to, e.g. something similar to:

    [small]

    []

    [uncons [>] split] (* [A B C] -> B [A] [C] *)

    [[swap] dip cons concat] (* A [B] [C] -> [B A C] *)

    binrec

    Thanks a lot!

## John Nowak — 2009-04-01 11:58:55

    On Apr 1, 2009, at 5:25 AM, fabio_cevasco wrote:

     > Could you elaborate a bit more on stack comments? You mean writing

    > (in Joy) something like:

    >

    > (* A -> A A *)

     Yep.

     > Which concatenative languages verify these comments?

     Factor does. Cat may allow them; it's been awhile since I've looked at

    it. Cat will, however, automatically infer the effect in question for

    you.

     > Do they expect comments after the code they refer to, e.g. something

    > similar to:

     I believe it is only allowed for definitions as such:

        : over ( a b -- a b a ) [ dup ] dip swap ;

    - John