[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