What is the difference between ; , and "."

I am taking a course on Udemy and noticed some lines use . while others use different line terminators. Here is the example given:

-module(factorial).

-export([factorial/1]).

factorial(1) →
    1;

factorual(N) →
    N * factorial(N-1).

On multilines I see , used. What is the difference between the 3?

2 Likes

I don’t recall where I got this from, and maybe it won’t make sense to you.

I view it as a type of punctuation.

Semi-colons separate clauses, commas separate statements, and periods are the terminator.

2 Likes

What are clauses?

For example why didn’t he write it like this?

-module(factorial).

-export([factorial/1]).

factorial(1) →
    1.

factorual(N) →
    N * factorial(N-1).
1 Like

I cannot answer that using your example since the code you posted is not valid Erlang.

A simple example. The assignment statement is separated from the case statement by a comma. The clauses of the case are separated by a semi-colons, and the function is terminated with a period.

my_fun(A) →
B = A + 1,
case B of
1 → ok;
2 → not_ok
end.

2 Likes

Sorry, I fixed the errors in the code.

2 Likes

This is how function declaration is look like in Erlang.
https://www.erlang.org/doc/reference_manual/functions.html

3 Likes

Do you think it might be a good idea to read the
documentation? Or a good book, like LYSE?

Complete declarations are TERMINATED by full stops.

ALTERNATIVES are separated by semicolons (which you
should pronounce as “or else”). I think it is a
mistake to put semicolons at the end of lines. As
in Prolog, they belong at the beginning.

Expressions to be evaluated in SEQUENCE are
separated by commas (which you should pronounce
as “and then”).

There is never any choice about which one to use.

1 Like

So basically what you’re saying is:

-module(factorial).

-export([factorial/1]).

factorial(1) →
    1;
    %% or else %%

factorual(N) →
    N * factorial(N-1).

You can have non terminating else statements in function bodies?

My book Introducing Erlang is on the way, but won’t arrive for another week or 2. The documentation doesn’t really help much as the example function given in the documentation is similar to the one I posted above from a course I am taking. The Lesson is on function clauses and recursion. The example given by the instructor is the one I posted above. However, he doesn’t explain why one function ends in ; while the other one end in the standard .

1 Like

I don’t know what you mean by that :sweat_smile:

Seriously, I would advise to wait for it, or try LYSE (which you can read for free online) until it arrives :wink:

In a nutshell, because it isn’t two functions but one and the same (Well, since you have a typo in there, you actually have 2 :rofl:) If you call factorial with an argument, the clauses will be tried one after another, and the first that matches is executed. You can imagine this…

factorial(1) -> 1;
factorial(N) -> N*factorial(N-1).

… written like this…

factorial(N) ->
    case N=:=1 of
        true -> 1;
        false -> N*factorial(N-1)
    end.

… if it helps. But function clauses are just so much nicer :heart_eyes:
Besides, you now have two case clauses instead of two function clauses. Plus, you may also notice there is nothing after the last case clause, no comma, no semicolon, no period. So I probably just added to your confusion (sorry :sweat_smile:).
So yeah, better wait for your book or try LYSE :wink:

3 Likes

Hello Ookma-Kyi,

The two clauses are parts of the same function. You can think of function clauses as different versions of the same function which do different things depending on what arguments the function is called with.

Clauses are terminated by semicolons, and the last one is terminated by a period, which tells the Erlang compiler that there aren’t any more clauses. Erlang (along with some other functional languages) uses “pattern-matching” to decide which clause should run, based on the arguments the function is called with. It also doesn’t use loops, only recursive (self-calling) function calls. So if you call factorial(1) then it knows to just return 1 because it’s right there in the first “base case” clause, but if you call factorial(5) then it’ll call the second one, which calls itself with 4, then 3, 2, and then it’s 1 again, so the base case is returned.

This might sound confusing but it’s really powerful, and along with putting things together in modules, it’s really a fundamental part of programming Erlang. You’re not going to get far without understanding how this works - but once you get the hang of using pattern-matching with recursive functions, you won’t feel the need to use “if/else” any more, as you’ll start to see that it’s handled by these different clauses (or similarly in a “case”expression inside one of them).

This can be hard to get to grips with when you come from a different language background. When I was first learning Erlang, I found “Learn You Some Erlang” really helpful in understanding that, and a lot of other aspects of Erlang. Each chapter is about a discrete topic, and all the basics of functions, clauses, modules, exports, and sequential and recursive code are very well covered.

I can understand wanting to have a printed book to read through and refer back to - I did the same with “Programming Erlang”, and “Erlang/OTP in Action” - but I really think working through some of LYSE online while waiting for your printed book could really help you get a bit more of an understanding of some of these basic building blocks, which are absolutely essential before getting into the real meat of working with Erlang - i.e. working with concurrency and learning to use OTP.

:slight_smile:

(Edit: looks like @Maria-12648430 got there quicker and more concisely than I did :grimacing:)

3 Likes

Thanks actually this makes more sense, thanks. The instructor of the course didn’t explain the code or how or why it is written the way it. He just gives an intro to the lesson, types the code, opens the Erlang shell to run the code and then ends that lesson. Basically your the missing manual to this lessons(compliments his course :smile:).

This is actually a lesson, but isn’t for quite some time into the course. It does sound “Fun” if you know what I mean. I am going to start supplementing the course with LYSE reading as the course is a bit lacking.

2 Likes

Another way to think about clauses here is that they make statements about factorials.

factorial(1) -> 1 says “The factorial of 1 is 1, that’s a fact”.
factorial(N) -> N * factorial(N - 1) says "The factorial of some number N is that number N multiplied with the factorial of N - 1". (Actually, it doesn’t really say that. You can give it a float or a negative integer and you will end in endless recursion; if you give it something that is not a number at all, you will get a badarith exception.)

So if you call it like factorial(3), the first clause will be passed over since you are not asking for the factorial of 1. The next clause matches and gets executed, resulting in a call like factorial(2).
Again, the first clause will be passed over since you’re asking for the factorial of 2, not 1. The next clause again matches, resulting in a call like factorial(1).
Now, the first clause matches, as you are asking for the factorial of 1, and so it returns the answer 1.
The 1 bubbles up into the previous call, where it is multiplied by 2, and that is returned as the answer.
The 2 bubbles up again into the previous call, gets multiplied by 3, and finally 6 is returned as the final answer.

As you see, clauses are tried in the order in which they are written, and so you can’t write them the other way round.

Thinking this way about clauses is handy in cases of recursive functions such as this. In other cases, especially if side effects are involved, that way of thinking does not work out so well. But I think you get the idea.

3 Likes

“Clauses are terminated by semicolons,”
No. No. NO. NO! NO!

Semicolons are SEPARATORS in Erlang, not TERMINATORS.
No clauses are ever terminated in Erlang.
factorial(0) → 1 % This is a clause, it is NOT TERMINATED
; % This SEPARATES the two clauses
factorial(N) → N*factorial(N-1) % Also a clause. NOT TERMINATED
. % Terminates the FUNCTION not any clause.

Function clauses are separated, not terminated, by semicolons.
Case clauses are separated, not terminated, by semicolons.
If clauses are separated, not terminated, by semicolons.
Try clauses are separated, not terminated, by semicolons.
Receive clauses are separated, not terminated, by semicolons.

Run, do not walk, to https://learnyousomeerlang.com/
and start free on-line reading.

3 Likes

Good call @nzok, thanks for the correction. Separator is not only the right term but describes it much better :man_shrugging:t2:

1 Like

Exactly what @nzok said.

Semicolons are separators in most languages that have them, if there’s a trailing one it’s just because there is a following ‘empty’ thing as well that the language authors didn’t optimize out as well.

Semicolons are separators in Algol 60, Algol W, Simula 67,
Algol 68, and Pascal, also Pop-2, Pop-11, Prolog, Goedel,
Mercury, Strand88, Erlang, F# (for an unfortunate reason)
and (technically) Haskell. They are terminators in PL/I,
BSL, Hal-S, CHILL, C,
Objective C, C++, Objective C++, Ada, Java, JavaScript, …
In (modern) Fortran I suppose semicolon is a separator and
end-of-logical-line a terminator, just to confound things.

I am not sure how to quantify “most programming languages”.

Most code programmers are likely to meet these days
(I mean, how much code is written in Ur or Disciple?)
uses semicolons as terminators, because of all the language
designers who decided to imitate C.

Consider if (x == 1) {y = 2;}
The semicolon is not optional. There is no empty
statement between “;” and “}”. The semicolon is an
intrinsic part of the statement “y = 2;”. The
construct “y = 2” in C is NOT a statement. It is an
expression, and not a statement. So in C
if (x == 1) {y = 2}
is not “optimised”, it’s illegal.

Now Erlang does not have statements at all,
and does not admit empty expressions. So
the classification between separators and terminators
is easy: After , is another
or

  • required? is a separator.
  • forbidden? is a terminator.
    Hence , ; | are separators and . is a terminator in Erlang.
2 Likes

I’m not sure if you’ve managed to read my PM Richard, but could you disable hard-wrapping on your email client when posting to the forum please? There’s a good article about it here but in short it makes it more difficult to read your posts, especially on mobiles and tablets or those with narrower browser windows.

Here’s what your post looks like on my mobile:

Screen readers also interpret a paragraph break as “blank,” and a line break may not be indicated, with the text on the new line making it sound like a new sentence.

1 Like

‘most’ as in it can be seen that way, if viewed as a separator even in C or C++ just means it doesn’t need a following one as the ‘right side’ of the ; operator can be empty and an expression cannot be standalone unless in specific expression contexts (which is what the ; operator provides for some odd reason).

1 Like