![](/i/fill.gif) |
![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
There's really one primary problem with early returns, and that's handling
things that ought be handled before the function returns, like deallocating
memory, closing files, logging, etc. In a decent language like C++, there's
invisible bits of code stuck into your object code before each return (or,
alternately, the compiler rewrites the return statement to be a goto to the
end of the function). Without that, it requires somewhat more code to return
early *and* clean up, to the point where skipping over blocks with flags
might be more clear and maintainable than duplicating clean-up code.
Especially if you want to DRY on the cleanup code.
Someone way back proved you could turn *any* control flow into structured
flow, with enough booleans and repeated code. That doesn't mean it's a good
idea.
And of course, this is all local to a fairly small function. Think about a
program with significant amounts of control flow using setjmp/longjmp, or
where gotos can go to any label anywhere in a program, and you get the idea
of what Dr D was arguing against.
It turns out that what's really formally the problem is more the labels than
the gotos. The problem is if you look at the preconditions of the label,
it's an amalgamation of all the postconditions of every goto that goes to
that label. You can't look at the line before the label and the line after
the label and know *anything* about any of the variables or program state
when you reach the line after the label, unless you hunt down and analyze
every goto as well.
> Likewise no "fall-through" in switch-case blocks in languages like C
I like how C# handles this. Each branch has to end with either a break or a
goto X, where X is one of the labels of the switch. So you can rearrange the
order of the branches, insert branches in the middle, etc, and not break
anything. And you can't accidentally fall through to the next case and bring
down the entire east coast telecommunication infrastructure. ;-)
> The more such error situations there could be, the more indentation the
> code would require which is not only inconvenient but also IMO makes the
> code less readable.
I find another way of doing this also works. My own invention:
bool worked = true;
if (worked) worked = try_thing_one();
if (worked) { reset_flobulator(); worked = query_flobulator(); }
if (worked) worked = yidda < 27;
clean_up_temps();
return worked;
and so on. Each block of work can be rearranged, nothing runs and it falls
out very quickly on failure, everything inline, everything's indented only
one, yet it's pretty easy to follow what's going on. And of course if you
want specific error codes, that works just as easily.
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
nemesis wrote:
> Darren New <dne### [at] san rr com> wrote:
>> andrel wrote:
>>> at least three or four different languages.
>> Noting that C, C++, C#, Java, and Pascal are all "the same language" for
>> these purposes. ;-)
>
> they are all Algol.
Basically, yes. Altho I'd say OO, and maybe some of the stuff in Ada are
things you should know about algol-like languages.
You should also learn APL, LISP, Prolog, Erlang, Eiffel, and some machine code.
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> nemesis wrote:
> > Darren New <dne### [at] san rr com> wrote:
> >> andrel wrote:
> >>> at least three or four different languages.
> >> Noting that C, C++, C#, Java, and Pascal are all "the same language" for
> >> these purposes. ;-)
> >
> > they are all Algol.
>
> Basically, yes. Altho I'd say OO, and maybe some of the stuff in Ada are
> things you should know about algol-like languages.
>
> You should also learn APL, LISP, Prolog, Erlang, Eiffel, and some machine code.
I'd put Perl and Haskell into the mix too.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
nemesis wrote:
> Darren New <dne### [at] san rr com> wrote:
>> nemesis wrote:
>>> Darren New <dne### [at] san rr com> wrote:
>>>> andrel wrote:
>>>>> at least three or four different languages.
>>>> Noting that C, C++, C#, Java, and Pascal are all "the same language" for
>>>> these purposes. ;-)
>>> they are all Algol.
>> Basically, yes. Altho I'd say OO, and maybe some of the stuff in Ada are
>> things you should know about algol-like languages.
>>
>> You should also learn APL, LISP, Prolog, Erlang, Eiffel, and some machine code.
>
> I'd put Perl and Haskell into the mix too.
Yeah, I was trying to remember some of the others. Something perl-like,
certanily (python, Perl, bash, Tcl, etc). FORTH. Haskell or some other
very functional language, for sure.
The thing about APL, LISP, FORTH, and Tcl is they're all simple enough that
once you have a few under your belt, you can learn them well enough to
understand the principles rather easily, even if it's not obvious the best
way to write large programs. Of course, writing large programs is where
having the alternate paradigms can shine (or collapse), so that might not be
a recommendation there.
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
On 10/15/2010 7:51 PM, nemesis wrote:
> Orchid XP v8<voi### [at] dev null> wrote:
>> Oh wow. 42 years later, it still exists:
>>
>> http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF
>>
>> "Go to considered harmful."
>
> a classic! I'm glad you've been interested in the evolution of programming
> formalisms -- and all by yourself! Next on, the lambda papers! ;)
>
>> I also note, with some interest, what looks suspiciously like Haskell
>> syntax, in a letter typed 42 years ago. Obviously it's not after
>> Haskell, but rather whatever mathematical formalism Haskell borrowed the
>> notation from. Still, interesting none the less...)
>
> bwahahahaha
>
> you must be talking about `conditional expressions as introduced by J.McCarthy
> ("B1 -> E1, B2 -> E2,....., Bn -> En")'... although it looks like currying
> haskell notation, it's actually what it's told: John McCarthy's conditional
> expressions for Lisp!
>
> (cond
> ((null? ls) 0)
> ((zero? (car ls)) 0)
> (#t (/ n (car ls))))
>
> cond was blowderized and adapted to all other programming languages in the
> simplified form if-then-else... even Lisp provides it as well as cond!
>
>
Snort.. While the above is slightly annoying (though only due to the
fact that I prefer a syntax which is marginally clearer, if only just..,
claiming that if-then-else is simpler is just flat wrong. Lot of extra
typing to get you where you are going, when something closer to the
original, like one languages 'switch' statement makes a lot more sense.
But, it only gets worse when some joker does what was done in a few
versions of basic (Apple Basic being one of them), and lost the else, or
denied use of more than one else, by disallowing 'else if', etc.
Definitely times when I seriously *hate* if-then-else, even if the
result is, theoretically, the same.
--
void main () {
if version = "Vista" {
call slow_by_half();
call DRM_everything();
}
call functional_code();
}
else
call crash_windows();
}
<A HREF='http://www.daz3d.com/index.php?refid=16130551'>Get 3D Models,
3D Content, and 3D Software at DAZ3D!</A>
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> There's really one primary problem with early returns, and that's handling
> things that ought be handled before the function returns, like deallocating
> memory, closing files, logging, etc. In a decent language like C++, there's
> invisible bits of code stuck into your object code before each return (or,
> alternately, the compiler rewrites the return statement to be a goto to the
> end of the function). Without that, it requires somewhat more code to return
> early *and* clean up, to the point where skipping over blocks with flags
> might be more clear and maintainable than duplicating clean-up code.
> Especially if you want to DRY on the cleanup code.
Maybe the fact that C++ basically removed the need for manual cleanup of
resources (if you use its constructs properly) made it much safer and
cleaner to use early returns. Early returns would indeed be a hazard in C,
where there's no automatic scope-based cleanup mechanism.
It's not just memory. It's anything that needs to be released after use,
such as for example file handles. In C++ it's completely safe to do things
like:
void foo()
{
std::ifstram is("file.txt");
do_something;
if(error) return;
do_something_else;
if(another_error) return;
do_more;
}
There's no need to clean up 'is' (ie. close the file handle) because it
will clean up itself when its scope ends. This is not possible in C (where
you always have to manually make sure it gets closed when the function is
exited).
(This is actually important because in C++ the function might be exited
at surprising places, even without explicit early returns, namely if
something throws an exception. The above code is exception-safe because
the file will be closed even if anything inside the function unexpectedly
throws.)
If you see something like this in C++:
void foo()
{
FILE* infile = std::fopen("file.txt", "r");
then you should immediately see "DANGER! DANGER!", because whatever follows
that will *not* be exception-safe (well, not unless the very next line is
'try').
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
On 16-10-2010 4:51, nemesis wrote:
> Orchid XP v8<voi### [at] dev null> wrote:
>> Oh wow. 42 years later, it still exists:
>>
>> http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF
>>
>> "Go to considered harmful."
>
> a classic! I'm glad you've been interested in the evolution of programming
> formalisms -- and all by yourself! Next on, the lambda papers! ;)
>
>> I also note, with some interest, what looks suspiciously like Haskell
>> syntax, in a letter typed 42 years ago. Obviously it's not after
>> Haskell, but rather whatever mathematical formalism Haskell borrowed the
>> notation from. Still, interesting none the less...)
>
> bwahahahaha
>
> you must be talking about `conditional expressions as introduced by J.McCarthy
> ("B1 -> E1, B2 -> E2,....., Bn -> En")'... although it looks like currying
> haskell notation, it's actually what it's told: John McCarthy's conditional
> expressions for Lisp!
>
> (cond
> ((null? ls) 0)
> ((zero? (car ls)) 0)
> (#t (/ n (car ls))))
>
> cond was blowderized and adapted to all other programming languages in the
> simplified form if-then-else... even Lisp provides it as well as cond!
Dijkstra's guarded commands does not have a if-then-else, I know it is a
rather obscure language, but not in this context.
I find the if-then-else case interesting because it shows so clearly
that there was a trade off between having a language that has some
relation to maths and one that is understandable for below average IQ
humans*. Dijstra's version, the McCarthy version he mentions here and
the LISP version all are homologues of the disjuction. If-then-else is
more like human reasoning using a decision tree. I am not surprised that
the latter is the more common now, but I don't think I am happy with it.
*) there is also that if-then-else is more close to the conditional jump
of most processors at the machine level, but mentioning that might spoil
my point.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp <war### [at] tag povray org> wrote:
> If you see something like this in C++:
> void foo()
> {
> FILE* infile = std::fopen("file.txt", "r");
> then you should immediately see "DANGER! DANGER!", because whatever follows
> that will *not* be exception-safe (well, not unless the very next line is
> 'try').
By the way, I honestly wonder what's the "proper" way of doing that
in Java, given that Java has exceptions and no scope-bound lifetime of
objects. Do you always have to follow file handle creation with a 'try'
block to guard against exceptions? Or is there some other trick to make
such code exception-safe?
(I understand that in C# this is handled with a 'using' block, which
automatically and immediately disposes of objects when the block is
exited. Is that the proper way of making file handles exception-safe
there?)
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Patrick Elliott wrote:
> But, it only gets worse when some joker does what was done in a few
> versions of basic
Roll back to when BASIC actually was edited line by line, with line numbers
and all that, and the only form of the IF statement was
300 IF Z < W THEN 500 ELSE 800
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Maybe the fact that C++ basically removed the need for manual cleanup of
> resources (if you use its constructs properly) made it much safer and
> cleaner to use early returns.
Exactly the point I'm making. In C++, "return" doesn't mean return from the
function any more. It means basically "branch to all the clean-up code for
anything allocated in any of the scopes I'm nested in." :-)
> If you see something like this in C++:
Yep. Rather than "checked and unchecked exceptions", I think Java (and C#)
would have been better off issuing warnings or errors for potential leaks of
disposable items.
If you have a file handle in C#, you're supposed to put it inside a "using"
so it gets closed if you exit the scope.
using (FileHandle handle = File.Open(...))
{
handle.read(); handle.write(); ...
}
Any time you exit the block after you've created the file handle, it'll get
cleaned up (in the sense that the Dispose call will be made). Kind of
poor-man's C++ destructors. If they were going to do that, they should have
put in some mechanism to keep you from accidentally making a handle (or
anything else that inherits Disposable) without clearly disposing it.
Sing# does something much like this by basically making it so that kind of
object can't get assigned to more than one variable at once (i.e., you can't
have two pointers/references to the same value), and it tracks and enforces
this at compile time. So "File.Open" would declare it returns a new file
handle, "File.Close" would declare it consumes the handle, and everything in
between would be unable to overwrite it or copy it or whatever (outside of,
of course, passing it to subroutines, etc).
The destructor semantics is one of the things I think C++ really did get
just right. It's really all the C compatibility that makes it awkward, I find.
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |