[erlang-questions] Erlang and syntax.

275 views
Skip to first unread message

Maxim Velesyuk

unread,
Feb 22, 2014, 4:14:08 AM2/22/14
to erlang-q...@erlang.org
For a long time I wonder why erlang syntax is not based on s-expressions? Did Creators debate on it, if yes, why had prolog-like syntax won?

Often when I feel I need to change ast I remember how clumsy and uncomfortable parse transform is, so I overcome myself and make workarounds.

But macros are still useful, programmers use parse transform when they have no other choice, and projects like merl appear.

List and zip comprehensions could be implemented as macros, ets and mnesia query language is actually prefix-notation language with code quoting, even pattern matching and so long expected maps could be just a libraries.

Take a look at c++ and java, how they suffer from inventing new syntax. Hopefully Erlang will not turn in such syntax-monster. It has syntax for many things, but it still not as flexible as it could be.

This forwards me back to my first question, why?

Anthony Ramine

unread,
Feb 22, 2014, 6:43:42 AM2/22/14
to Maxim Velesyuk, erlang-questions Questions
Erlang is made to be boring. Boring means that it needs to be brain dead easy to comprehend. Brain dead easy means no macros. Given the premise of the absence of macros, why should the language use S-expressions and be homoiconic?

Why do you need to change the AST? That sounds like a constructed need from a bored developer looking for a problem. If you want better parse transforms, help me finish brackets for 19.0.

https://github.com/nox/otp/commit/brackets

I don’t see how could you compare C++ and Java to Erlang, certainly not on the front of syntax. Erlang is a tiny language.

So to get back to the first question: because macros do not promote explicitness.

Regards,

--
Anthony Ramine
> _______________________________________________
> erlang-questions mailing list
> erlang-q...@erlang.org
> http://erlang.org/mailman/listinfo/erlang-questions

_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions

Vlad Dumitrescu

unread,
Feb 22, 2014, 6:58:40 AM2/22/14
to Anthony Ramine, erlang-questions


On 22 Feb 2014 12:43, "Anthony Ramine" <n.o...@gmail.com> wrote:
>
> Erlang is made to be boring. Boring means that it needs to be brain dead easy to comprehend. Brain dead easy means no macros.

Sorry, but -define () macros can be just as confusing as lispy ones... I would gladly trade them away.

/Vlad

Mahesh Paolini-Subramanya

unread,
Feb 22, 2014, 7:02:17 AM2/22/14
to Vlad Dumitrescu, erlang-questions
An edge case does not necessarily negate an entire philosophy.
e.g. "I dont' steal from others" ++ "Yup, that book of Post-Its came from my Office"

Cheers
--
That tall bald Indian guy..
Google+  | Blog   | Twitter  | LinkedIn

Anthony Ramine

unread,
Feb 22, 2014, 7:14:36 AM2/22/14
to Vlad Dumitrescu, erlang-questions
Sorry but no. I know I am reading a macro call with epp macros. I don’t know that in a language using s-expressions. And people use preprocessor macros rarely.

--
Anthony Ramine

Le 22 févr. 2014 à 12:58, Vlad Dumitrescu <vlad...@gmail.com> a écrit :

> Sorry, but -define () macros can be just as confusing as lispy ones... I would gladly trade them away.
>
>

Vlad Dumitrescu

unread,
Feb 22, 2014, 11:31:59 AM2/22/14
to Anthony Ramine, erlang-questions


On 22 Feb 2014 13:14, "Anthony Ramine" <n.o...@gmail.com> wrote:
>
> Sorry but no. I know I am reading a macro call with epp macros. I don’t know that in a language using s-expressions.

There's nothing to stop the syntax to require special names for macros, it doesn't have to be lisp.

> And people use preprocessor macros rarely.

You'd be surprised :-)

/vlad

Miles Fidelman

unread,
Feb 22, 2014, 11:39:51 AM2/22/14
to erlang-questions
Dare I mention that there is LFE.

Miles Fidelman

Vlad Dumitrescu wrote:
>
>
> On 22 Feb 2014 13:14, "Anthony Ramine" <n.o...@gmail.com

> <mailto:n.o...@gmail.com>> wrote:
> >
> > Sorry but no. I know I am reading a macro call with epp macros. I
> don’t know that in a language using s-expressions.
>
> There's nothing to stop the syntax to require special names for
> macros, it doesn't have to be lisp.
>
> > And people use preprocessor macros rarely.
>
> You'd be surprised :-)
>
> /vlad
>
> > --
> > Anthony Ramine
> >
> > Le 22 févr. 2014 à 12:58, Vlad Dumitrescu <vlad...@gmail.com

> <mailto:vlad...@gmail.com>> a écrit :


> >
> > > Sorry, but -define () macros can be just as confusing as lispy
> ones... I would gladly trade them away.
> > >
> > >
> >
>
>
>

> _______________________________________________
> erlang-questions mailing list
> erlang-q...@erlang.org
> http://erlang.org/mailman/listinfo/erlang-questions


--
In theory, there is no difference between theory and practice.
In practice, there is. .... Yogi Berra

Loïc Hoguin

unread,
Feb 22, 2014, 12:46:00 PM2/22/14
to Maxim Velesyuk, erlang-q...@erlang.org
Hello,

On 02/22/2014 10:14 AM, Maxim Velesyuk wrote:
> Often when I feel I need to change ast I remember how clumsy and
> uncomfortable parse transform is, so I overcome myself and make workarounds.

Why do you even feel the need to change the AST? Don't do that.

> But macros are still useful, programmers use parse transform when they
> have no other choice, and projects like merl appear.

Macros are not useful. Macros are what makes a perfectly readable
program into the worst spaghetti code you have ever seen. Even EPP
macros, which look pretty harmless as they have very limited
capabilities, can make things a lot worse than it needs to be.

Sure macros allow you to write less code. But I spend 90% of my time
reading code, so I like programs that are optimized for my reading. If I
always need to refer to some other source file or document to understand
what the code is doing, then I am not going to be using your program
very long.

Macros make your programs worse than PHP and Java combined.

> List and zip comprehensions could be implemented as macros, ets and
> mnesia query language is actually prefix-notation language with code
> quoting, even pattern matching and so long expected maps could be just a
> libraries.

And then one person creates maps, another creates frames, both with the
same or similar syntax but with different semantics, both get used in
the same system, and the code is completely unreadable. No thanks.

> Take a look at c++ and java, how they suffer from inventing new syntax.
> Hopefully Erlang will not turn in such syntax-monster. It has syntax for
> many things, but it still not as flexible as it could be.
>
> This forwards me back to my first question, why?

You don't need it.

--
Loïc Hoguin
http://ninenines.eu

Anthony Ramine

unread,
Feb 22, 2014, 1:55:05 PM2/22/14
to Vlad Dumitrescu, erlang-questions
If it doesn't need to be Lisp, why does it need S-expressions?

--
Anthony Ramine

> Le 22 févr. 2014 à 17:31, Vlad Dumitrescu <vlad...@gmail.com> a écrit :
>
> There's nothing to stop the syntax to require special names for macros, it doesn't have to be lisp.

Vlad Dumitrescu

unread,
Feb 22, 2014, 2:53:58 PM2/22/14
to Anthony Ramine, erlang-questions
On Sat, Feb 22, 2014 at 7:55 PM, Anthony Ramine <n.o...@gmail.com> wrote:
If it doesn't need to be Lisp, why does it need S-expressions?


Because Lisp is much more than its syntax, what we'll get instead is (surprise!) "lisp flavoured erlang" which could decide to have the syntax for macro names start with a ?, for example, making it clear what they are. I don't say it should, just that it is possible to enforce this kind of rule, to help reading the code.

regards,
Vlad

Loïc Hoguin

unread,
Feb 22, 2014, 3:50:30 PM2/22/14
to Maxim Velesyuk, erlang-q...@erlang.org
On 02/22/2014 09:33 PM, Maxim Velesyuk wrote:
> >Sure macros allow you to write less code. But I spend 90% of my time
> reading code, so I like programs that are optimized for my reading. If I
> always need to refer to some other source file or document to understand
> what the code is doing, then I am not going to be using your program
> very long.
>
> But when you see unknown function you still have no idea what it does
> and you have to figure it out. Any abstraction works in same way.

I beg to differ. file:read_file/1 is pretty obvious. So is lists:map/2.
So is most of the functions you can read in your code. You don't need to
know the internals of a function or even the full spec when you are
reading code, you know that you are reading a file, or mapping a
function over a list. When you have macros, you don't even have any idea
whether what you are calling is a function or something else entirely.
And when you have macros that are being used to create new syntax? Then
it is completely alien to all the code around it.

> >And then one person creates maps, another creates frames, both with
> the same or similar syntax but with different semantics, both get used
> in the same system, and the code is completely unreadable. No thanks.
>

> Macros are dangerous as much as powerful. If programmer does not use
> them correctly its a programmer's fault. In the end who is the target
> audience of a programming language for concurrent, fault tolerant
> systems? Probably the programmer who understands what he does. Should
> language designer force programmer to write code in 'correct' way? I
> don't know, but valuable spaces and programming patterns appeared from
> such ideas.

Hello elitism! I mean, seriously? You aren't using Erlang because you
are a better person. You are a better person because you are using
Erlang, because it gives you the tools to do things that are difficult
to do in other languages. Don't dismiss other people simply because they
don't have the chance to work with Erlang.

I will point out though that the target audience of any code, regardless
of the language it is being written in, is the *reader*. It's not the
programmer who wrote it. It's not the compiler. It's the many guys that
will come afterward, read the code, fix bugs, maintain it. If your code
is just using Erlang, then it's pretty easy for them to see what is
going on. If you have changed all the rules using macros, then they need
to learn more things before they can become productive with your code.
They will not like you for it, especially if the production system is
down and they have to fix the bug quickly.

> >You don't need it.
>

> Who but the particular programmer can decide what does he need and what
> doesn't ?

You don't decide what you need. You need it. That's it. If you can do
without and still meet your requirements and deadlines, then you don't
need it, it's just luxury. Except in this case it's luxury similar to
having snakeskin boots, you get what you want, but at the cost of the
blood of the people that will come after you.

Mikhail Gusarov

unread,
Feb 22, 2014, 3:20:21 PM2/22/14
to Vlad Dumitrescu, erlang-questions
"Much more" of Lisp, besides being a functional language (sans
impurity in most Lisps) stems from code-data duality and macro system.
Take it away and you'll gain nothing compared to Erlang as is.

Best regards,
Mikhail Gusarov.

Maxim Velesyuk

unread,
Feb 22, 2014, 3:33:08 PM2/22/14
to Loïc Hoguin, erlang-q...@erlang.org
>Sure macros allow you to write less code. But I spend 90% of my time reading code, so I like programs that are optimized for my reading. If I always need to refer to some other source file or document to understand what the code is doing, then I am not going to be using your program very long.

But when you see unknown function you still have no idea what it does and you have to figure it out. Any abstraction works in same way.

>And then one person creates maps, another creates frames, both with the same or similar syntax but with different semantics, both get used in the same system, and the code is completely unreadable. No thanks.

Macros are dangerous as much as powerful. If programmer does not use them correctly its a programmer's fault. In the end who is the target audience of a programming language for concurrent, fault tolerant systems? Probably the programmer who understands what he does. Should language designer force programmer to write code in 'correct' way? I don't know, but valuable spaces and programming patterns appeared from such ideas.

>You don't need it.

Who but the particular programmer can decide what does he need and what doesn't ?

Mikhail Gusarov

unread,
Feb 22, 2014, 4:33:39 PM2/22/14
to Maxim Velesyuk, erlang-q...@erlang.org
> But when you see unknown function you still have no idea what it does and
> you have to figure it out. Any abstraction works in same way.

There is subtle difference: all functions conform to the function
abstraction: function does not alter/capture names from calling
namespace and function does not arbitrarily transfer control. Function
is a simple abstraction: it accepts arguments and returns result.

As a result, function abstraction allows one to selectively ignore
parts of the code while reading it (which was the driving force behind
structured programming). This luxury is not available to readers of
programs in languages with macros (unhygienic ones), especially if
those don't distinguish macros and functions syntactically.

So, macros are new GOTO statements: they are powerful tool, but power
results in harder-to-understand programs.

Best regards,
Mikhail Gusarov.

Anthony Ramine

unread,
Feb 22, 2014, 7:27:53 PM2/22/14
to Mikhail Gusarov, erlang-questions
Exactly.

You have no idea how many patches I wrote where I have absolutely no clue about the surrounding code. This wouldn’t be as feasible in a homoiconic language with macros where whatever can happen.

--
Anthony Ramine

Le 22 févr. 2014 à 22:33, Mikhail Gusarov <dott...@dottedmag.net> a écrit :

> As a result, function abstraction allows one to selectively ignore
> parts of the code while reading it (which was the driving force behind
> structured programming). This luxury is not available to readers of
> programs in languages with macros (unhygienic ones), especially if
> those don't distinguish macros and functions syntactically.

Bengt Kleberg

unread,
Feb 23, 2014, 1:45:30 AM2/23/14
to erlang-q...@erlang.org
Greetings,

It would be a major drawback to add unhygenic macros to a programming language.

Bengt

Sent from Moxier Mail
(http://www.moxier.com)


----- Ursprungligt meddelande -----
Från: Mikhail Gusarov <dott...@dottedmag.net>
Till: Maxim Velesyuk <loz....@gmail.com>
Kopia: "erlang-q...@erlang.org" <erlang-q...@erlang.org>
Skickat: 22-02-2014 10:35 em
Ämne: Re: [erlang-questions] Erlang and syntax.

Richard A. O'Keefe

unread,
Feb 23, 2014, 4:35:22 PM2/23/14
to Vlad Dumitrescu, erlang-questions

On 23/02/2014, at 12:58 AM, Vlad Dumitrescu wrote:
> On 22 Feb 2014 12:43, "Anthony Ramine" <n.o...@gmail.com> wrote:
> >
> > Erlang is made to be boring. Boring means that it needs to be brain dead easy to comprehend. Brain dead easy means no macros.
>
> Sorry, but -define () macros can be just as confusing as lispy ones... I would gladly trade them away.

Don't forget: Erlang had been around and useful for some time
before the preprocessor was added.

I used to love the Lisp family.
But it's not the only functional syntax.
There are functional languages that make Perl look beautiful
(O'CAML and F#: I'm thinking of you).
There's SML, which would be pretty neat if it had list
comprehensions. (Hello, SML? This is the 21st century
calling! We have something like list comprehensions in C#.
Isn't it about time you caught up?)
There's Haskell and Clean.
Compare

;; Scheme

(define (dot Xs Ys)
(let loop ((Xs Xs) (Ys Ys) (S 0))
(if (and (pair? Xs) (pair? Ys))
(loop (cdr Xs) (cdr Ys) (+ S (* (car Xs) (car Ys))))
S)))

-- Haskell

dot xs ys = loop xs ys 0
where loop (x:xs) (y:ys) s = loop xs ys (s+x*y)
loop _ _ s = s

%% Erlang

dot(Xs, Ys) ->
loop(Xs, Ys, 0).

loop([X|Xs], [Y|Ys], S) ->
loop(Xs, Ys, S + X*Y);
loop(_, _, S) ->
S.

Does anyone else remember my proposal that would have had us
write this as

dot(Xs, Ys) ->
(S where S = 0 then S+X*Y || X <- Xs & Y <- Ys).


Historically, Prolog -> Strand-88 -> Erlang.
I still don't understand why Erlang uses band bor bnot bsl bsr
instead of Prolog's /\ \/ \ << >> (I understand why it can't
_now_) and why Erlang reversed the sense of == and =:=.

Erlang's syntax hits a "familiarity" sweet spot:
- it uses infix operators like Pascal/C/Javascript
(unlike Lisp)
- it uses f(X, Y, Z) function calls like Pascal/C/Javascript
(unlike Haskell)
- it uses = for binding a value to a variable, which looks
a bit like assignment to a C/Javascript programmer

Or rather, it _did_ hit a sweet spot before records and
the preprocessor were added. Even then, the preprocessor
is aimed squarely at C programmers. -define macros may
be (heck, they _are_) even more confusing than lispy ones,
but they are confusing in a comfortingly familiar way.

Richard A. O'Keefe

unread,
Feb 23, 2014, 4:43:33 PM2/23/14
to Mikhail Gusarov, erlang-q...@erlang.org

On 23/02/2014, at 10:33 AM, Mikhail Gusarov wrote:

>> But when you see unknown function you still have no idea what it does and
>> you have to figure it out. Any abstraction works in same way.
>
> There is subtle difference: all functions conform to the function
> abstraction: function does not alter/capture names from calling
> namespace and function does not arbitrarily transfer control. Function
> is a simple abstraction: it accepts arguments and returns result.

There is Lisp. And then there is Scheme.

Scheme define-syntax macros do NOT alter/capture names from
the calling namespace. They are said to be "hygienic."

Thanks to throw/1, function calls CAN transfer control.
(The Scheme analogue is call-with-current-continuation.)

So a Scheme-flavoured-Erlang would conform to the claimed
abstraction every bit as well as Erlang without any kind of
macros.

Duncan McGreggor

unread,
Feb 23, 2014, 5:35:21 PM2/23/14
to Richard A. O'Keefe, erlang-q...@erlang.org
On Sun, Feb 23, 2014 at 3:43 PM, Richard A. O'Keefe <o...@cs.otago.ac.nz> wrote:

On 23/02/2014, at 10:33 AM, Mikhail Gusarov wrote:

>> But when you see unknown function you still have no idea what it does and
>> you have to figure it out. Any abstraction works in same way.
>
> There is subtle difference: all functions conform to the function
> abstraction: function does not alter/capture names from calling
> namespace and function does not arbitrarily transfer control. Function
> is a simple abstraction: it accepts arguments and returns result.

There is Lisp.  And then there is Scheme.

Scheme define-syntax macros do NOT alter/capture names from
the calling namespace.  They are said to be "hygienic."

Thanks to throw/1, function calls CAN transfer control.
(The Scheme analogue is call-with-current-continuation.)

So a Scheme-flavoured-Erlang would conform to the claimed
abstraction every bit as well as Erlang without any kind of
macros.

LFE has come up a couple of times (yay!), but as for an Erlang Scheme, there is this:
  https://github.com/the-concurrent-schemer/scm

Also, I believe that Joxa is a Lisp-1 (though it does have unhygenic macros).

d

Siraaj Khandkar

unread,
Feb 24, 2014, 6:38:43 PM2/24/14
to erlang-q...@erlang.org
I fail to see the ugliness in:

let dot xs ys =
let rec loop = function
| x::xs, y::ys, s -> loop (xs, ys, s + x * y)
| _, _, s -> s
in
loop (xs, ys, 0)


>
> Does anyone else remember my proposal that would have had us
> write this as
>
> dot(Xs, Ys) ->
> (S where S = 0 then S+X*Y || X <- Xs & Y <- Ys).

Where is S coming from here?

Richard A. O'Keefe

unread,
Feb 24, 2014, 8:36:22 PM2/24/14
to Siraaj Khandkar, erlang-q...@erlang.org

On 25/02/2014, at 12:38 PM, Siraaj Khandkar wrote:
> I fail to see the ugliness in:
>
> let dot xs ys =
> let rec loop = function
> | x::xs, y::ys, s -> loop (xs, ys, s + x * y)
> | _, _, s -> s
> in
> loop (xs, ys, 0)

F# syntax is based on CAML.
CAML syntax is based on an early revision of ML
before the major cleanup that produced SML.
(Not unlike the way C perpetuated a blunder
that had already been fixed in its ancestor BCPL.)

Already in this example it shows up.
There are two functions.
They have to be declared differently.

There are two kinds of lambda expression:

fun Pat+ [when Guard] -> Expr

may have multiple *arguments* but not multiple *clauses*,
while

function [|] Pat [when Guard] -> Expr
{ | Pat [when Guard] -> Expr }...

may have multiple *clauses* but not multiple *arguments*.

The calls to loop have parentheses around the arguments
(to make a tuple, because a function with multiple clauses
can't have multiple arguments), but the pattern matches
_don't_ have the parentheses. By the way, tuples-without-
parentheses are why O'CAML has to use semicolons to
separate list elements, unlike practically everything else
where commas are used.

The outer function 'dot' is declared using a let-binding
value_name {parameter}... = expr
which allows multiple *arguments* but not multiple
*clauses*, which is why the inner function 'loop'
*has* to use a lambda-expression.

In contrast, here's the same thing in SML.

fun dot xs ys =
let fun loop (x::xs) (y::ys) s = loop xs ys (s+x*y)
| loop _ _ s = s
in loop xs ys 0
end

In SML, functions can have BOTH multiple arguments and
multiple clauses.

>> Does anyone else remember my proposal that would have had us
>> write this as
>>
>> dot(Xs, Ys) ->
>> (S where S = 0 then S+X*Y || X <- Xs & Y <- Ys).
>
> Where is S coming from here?

From the 'where' part. It's
( <result> where <binding>,... || <generators and tests> )
and a <binding> is
<pattern> = <initial value> [then <subsequent value>].

'let P = E0 in E1' and 'E1 where P = E0' are traditional
alternatives in functional programming.

It's a cross between Lisp's DO (the <binding> part and the
<result> part) and comprehensions (the <generators and tests> part).

Siraaj Khandkar

unread,
Feb 25, 2014, 10:35:47 PM2/25/14
to erlang-q...@erlang.org
On 2/24/14, 8:36 PM, Richard A. O'Keefe wrote:
>
> On 25/02/2014, at 12:38 PM, Siraaj Khandkar wrote:
>> I fail to see the ugliness in:
>>
>> let dot xs ys =
>> let rec loop = function
>> | x::xs, y::ys, s -> loop (xs, ys, s + x * y)
>> | _, _, s -> s
>> in
>> loop (xs, ys, 0)
>
> F# syntax is based on CAML.

The above example is in OCaml, on which F# syntax was initially based
on, not directly on Caml (which preceded OCaml).

http://ocaml.org/learn/history.html


> CAML syntax is based on an early revision of ML
> before the major cleanup that produced SML.

Yes.


> (Not unlike the way C perpetuated a blunder
> that had already been fixed in its ancestor BCPL.)
>
> Already in this example it shows up.
> There are two functions.
> They have to be declared differently.

They _can_ but don't _have_ to be. I chose to write it that way to match
your examples most closely. Personally, I would rather write it as:

let dot xs ys =
let rec loop xs ys s =
match xs, ys with
| [], [] -> s
| x::xs, y::ys -> loop xs ys (s + x * y)
| _, _ -> assert false
in
loop xs ys 0

or better yet, using stdlib:

let dot =
List.fold_left2 (fun s x y -> s + x * y) 0


>
> There are two kinds of lambda expression:
>
> fun Pat+ [when Guard] -> Expr
>
> may have multiple *arguments* but not multiple *clauses*,
> while
>
> function [|] Pat [when Guard] -> Expr
> { | Pat [when Guard] -> Expr }...
>
> may have multiple *clauses* but not multiple *arguments*.
>
> The calls to loop have parentheses around the arguments
> (to make a tuple, because a function with multiple clauses
> can't have multiple arguments), but the pattern matches
> _don't_ have the parentheses.

This describes the form, but does not make any judgements. Why is that
so terrible? I don't see the problem.

Sure, I can entertain the idea that having an additional form can be
seen as less than perfectly symmetric, but it is still far from the
extreme negativity that you cast when speaking of OCaml (saying it is
uglier than Perl is about as extreme as it gets). There's never a
situation where one _has_ to use the form "function" over "fun", it is
provided merely as an alternative that you _may_ find convenient at
times.


> By the way, tuples-without-
> parentheses are why O'CAML has to use semicolons to
> separate list elements, unlike practically everything else
> where commas are used.

"unlike practically everything else" is a really weak statement on an
Erlang mailing list ;)


> The outer function 'dot' is declared using a let-binding
> value_name {parameter}... = expr
> which allows multiple *arguments* but not multiple
> *clauses*, which is why the inner function 'loop'
> *has* to use a lambda-expression.

Again, it doesn't *have* to, but sometimes it is convenient.

Though, frankly, I'm not fully satisfied with function declaration in
any language that I'm familiar with so far. I mostly lean toward the way
it is done in OCaml, because it reinforces the idea that a function is
no different from any other value, so you bind it to a name in exactly
the same way (let name = expr), where as in SML there's a distinction
between (val name = expr) and (fun name arg1 .. argn = expr).

Ideally, I'd probably keep only the "fun" lambda expression form (and
add clauses), because why should there ever be a distinction between a
function declaration and a lambda expression? A named function is just a
lambda attached to a name, after all...

And, before you bring it up as a gold standard, I find Haskell's way
quite unsatisfactory as well. Primarily in two ways: 1) all bindings are
_implicitly_ recursive and 2) no shadowing (which I know, from other
discussions, that you hate, but that is a preference...).


> In contrast, here's the same thing in SML.
>
> fun dot xs ys =
> let fun loop (x::xs) (y::ys) s = loop xs ys (s+x*y)
> | loop _ _ s = s
> in loop xs ys 0
> end
>
> In SML, functions can have BOTH multiple arguments and
> multiple clauses.

But not guards...

As a user of both languages, I generally agree - SML is the more
elegantly designed language [1]. It was designed to be so, where as
OCaml evolved more organically (much like Erlang has), as a tool for
implementation of Coq, without a formal definition, but nonetheless
rigorously worked out theory behind the language additions.

There has been a lot of interesting progress in both, the OCaml
language and ecosystem, since the last time you seriously looked at it
(which I can only imagine must have been a while):

- recursive modules
- private types (which differ from abstract types by allowing reading
but still not constructing the representation)
- locally abstract types (which allow "shared secrets", that Harper
said were only possible in SML [2])
- first-class modules
- GADTs!
- a really awesome package manager [3]

More here: http://caml.inria.fr/pub/docs/manual-ocaml-400/manual021.html


[1]: Though I do find some things unpleasant, mainly the lack of
syntactic distinction between constructors and identifiers.

[2]:
http://existentialtype.wordpress.com/2012/12/03/exceptions-are-shared-secrets

[3]: https://opam.ocaml.org


>>> Does anyone else remember my proposal that would have had us
>>> write this as
>>>
>>> dot(Xs, Ys) ->
>>> (S where S = 0 then S+X*Y || X <- Xs & Y <- Ys).
>>
>> Where is S coming from here?
>
> From the 'where' part. It's
> ( <result> where <binding>,... || <generators and tests> )
> and a <binding> is
> <pattern> = <initial value> [then <subsequent value>].
>
> 'let P = E0 in E1' and 'E1 where P = E0' are traditional
> alternatives in functional programming.
>
> It's a cross between Lisp's DO (the <binding> part and the
> <result> part) and comprehensions (the <generators and tests> part).

I really like the idea of being able to express "X <- Xs & Y <- Ys",
but, even with the explanation, I still find the left hand side quite
unintuitive.

Richard A. O'Keefe

unread,
Feb 26, 2014, 12:06:11 AM2/26/14
to Siraaj Khandkar, erlang-q...@erlang.org

On 26/02/2014, at 4:35 PM, Siraaj Khandkar wrote:

Let's put it this way: the issues I discussed were
(a) verified using the latest available O'CAML manual and
(b) amongst but far from the only features of O'CAML that
had me unable to bear to use it.

O'CAML has always been famed for the excellent performance
of its compiler, and I used to cherish a daydream of using
its preprocessing facilities to give it a syntax I liked..

F# has a number of merits -- which is why I have it on my
machine -- including native threads and some very interesting
innovations. It actually has two syntaxes. The syntax is,
however, very complex.

>> There are two kinds of lambda expression:
>>
>> fun Pat+ [when Guard] -> Expr
>>
>> may have multiple *arguments* but not multiple *clauses*,
>> while
>>
>> function [|] Pat [when Guard] -> Expr
>> { | Pat [when Guard] -> Expr }...
>>
>> may have multiple *clauses* but not multiple *arguments*.
>>
>> The calls to loop have parentheses around the arguments
>> (to make a tuple, because a function with multiple clauses
>> can't have multiple arguments), but the pattern matches
>> _don't_ have the parentheses.
>
> This describes the form, but does not make any judgements. Why is that
> so terrible? I don't see the problem.

Surely nobody expects a syntax to be _capable_ of making judgements.
I don't understand you here.

The problem is having two different ways to do the same kind of
thing, both of them crippled, and neither having a name that
reminds you of the direction of its crippling.

Back when I was trying to write O'CAML, I banged my head
on this over and over again: I wanted to write one simple
function with
- no tuples,
- multiple arguments, and
- multiple clauses.

Here's a simple little Erlang function:

fun (a, b) -> 1
; (a, _) -> 2
; (_, b) -> 3
; (_, _) -> 4
end

Writing that is not simple in O'CAML, and
there doesn't seem to be any *REASON* for the restrictions.


> Sure, I can entertain the idea that having an additional form can be
> seen as less than perfectly symmetric,

We're not talking about "less than perfect", we are talking
about *unmotivated crippling*. We're talking about having
to remember than 'fun' does multi-arg but not multi-case
and 'function' does multi-case but not multi-arg.
(If 'function' were spelled 'matchfun' it would then
*obvious* which one was like 'match' and which wasn't.)

> There's never a
> situation where one _has_ to use the form "function" over "fun", it is
> provided merely as an alternative that you _may_ find convenient at
> times.

No, 'function' is not "merely an alternative".
'fun' and 'function' do _different_ things, and
neither of them does a full job.

Let's look back at that little Erlang function.
The closest I can come in O'CAML is

fun x y ->
match x with
| A -> match y with
| B -> 1
| _ -> 2
| _ -> match y with
| B -> 3
| _ -> 4

If I'm going to have to twist my head around something
complicated in a language, I'd rather it was something
like type families than pointlessly restrictive syntax.


>
> Though, frankly, I'm not fully satisfied with function declaration in
> any language that I'm familiar with so far. I mostly lean toward the way
> it is done in OCaml, because it reinforces the idea that a function is
> no different from any other value, so you bind it to a name in exactly
> the same way (let name = expr), where as in SML there's a distinction
> between (val name = expr) and (fun name arg1 .. argn = expr).

But in SML nothing stops you writing

val f = fun ... -> ...

if you want to. Turning your own words against you,
'fun' in SML is "merely an alternative syntax that you
may find convenient".

> And, before you bring it up as a gold standard, I find Haskell's way
> quite unsatisfactory as well. Primarily in two ways: 1) all bindings are
> _implicitly_ recursive and 2) no shadowing (which I know, from other
> discussions, that you hate, but that is a preference...).

The implicit recursive bit -- which is what we also have in
Erlang -- is in practice what I always want, and it is strongly
connected with the free order of function definitions -- which
we also have in Erlang.

_Not_ having to have a weird special form for declaring
a group of mutually recursive functions is one of the
things that had me running from SML to Haskell (and Erlang!).

> There has been a lot of interesting progress in both, the OCaml
> language and ecosystem, since the last time you seriously looked at it
> (which I can only imagine must have been a while):

There are some incredibly interesting tools I would like to use
that are written in O'CAML. I even went to the trouble of
setting up a special VirtualBox VM running a specific version
of Linux to run one of them. That was last year.
>
> - recursive modules
> - private types (which differ from abstract types by allowing reading
> but still not constructing the representation)
> - locally abstract types (which allow "shared secrets", that Harper
> said were only possible in SML [2])
> - first-class modules
> - GADTs!
> - a really awesome package manager [3]

Those are all nice things, although I note that the Goedel
programming language had GADTs back in the 1980s -- mainly
because John Lloyd couldn't think of a reason _not_ to
offer them. He wasn't aware of the type inference issues
they cause, but since Goedel did type checking, not type
inference, that wouldn't have been a problem anyway.

I have kept going back to O'CAML because of the fantastic
things that have been done with it, but I have really really
bad memories of GODI. If OPAM lets me forget about GODI,
I may try O'CAML again.

>>>> (S where S = 0 then S+X*Y || X <- Xs & Y <- Ys).
>>>
>>> Where is S coming from here?
>>
>> From the 'where' part. It's
>> ( <result> where <binding>,... || <generators and tests> )
>> and a <binding> is
>> <pattern> = <initial value> [then <subsequent value>].
>>
>> 'let P = E0 in E1' and 'E1 where P = E0' are traditional
>> alternatives in functional programming.
>>
>> It's a cross between Lisp's DO (the <binding> part and the
>> <result> part) and comprehensions (the <generators and tests> part).
>
> I really like the idea of being able to express "X <- Xs & Y <- Ys",
> but, even with the explanation, I still find the left hand side quite
> unintuitive.

Familiarity again.

First, multiple generators in parallel is a straight
steal from Clean.
Second,
<expression> where <bindings>
was back in Landin's "The next 700 programming languages" paper
and has appeared in several programming languages since ISWIM.

The proposed syntax has (Expression where Matches)
as a special case. Having used both 'let' and 'where' in
Haskell, I find that

roots a b c = (-b + sqrt discrim)/(2*a)
where discrim = b^2 - 4*a*c

reads better than

roots a bc =
let discrim = b^2 - 4*a*c
in (-b + sqrt discrim)/(2*a)

In Haskell, 'let' and 'where' do slightly different things
('let' is local to an expression, 'where' goes with a
group of guarded alternatives in the same clause).

I tried (let <bindings> [while <guard>] [|| <generators and tests>]
in <result>)
but found that it was more awkward.

What really matters is that we _could_ have a form that does
for fold+map+filter what list comprehensions do for map+filter.

Siraaj Khandkar

unread,
Feb 27, 2014, 12:30:33 PM2/27/14
to erlang-q...@erlang.org

On 2/26/14, 12:06 AM, Richard A. O'Keefe wrote:
>>> There are two kinds of lambda expression:
>>>
>>> fun Pat+ [when Guard] -> Expr
>>>
>>> may have multiple *arguments* but not multiple *clauses*,
>>> while
>>>
>>> function [|] Pat [when Guard] -> Expr
>>> { | Pat [when Guard] -> Expr }...
>>>
>>> may have multiple *clauses* but not multiple *arguments*.
>>>
>>> The calls to loop have parentheses around the arguments
>>> (to make a tuple, because a function with multiple clauses
>>> can't have multiple arguments), but the pattern matches
>>> _don't_ have the parentheses.
>>
>> This describes the form, but does not make any judgements. Why is that
>> so terrible? I don't see the problem.
>
> Surely nobody expects a syntax to be _capable_ of making judgements.
> I don't understand you here.

Sorry for poor choice of words, I meant to say that you've described the
facts but did not explain why you feel it is problematic (which at this
point you already did).


>
> The problem is having two different ways to do the same kind of
> thing, both of them crippled, and neither having a name that
> reminds you of the direction of its crippling.
>
> Back when I was trying to write O'CAML, I banged my head
> on this over and over again: I wanted to write one simple
> function with
> - no tuples,

Why the aversion to tuples? Are you planning to partially apply it?
Because if so, it is not something you can do in Erlang to begin with...
Erlang functions take a fixed number of arguments, which is practically
equivalent to taking tuples.


> - multiple arguments, and
> - multiple clauses.
>
> Here's a simple little Erlang function:
>
> fun (a, b) -> 1
> ; (a, _) -> 2
> ; (_, b) -> 3
> ; (_, _) -> 4
> end
>
> Writing that is not simple in O'CAML, and
> there doesn't seem to be any *REASON* for the restrictions.

The two alternatives are very simple, see bellow.


>> Sure, I can entertain the idea that having an additional form can be
>> seen as less than perfectly symmetric,
>
> We're not talking about "less than perfect", we are talking
> about *unmotivated crippling*. We're talking about having
> to remember than 'fun' does multi-arg but not multi-case
> and 'function' does multi-case but not multi-arg.
> (If 'function' were spelled 'matchfun' it would then
> *obvious* which one was like 'match' and which wasn't.)
>
>> There's never a
>> situation where one _has_ to use the form "function" over "fun", it is
>> provided merely as an alternative that you _may_ find convenient at
>> times.
>
> No, 'function' is not "merely an alternative".
> 'fun' and 'function' do _different_ things, and
> neither of them does a full job.

I agree that it is not fully satisfying, I would _much_ rather have only
the "fun" form with clauses (and I don't actually know the history of
this choice of syntax). But again, I just don't feel this outweighs all
the other great aspects of the language, especially when "match" can
easily fill-in for the deficiencies of "fun". See bellow.


>
> Let's look back at that little Erlang function.
> The closest I can come in O'CAML is
>
> fun x y ->
> match x with
> | A -> match y with
> | B -> 1
> | _ -> 2
> | _ -> match y with
> | B -> 3
> | _ -> 4

Do you need partial application?

If yes, then this:

fun t t' ->
match t, t' with
| A, B -> 1
| A, _ -> 2
| _, B -> 3
| _, _ -> 4

if no, it is even simpler:

function
| A, B -> 1
| A, _ -> 2
| _, B -> 3
| _, _ -> 4


Exactly as is, your version would not work, because it is ambiguous
where the second case of x belongs (as a second case of x or the 3rd
case of 1st y match?). The solution would be to use parenthesis of
begin...end, but it is even better to just flatten the whole thing, as I
did above. Also, the indentation depth is a bit excessive, but that's a
personal thing, of course.


>> Though, frankly, I'm not fully satisfied with function declaration in
>> any language that I'm familiar with so far. I mostly lean toward the way
>> it is done in OCaml, because it reinforces the idea that a function is
>> no different from any other value, so you bind it to a name in exactly
>> the same way (let name = expr), where as in SML there's a distinction
>> between (val name = expr) and (fun name arg1 .. argn = expr).
>
> But in SML nothing stops you writing
>
> val f = fun ... -> ...
>

Since "fun" is not a lambda expression, you must mean this:

val f = fn ... => ...;


>> And, before you bring it up as a gold standard, I find Haskell's way
>> quite unsatisfactory as well. Primarily in two ways: 1) all bindings are
>> _implicitly_ recursive and 2) no shadowing (which I know, from other
>> discussions, that you hate, but that is a preference...).
>
> The implicit recursive bit -- which is what we also have in
> Erlang -- is in practice what I always want, and it is strongly
> connected with the free order of function definitions -- which
> we also have in Erlang.

In Erlang it is far less surprising, because it only appears in
module-level function definitions, not for just any name binding.

$ erl
1>
1> X = X.
* 2: variable 'X' is unbound
2>

$ ghci
Prelude>
Prelude> let x = x
Prelude>
Prelude> x

Never returns!


> _Not_ having to have a weird special form for declaring
> a group of mutually recursive functions is one of the
> things that had me running from SML to Haskell (and Erlang!).

Being a bear of very little brain, I want mutual recursion to be
explicit and "let rec" and "and" are a miniscule syntactic price to pay
for the aid in reasoning that it gives to the reader (_and_ writer for
that matter, since you get compiler feedback).


>> There has been a lot of interesting progress in both, the OCaml
>> language and ecosystem, since the last time you seriously looked at it
>> (which I can only imagine must have been a while):
>
> There are some incredibly interesting tools I would like to use
> that are written in O'CAML. I even went to the trouble of
> setting up a special VirtualBox VM running a specific version
> of Linux to run one of them. That was last year.

Everything works for me on Mac as well as Ubuntu. The only things I
could not get to build on a Mac were IDE for Why3 and GUI for Unison
(though it I think there's a packaged binary available online somewhere).


>> - recursive modules
>> - private types (which differ from abstract types by allowing reading
>> but still not constructing the representation)
>> - locally abstract types (which allow "shared secrets", that Harper
>> said were only possible in SML [2])
>> - first-class modules
>> - GADTs!
>> - a really awesome package manager [3]
>
> Those are all nice things, although I note that the Goedel
> programming language had GADTs back in the 1980s -- mainly
> because John Lloyd couldn't think of a reason _not_ to
> offer them. He wasn't aware of the type inference issues
> they cause, but since Goedel did type checking, not type
> inference, that wouldn't have been a problem anyway.
>
> I have kept going back to O'CAML because of the fantastic
> things that have been done with it, but I have really really
> bad memories of GODI. If OPAM lets me forget about GODI,
> I may try O'CAML again.

Indeed, OPAM is quite an improvement. I'm not sure that I can even spell
GoDdy anymore :)


>>>>> (S where S = 0 then S+X*Y || X <- Xs & Y <- Ys).
>>>>
>>>> Where is S coming from here?
>>>
>>> From the 'where' part. It's
>>> ( <result> where <binding>,... || <generators and tests> )
>>> and a <binding> is
>>> <pattern> = <initial value> [then <subsequent value>].
>>>
>>> 'let P = E0 in E1' and 'E1 where P = E0' are traditional
>>> alternatives in functional programming.
>>>
>>> It's a cross between Lisp's DO (the <binding> part and the
>>> <result> part) and comprehensions (the <generators and tests> part).
>>
>> I really like the idea of being able to express "X <- Xs & Y <- Ys",
>> but, even with the explanation, I still find the left hand side quite
>> unintuitive.
>
> Familiarity again.
>
> First, multiple generators in parallel is a straight
> steal from Clean.
> Second,
> <expression> where <bindings>
> was back in Landin's "The next 700 programming languages" paper
> and has appeared in several programming languages since ISWIM.
>
> The proposed syntax has (Expression where Matches)
> as a special case.

"where" as an alternative to "let" is quite nice and I like the way it
works in Haskell. What I found unintuitive in your proposal is the
initialization of the accumulator and the usage of "then". Having looked
at it long-enough though, it is kind of growing on me, but certainly was
strange at first :)
Reply all
Reply to author
Forward
0 new messages