On 1/24/13, Beni Cherniavsky-Paskin <c...@users.sf.net> wrote:
> Bah!  Mobile gmail ate my indentation AND many newlines!  Sorry.
> Hope this copy is sent correctly.
> Converted examples to fixed-width font to make up for the noise.
>
> On Jan 2, 2013 7:15 PM, "David A. Wheeler" <dwhee...@dwheeler.com> wrote:
>>
>> Ben Booth:
>> > There was an interesting language that made the rounds on reddit
> /r/programming a few days ago:
>> >
>> > http://chrisdone.com/z/
>> >
>> I agree with 1337hephaestus_sc2: "The main idea seems clever, but also
> too clever."  A few issues:
>> 1. When you have multi-parameter functions, this syntax quickly forces
> you to grow vertically.  This is exactly the opposite of the actual real
> estate available.  Screens are wide and short, and even if you use
> traditional paper sizes it's wider than tall (typically 80 characters
> across, ~66 lines down).
>

Perhaps it can be mitigated by the simple expedient of disabling
indentation processing in parentheses.

> While I wouldn't use Z as is, it appeals to me for 2 reasons.
>
> The first is excellent handling of nested lists in let:
>
> let ! \\ ! x cos a
>     !    ! y sin a
>     ! do-something ...
>
> By \\ here I mean GROUP (not SPLIT).  Z doesn't have GROUP but it trivially
> could [1].
> I also introduced ! because I'm writing this in a variable-width font,
> which is a big problem; more on this below.
>
> This is excellent in the sense that it introduces a second level without
> wasting any vertical real estate!
> (This is opposite of David's observation that Z makes you grow vertically.
> Both can be true - the problem is that Z is *fully normalized*, so
> sometimes it's too wide and sometimes too tall, and you don't have any
> leeway to trade one for the other.)
>
> The second is symmetric treatment of args on the same line and followup
> lines.
> It's a point which has long bothered me about sweet-exprs, and I don't have
> a concrete solution I'm happy with; but let me explain what I mean and
> introduce a strawman notation as a starting point.
> [BTW, I'm well aware that I'm late to the party, to express now doubts
> about basics of t-expr while you guys are busy polishing the BNF.  But I
> didn't have time previously to write this down well, so here goes FWIW...]
>
> The purest uses of indentation can be visually parsed in one of 2 ways,
> which I'll call "header" vs "bullet".
> When short on horizontal space, people resort to compromise "undent"
> styles.
>
> == header ==
>
> Body lines lie *below* the header line(s), shifted a fixed width to the
> right:
>
> defun func (arg1 arg2)
> ; ----------------------------
>     body ...
>     ...
>
> This style is used in most languages for control structures.
> The body is usually considered a series of lines - even if newlines are not
> significant, there are strong conventions to prefer one "instruction" per
> line.
>
> Python's significant indentation is all in header style.
> Sweet-expressions handle this style well, allowing several args on header
> line and one per
>
> == bullet ==
>
> All arguments lie to the *right* of the function, lined up one under the
> other:
>
>   !
> * ! (cos x)
>   ! (sin x)
>   ! 2
>   !
>
> This is used in typography for bullet lists (and rare constructs like
> "compact" definition lists), hence the name.
> In most programming languages, this is commonly used for function calls:
>
> func(nested1(nested, long, calls,
>              can, be, split),
>      arg2, arg3,
>      nested4(x, y,
>              z))
>
> Splitting args to one per line is usually considered optional, a matter of
> taste.
>
> Z *always* uses this style and requires 1 arg per line.
>
> I'm confused on whether the "offside rules" of Haskell and similar
> languages support this style.

Offside rule simply looks at the column of the first non-{ token after
keywords "do", "let", "of", and "where", and memorizes that column,
inserting the missing "{".  Tokens starting at that column will have a
";" inserted before it (basically, ending the previous expression),
and tokens starting at columns to the left of the off-sided column
will end the off-siding (inserting a "}" to end it).

So for example:

> do ! x
>    ! y
> bar

Gets translated to:

> do ! {x
>    ! ;y
> }bar

So basically, Haskell implicitly supports the "bullet" style, but can
admit the "header" style.

In practice, most Haskell code is in "header" style.

*Technically* the name of the rule "offside" refers to the fact that
inner elements should be to the *to the side of and below* their
parent keyword (i.e. think a strict combination of the "bullet" and
"header").  The requirement to be to the "side" was relaxed in Miranda
(and eventually Haskell), but the rule remained "offside".

By my memory the original offside rule (below and to the right,
strictly) is the first time that anyone formalized indentation-based
program organization.

In practice, Haskell can support either "header" or "bullet" because
only some keywords support indentation formatting(function
applications in Haskell *must* be grouped by parens or infixes, so you
can't use indent to denote boundaries of function arguments).

>
> Sweet-expressions admit this style, BUT treat the first line asymetrically
> from the followup lines.
>
> 1. First line can contain 0 or more args, all handled the same.
> 2. Followup lines are treated as 1 arg per line - mulitple args are wrapped
> in a list, unless you use \\.
> 3. You CAN'T have the nested1 call on first line, unless you fall back to
> (...) notation.  I'm not sure how bad this is, but it's at the heart of the
> let problem - and the loss of \\ and $ resulting from (...) use was a
> motivation to employ <* .. *> there.
>
> -- Practical issues --
>
> Unfortunately, there are several practical issues with making bullet style
> indentation meaningful and opening several levels of indentation on one
> line, like func(nested1( above.
>
> - It's harder to implement, requires parser to count columns while parsing
> actual code.
>     - If you support unicode, zero-width and full-width CJK chars are a
> problem.  I don't remember how reStructuredText specced this (it's relevant
> for table syntax), but it's a complication in any case.
>
> - It's even more ambiguous and sensitive to indentation-lossage in email
> etc. than "header" style of indent.
>
> - Even when spaces are preserved, merely viewing it with PROPORTIONAL FONTS
> - which most places on internet use nowdays - breaks it badly!
>     - Things don't line up, so the visual appeal is lost, even without nest
> calls on first line.
>     - Nested calls one first line become very hard to decipher.  It's
> acceptable in most languages because you have parens, but not if all you
> have is indentation.
>
> The last point is really a deal-breaker.
> Fortunately, we have ! which would allow this style to survive media like
> forums and email and still be decipherable.  But it gets tedious.
> [In an ideal world everybody would preserve tabs, and display them
> according to Elastic tabstops spec <
> http://nickgravgaard.com/elastictabstops/>.
> In an even better world, everybody would size leading spaces to match
> corresponding letters in the less-indented line above them.
> Nobody does either, so ! is our best weapon.]

Here's an even bigger deal-breaker: it makes parent structures harder
to modify, because if the length of the parent is changed (for example
inserting an argument you forgot in the first pass of writing the
code, or modifying an existing function to accept a new argument, or
adding some logging code, or whatever) then you have to re-indent all
the children.

This is the main reason why Miranda and Haskell relaxed the "to the
right" part of the original offside rule.

>
> == Compromise "undent" styles ==
>
> When things get too wide, the first thing people try is convert some
> "bullet" calls into "header" style:
>
> long_function_name(
>   another_long_func(
>     bar,
>   )
>   foo,
> )
>
> But this costs vertical space, so sometimes (especially when the outer
> structure has just one child), people do this:
>
> long_function_name(another_long_func(
>     bar))
>
> Now that's somewhat evil - how come bar is far to the left of (another_...
> while beeing deeper than it?!  Shouldn't indentation increase monotonically
> with depth?
> But some people are OK with it, and in some constructs it look quite
> natural, especially if there are no parens to highlight the "violation":
>
> variable = [
>   bar,
> ]
>
> which looks really innocent compared to the fully parenthesised:
>
> (setq variable (list
>    bar
> ))
>
> In sweet-expressions, this is supported by $:
>
> setq variable $ list
>   bar
>
> I'd say "define (f args) $ cond" is a good example for a construct that is
> acceptable with $ but too ugly with full parens.

Yes, and one of the best arguments for "$".

>
> So I'm calling it "undent(ation)", following F# which allows it in some
> situations, with frightfully specific rules:
> http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/spec.html#_Toc335819062
>
> == "list" style? ==
>
> In lisp the first element of most constructs is special, but occassionally
> lists do have flat structure - within quoted data, or the list of let
> clauses (what is it about let that makes it crop up everywhere?!)...
> In those cases, it's customary to align all elements:
>
> (f '(foo
>      bar
>      baz))
>
> But I wouldn't consider this a special style, it's just a degenerate case
> of "bullet" style.
> Flat lists are also sometimes seen in "header" style, especially in
> languages with "traditional" syntax:
>
> x = [
>         foo,
>         bar,
>     ]
>
> or undented as mentioned above.
>
> ==== Strawman: A-expressions ====
>
> Consider now a notation opposite to Z where things are *never* implicitly
> grouped into lists.
> I'm not proposing this is usable, only as food for thought.
>
> In A-expressions to open a list you must use ( - from where you're in
> S-expr mode - or the new operator |, which has "bullet" semantics -
> everything to the right on this and followup lines is part of the list, but
> division into lines doesn't matter.
> So a simple long function call looks like:
>
> | f arg1 arg2
>   long_arg3
>   arg4 arg5
>
> Hmm, that's ugly, I want here to align under arg1!
> - I can just allow superflous indentation.
> - I could also follow neoteric f(args) syntax and add a postfix form of |:
>
> f| arg1 arg2
>    long_arg3
>    arg4 arg5
>
> In any case, the point is that all 5 arguments are handled uniformly - no
> special casing of first line, no special treatment of long_arg3 because
> it's alone on its line, no need for \\ between arg4 \\ arg5...
>
> And of course I can use | many times on a line:
>
> | let | | x | cos a
>       ! | y | sin a
>       ! body-of-let ...

Err I think this would be better off as:

> | let | | x | cos a
> !     ! | y | sin a
> !     | body-expr-of-let
> !     | body-expr-of-let-2

what you think?

>
> It's tempting to unify | and ! but the difference is important -  the ! do
> not open a new list.
>
> So far so good, but what about "header" style?
>
> | defun f | a b c
>   ; note BTW how the second | above consumes
>   ; only a b c, not the whole body as $ a b c would do.
>   | cond
>     | condition1
>       | let
>            | | x | cos a
>         | y | sin a
>        body-of-let...
>     | condition2
>       body2...
>
> Again, I'd like defun, cond, let to stand out better.
> This would look mildly better if I allow deeper indentation than required:
>
> ; in scheme: define| f| a b c - similar to
> ; defun f (a b c) vs. define f(a b c)
>
> | defun f | a b c
>     | cond
>        | condition1
>           | let
>              ...
>
> But this is merely lisp with s/(/| / and s/)//!
> And all the leading | are still distracting.

And it's not half-bad IMO, actually.

I take it that postfix f| means something like n-expression f(?

So:

define| f| a b c
  body| x

==>

define( f( a b c ) ; closing parens inserted
  body( x ) ; closing parens inserted
) ; closing parens inserted

==>

(define (f a b c)
  (body x)
)

--

Practical problems:

1.  In Common Lisp, "|" is used to denote symbols with "special
syntax", i.e. (symbol "foo bar") => |foo bar|.

2.  Supporting "| defun foo | a b" requires knowing how wide
characters are (CJK width problem), especially if you can do something
like:

| defun foo | &key
|          ! | var1 | default-value 'var1
...



>
> What about using postfix |?  I don't want the full "bullet" indent of:
>
> defun| f | a b c
>        cond|
>              condition1|
>                          ...
>
> It's only acceptable if I allow *shallower* indents for postfix|, as long
> as its deeper than the word preceding |:
>
> defun| f | a b c
>   cond|
>     condition1|
>       let|
>         | x| cos| a
>           y| sin| a
>         body-of-let ...
>     condition2|
>       body2...
>
> Note that this notation provides no "undent" facility - one can compress
> things such as "defun| f (a b c) cond|" or "cond| condition1|" but those
> force deeper bullet-style indents on the rest.
>
> OK, that's enough for now.
> I'm starting to have concrete proposals for sweet-expressions, but that's
> for a future post.
>
> --
>
> [1] Aside: I don't understand what's the point of Z fully currying
> functions.  It'd make perfect sense if "func x y" would group as "((func x)
> y)" but it doesn't; moreover without GROUP the language has no way to at
> all to represent a list in first position, so no way to call a curried
> function!
> Perhaps any multi-argument (i.e. multi-line) call is syntactic sugar for
> nested lists, in which case Z does have a way to represent let ((x (cos a))
> (y (sin a))):
>
> let ! x ! cos a
>     !   ! y sin a
>     ! do-something ...
>
> but that's perverted and asymmetric.
> --
> Beni Cherniavsky-Paskin
>

Sincerely,
AmkG

------------------------------------------------------------------------------
Master Visual Studio, SharePoint, SQL, ASP.NET, C# 2012, HTML5, CSS,
MVC, Windows 8 Apps, JavaScript and much more. Keep your skills current
with LearnDevNow - 3,200 step-by-step video tutorials by Microsoft
MVPs and experts. ON SALE this month only -- learn more at:
http://p.sf.net/sfu/learnnow-d2d
_______________________________________________
Readable-discuss mailing list
Readable-discuss@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/readable-discuss

Reply via email to