[erlang-questions] Erlang and syntax.

Siraaj Khandkar siraaj@REDACTED
Thu Feb 27 18:30:33 CET 2014


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 :)


> 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.





More information about the erlang-questions mailing list