| Author |
Message |
< Erlang ~ why not a "return" statement in Erlang |
| jbj |
Posted: Thu Sep 18, 2008 8:32 pm |
|
|
|
Joined: 16 Sep 2008
Posts: 4
|
bkl wrote: jbj,
I see. So, you propose
No, I don't. You asked why Lisp has return and Erlang doesn't. I was pointing at that Erlang has the same operator with a different name.
bkl wrote:
but
I still have reservations about using exception handling as a way to
handle a simple, well, "return"...
That was kind of my point. return in Lisp is something special, it bypasses the normal evaluation rules of the languages. It's exceptional if you will. But since it has a familiar name it isn't really seen this way. For whatever reason Erlang named this behavior in a way that changes how one thinks about it (e.g. "but, this isn't an exceptional circumstance!").
What I would actually propose is when you find yourself reaching for return, consider breaking the functions out more. Refactor. I admit I am more of a Smalltalk programmer so I like lots of little functions, but I see this a lot from the Haskell camp as well. Both camps do it because of the composability.
Interesting. Thanks.
bkl wrote:
I agree with your statement of
"you don't know how to program in a language until you know how to
express your programs the way the language wants to express them."
And I have been using all the good features you mentioned in Erlang,
and quite like them  . But I cannot agree with the (seemingly
recursive) reason of "Erlang does not support return because you need
to use features other than return", and "it's good for you"; i.e.
1. "return" can be replaced by other mechanisms in Erlang
(e.g. catch-throw)
2. because code with "return" is bad practice.
My counter-arguments are:
For 1: "XYZ" can be replaced by other mechanisms in the Turing
Machine. Well, I kind of learned about this fact some time ago and
still struggle to find a good argument against it  .
There is a simple argument against this: who wants to program in a Turing Machine? All general purpose languages may be "Turing Equivalent" but who cares? It's not about what's possible, it's about what is possible with a reasonable amount of work (e.g. Lisp+CLOS vs. Assembly language).
But the point I wanted to make is that Erlang has what Lisp has with its "return" function. It just has a different name. It's interesting how changing the name of the feature makes people hesitate to use it (it probably makes me use it less). It's like that trick in school where they teacher told you to write your best friends name on a piece of paper and then try to stomp on it.
bkl wrote:
More seriously, for 2, I have seen some 500+ lines of functions with
nested if, loop (and returns  , which made me want to "throw" up (no
pun intended  . But on the other hand, I have seen programs with
20-some-line functions/predicates/patterns all over the place; by the
time you use up all your hand/mouse/keyboard-eye coordinate power
plus matching all the calling parameters with their caller's local
values among these short program pieces, your mind is already gone crazy.
20 lines? Time to refactor that code!
bkl wrote:
My point is that, like many things in life, lots of the "rules" are
more of guidelines (nicely put by Cap. Barbossa in the "Pirates of the
Caribbean": "the code is more what you'd call 'guidelines' than actual
rules"). Situation dictates, and there are times when a sequential set
of steps with multiple 1-level if/case + return is way more readable
than "other mechanisms" in Erlang.
Ah, but your return code wasn't more readable then the alternative. I found it less so then the alternative because there were so many "nop" statements.
In the case you presented, the best solution IMO is refactoring to smaller functions. If the situation really truly calls for an early termination of a statement then it must be exceptional.
In C/C++/etc. this wouldn't be the case, but those are very different approaches that differentiate between statement and expression. And those are pretty "close to the metal" languages (that is, "return"ing to the calling function up the stack is a lower level concern then the more mathematical-ish "f(one) = 1." style of higher level languages).
bkl wrote:
I hope I didn't miss any of your arguments, but I think I need a more
direct or stronger argument against return...
I think the fact that the company who created it have used it over a decade and never thought to add return is indeed quite strong. It shows that they must be programming different then you are currently. Otherwise they would have felt the need by now.
bkl wrote:
P.S. I hope this is not another religious war in brewing.
Not at all. Just discussion among peers IMO.
bkl wrote:
But, I
personally find it interesting when I heard statement like "Lisp is
not a functional language". I can live with "Lisp is not a functional
ENOUGH language" (see my reply to dsmith on Sep 11).
Lisp isn't a functional language. People make the assertion that it is all the time on #Lisp, comp.lang.lisp and so on and the Lisp masters always shoot it down. Lisp is multi-paradym. Lisp does use many functional constructs (as does Smalltalk) because they are powerful techniques, but it also has: (1) the most powerful OO system I have ever seen, (2) a rather powerful method for modifying variables, (3) a powerful system for global variables, etc.
bkl wrote:
If the
implication is that Erlang is one of THE functional languages, like
there is a clear-cut, black-and-white line in the sand of what's pure
functional and what's not.
Erlang is a functional language because it isn't anything else. OO is not supported (at least not in the usual way), there is no loop primitives, normal variables are write once. It's not "pure", but if you don't call it functional what would you call it?
bkl wrote:
My point is, like the primeval DNA from Von Neumann at work, (1) the
concept of "state" in practical programming is unavoidable, and (2) for
practical applications you have to introduce some
"non-theroticall-functional" constructs into the language. We have
come a very long way since the evolution from assembly -> C -> ...->
Java -> ... -> Erlang, Haskel... Along the way, we deviced things like compiler
hints, ingenious programming paradigms, even higher level mathematical
constructs (e.g. monad) to manage these uncivilized-but-necessary-evils
in a language design, but you just cannot totally remove them.
I don't disagree that state is needed. But if you mean "but you just cannot totally remove return" then I disagree. First they didn't remove the ability to terminate an expression early they just changed the name, and second they don't have a return keyword and everything is working fine.
I have not seen any argument yet for the need of return other then "I'm used to it". But adding features because people are "used to" them is the worst thing one can do. If you keep doing this then you just end up with the language they had before.
bkl wrote:
IMHO, lambda calclus is THE only functional programming language I
knew. It can be used to build applications like hand-calculator (as a
homework assignment some decades ago  . But, with all the beauty and
theoretical correctness, I am still struggling to build a Web Server
or RDBMS using it...
my 1.5 cents
I'm not a "types fanboy" or a "functional fanboy". I just find it is most productive to learn how a language works best and use it that way. Everytime I find myself saying "man if I just had the feature I have in this other language" then I know one of two things has happened: Either (1) I'm using a weak language (e.g. C/C++/C#) or (2) I'm not far enough with my mastery of the new language. |
|
|
| Back to top |
|
| bkl |
Posted: Sun Sep 21, 2008 9:04 pm |
|
|
|
User
Joined: 17 Aug 2008
Posts: 19
|
I don't think the "name" (i.e. "throw") is the only thing that I have
reservations about... "Return" does not require a "catch" to "catch"
the result: i.e. if I forget to have a "catch" in the block, the
"throw"-ed result will pass all the way up and crash my program...
Quote: All general purpose languages may be "Turing Equivalent" but who
cares? It's not about what's possible, it's about what is possible
with a reasonable amount of work
Well, that's exactly the point I want to make advocating that a
"return" in some situation is much more "natural", even though it can
be replaced by other un-natural (at least to me) mechanism in
Erlang.
Quote: 20 lines? Time to refactor that code! That's exactly what I have
been doing, because "return" is not supported (grunt softly ...
Quote: Ah, but your return code wasn't more readable then the alternative. I
found it less so then the alternative because there were so many "nop"
statements.
Well, don't get me started with the "if"; it is another big complain I
have. See http://damienkatz.net/2008/03/what_sucks_abou.html, I happen
to agree with most of the argument in his blog about Erlang syntax.
Quote: "Not at all. Just discussion among peers IMO." Nice to meet some one
with an open mind. Maybe a bit about what I am trying to do...
I have been trying to build a distributed RDBMS system using
Pyhton. 6,000 lines down the road and almost finished the underlying
messaging framework, I was seriously took a look at Erlang for the
first time. What I realized it was that all I have been doing is
implementing a mini-Erlang . I immediately switch to Erlang, and
never looked back. Over the last 2 month, I learned quit a lot about
Erlang, and making all the connections with other langagues/projects I
hvae participated (e.g. "prolog-like" OODB language I designed ,
and amazed how much Erlang has brought to the table.
However, I must say, comparing with Python, programming (I mean the
act of writing and editing the actual statements, refactoring old
codes..) in Erlang takes much more "unnecessary" burden: just trying
to match all the "," ";" "." "end" and "#record.xyz"
"#record{x=#childrecord{y=..." will drive you nuts. IMHO, Python has
the best syntax design of all the languages I know; no matching "{",
"}" or "BEGIN" "END", just tab all the way in Emacs and you are done
with any copy-and-paste refactoring. But, I never looked back, because
for my application Erlang offered much more somewhere else.
For our discussion, thanks for all your replies. I learned quite a lot
from our discussion. Maybe we will have to "agree to disagree"
. Only time will tell, maybe couple months down the road, I will
finally see the light of not using return and the "it's good for you"
part. In the mean time, code on in Erlang with no return (no pun
intended ... |
|
|
| Back to top |
|
| dsmith |
Posted: Mon Sep 22, 2008 5:31 pm |
|
|
|
User
Joined: 08 Aug 2007
Posts: 41
Location: Toronto
|
There are some points in this blog I agree with, but his criticism of the "if" I don't. (I suppose my biggest problem with "if" is that it exists at all.)
The "if" expressions branches on the evaluation of a guard expression. The limitations of the guard expression ensure that the guard will evaluate safely. As Katz suggests the "case" expression is a satisfactory alternative in most cases where guard expressions are too limiting.
For both "case" and "if", one of the branches must match or an exception is thrown. Again, "if" and "case" are expressions and must evaluate to something. If we do not throw, there would need to be some implied default valuation for these expression. Presumably they would default to an atom like 'false' or 'nil'. I think this type of implied valuation reduces transparency in the code and makes it less readable.
I find I use "if" expressions very rarely, preferring to factor them into functions as jbj does. |
|
|
| Back to top |
|
| rvirding |
Posted: Thu Sep 25, 2008 12:16 am |
|
|
|
User
Joined: 30 Aug 2006
Posts: 452
Location: Stockholm, Sweden
|
I am getting into this discussion a bit late and I see that the topic has wandered a bit. I would like, however, to make one comment about the original subject.
throw is a non-local return. There is one difference with many other languages in that there is no implicit place to which you throw, you have to explicitly catch it somewhere or else an error is generated. This was done on purpose.
catch is unfortunately a bit of a mess (more than just syntactically) in that it catches both throw and errors and in such a way you can't differentiate them. You can throw or return {'EXIT',...} and catch will not tell you this. This was also done on purpose. You can do way cool stuff with this but it might not be very clear what you are doing.
try is much cleaner in that you can easily separate errors, throws and normal returns and also specify which of each you wish to catch and which will be passed on. So nested trys are ok as long as you have disjoint throw tags.
So I would say that you use throw/catch/try for non-local returns but with all the caveats you have for non-local returns.
My comment on Damien Katz's comments on Erlang is that if you look at it most of it boils down to that Erlang syntax is different from what he was used to and he doesn't like that. Apart from a few small warts Erlang syntax is actually very consistent and regular and quite small. I honestly spend less time per year handling the problems he mentioned than I spent reading his article. And, no, Erlang is not OO so why should it be organised the same as OO stuff.
I find that with most of the complaints I have read about Erlang syntax that if you really study them you find that people are basically complaining that it's different. Which is really not surprising seeing it is a different language. Duh!
'If' was a bit joke added on the spur of the moment, case is what is often better. As everything is an expression we felt it was better to always explicitly return a value rather than have implicit return values from say case and if. Also as Erlang explicitly handles errors it *is* sensible to generate errors for cases which are not handled.
Sorry for the long comment here. |
|
|
| Back to top |
|
| jbj |
Posted: Fri Sep 26, 2008 6:37 pm |
|
|
|
Joined: 16 Sep 2008
Posts: 4
|
bkl wrote: I don't think the "name" (i.e. "throw") is the only thing that I have
reservations about... "Return" does not require a "catch" to "catch"
the result: i.e. if I forget to have a "catch" in the block, the
"throw"-ed result will pass all the way up and crash my program...
Right, but Lisp return works like this as well. defun is a macro that turns your function body into a lambda and assigns this lambda to the symbol of the name you gave it. So if you tried to do this by hand you might do:
Code:
(setf (symbol-function 'my-fun)
(lambda (param)
(format t "hi~%")
(return 0)))
(my-fun)
But the return will crash the program because it is actually a non-local return. The reason this works in Lisp is because the lambda is actually constructed inside a nil block, which is what return looks for.
But anyway, I'm not advocating you use throw, I would recommend refactoring into smaller functions. This helps in more ways then just dealing with the lack of return. Having smaller, better factored functions means that often new functions are just compositions of existing ones.
bkl wrote:
Well, that's exactly the point I want to make advocating that a
"return" in some situation is much more "natural", even though it can
be replaced by other un-natural (at least to me) mechanism in
Erlang.
Well, I just wanted to say what I say to people who claim there is no benefit in a higher level language because all languages are "Turing equivalent".
In functional languages (even languages that are more multiparadym like Lisp) there isn't really a "return" in the sense that C, etc. have it. Haskell also has a return function but it doesn't even transfer control.
bkl wrote: That's exactly what I have
been doing, because "return" is not supported (grunt softly  ...
See! Then it is working as intended.
Well, the name is a bit confusing since when most people see it (including myself) they think in terms of if/else.
In Erlang we have "case" which evaluates one expression and then executes the branch that matches the result. "if", on the other hand, doesn't do the first step and instead evaluates each branch and executes the first one that returns true. This difference in functionality probably is needed, but "if" is just a kind of confusing name (to me) for it.
bkl wrote:
However, I must say, comparing with Python, programming (I mean the
act of writing and editing the actual statements, refactoring old
codes..) in Erlang takes much more "unnecessary" burden: just trying
to match all the "," ";" "." "end" and "#record.xyz"
"#record{x=#childrecord{y=..." will drive you nuts.
Yea, I can agree that all the various terminators are a lot to keep up with. Though I started out with mostly C++ so Erlang is much less complex then that. The record thing is something that if I'm away from Erlang for a few weeks I have to look up how to do it again.
bkl wrote:
IMHO, Python has
the best syntax design of all the languages I know;
You should try Smalltalk. Its simple message based syntax is pure elegance and beauty IMO.
bkl wrote:
For our discussion, thanks for all your replies. I learned quite a lot
from our discussion. Maybe we will have to "agree to disagree"
 . Only time will tell, maybe couple months down the road, I will
finally see the light of not using return and the "it's good for you"
part. In the mean time, code on in Erlang with no return (no pun
intended  ...
Good to hear about your experiences and I do suspect that given time with it and similar languages you will get to the point that you don't reach for return anymore.
Programming in a functional language is a big difference from everything else. I generally learn languages pretty quickly, but when I started out with OCaml it was really mind bending and took 4 or 5 times as long as everything else did.
So good luck with your adventures.  |
|
|
| Back to top |
|
|
|
All times are GMT
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum
|
|
|