Joy and code readability

View: New views
5 Messages — Rating Filter:   Alert me  

Joy and code readability

by fabio_cevasco :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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]


Re: Joy and code readability

by John Cowan :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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

Re: Joy and code readability

by William Tanksley, Jr :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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



Re: Joy and code readability

by fabio_cevasco-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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!


Re: Re: Joy and code readability

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


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