[Haskell-cafe] Desugaring of infix operators is (always?) the wrong way round

Brian Hulley brianh at metamilk.com
Thu Sep 27 10:37:26 EDT 2007


Sam Hughes wrote:
> Brian Hulley wrote:
>
>> ... For example, with the prefix definition of a function with 
>> multiple clauses, the function name at the start of each clause is 
>> already lined up since it must appear at the margin of the current 
>> layout block ...
>
> Or you could have everything be backwards, and use an editor that 
> right aligns things.
>
>
>            (a -> b) -> [a] -> [b] ::          map
>                                 [] = []     _ map
>                     x f : xs f map = (x:xs) f map
>

There is still a reversal here between the order of arguments in the 
type signature and the order in the clauses.

Henning Thielemann wrote:
>
>
> Curried functions like
>
> f :: a -> b -> c
>
> suggest a swapped order of arguments for (->), since 'f' must be 
> called this way
>
> b a f
>
> Maybe it should be
>
> f :: c <- b <- a
>
>

Which would fix this reversal. However there are 2 other kinds of 
reversal with postfix notation:
1) Declarations start with a keyword followed by some content whereas 
function definitions start with the args followed by the function name
2) Special constructs like "case" or "let" have to start with the 
keyword (so the parser knows what kind of thing it is supposed to parse 
next and so that the editor knows how to highlight what the user is 
typing before the user has finished typing the whole construct), again 
making a reversal between built-in constructs and constructs you can 
define using higher order functions (eg consider "if" as a user-defined 
construct in a postfix language)

I've come to the conclusion that whereas postfix notation is extremely 
neat for simple stack-based languages like Forth and PostScript it would 
not play well with languages which have a structured syntax since 
structured syntax + left to right reading order implies each syntactic 
element must start with a "head" followed by content appropriate to that 
element, or else recursive descent parsing and/or as-you-type 
grammatical highlighting would be impossible, and therefore in terms of 
function application, the "head" must of course be the function itself 
hence Prefix is the only solution.

Jonathan's comparison to natural languages made me think of it this way:

    x `plus` y   ===  [Subject] [Verb] [Object]

    x .plus(y) === [Subject] [Verb Object]

    plus y x === [Verb Object] [Subject]

    plus x y === [Verb Subject] [Object]

which illustrates why infix notation feels natural (corresponds to SVO 
in English etc), why OOP notation feels natural, why prefix notation is 
natural for a functional language (since we are interested primarily in 
the transformation not the things being transformed hence we put [VO] 
first), and why the desuraging of infix in Haskell/ML is quite simply 
just wrong, since the object is now separated from the verb.

ok wrote:
> Binary operators have two arguments.  That's why sections are needed.

What's wrong with just using (flip)?

>
>
> I am a bear of very little brain, and I would find it VERY confusing
> if the order of arguments in an operator application did not match
> the order of arguments in a function call.  I can live with
>     x @ y = (op @)(x, y)        (* SML *)
>     x @ y = (@) x y            -- Haskell
> but making the orders different would guarantee that I would *always*
> be in a muddle about which argument was which.  Living with
> inconvenient argument orders is vastly easier than with inconsistent 
> ones.

If you inwardly parse x @ y as [x] [@ y] then the prefix notation is 
naturally formed just by putting the "specialized verb" before the 
subject instead of after it ie [@ y] [x].
Therefore I think this desugaring, though different from the usual one, 
would seem natural as soon as the reason for it was understood (of 
course, only if you agree with the reason ;-) ), and the great advantage 
of it is that we could write library functions without having to decide 
in advance whether or not they should be used with infix sugar.

(Regarding Henning's point about ((->) a) being needed for Reader monads 
we could define "type Fun a b = a -> b" and then use (Fun a))

In any case, thanks to all who replied. I've found the discussion very 
illuminating and it's certainly helped a lot to clarify the issues for me,

Brian.


More information about the Haskell-Cafe mailing list