
Flowcharts of programming language constructs - martinfjohansen
https://www.progsbase.com/blog/flow-charts-of-programming-language-constructs/
======
notacoward
Pretty neat. Three things that occurred to me while reading it.

(1) Makes it pretty clear why exceptions are even more of a control-flow
nightmare than goto. What a messy graph.

(2) The 'defer' diagram is a big WTF. Couldn't even figure out which box is
supposed to represent which statement in the example.

(3) I'd love to see a graph of the control flow when you throw in a few
spurious lambdas like "advanced" C++ programmers tend to do (and similar in a
few other languages). Might even make exceptions look good.

~~~
bibyte
My takeaway was the exact opposite of yours. For example the goto graph seems
very intuitive and simple. But it is much more complex to reason about
compared to exceptions. My takeaway was that simple graphs like these can't
really explain the pros and cons of high level programming constructs.

~~~
beetwenty
The tradeoff in goto-vs-exception is that a goto needs an explicit label,
while an exception allows the destination to be unnamed, constrained only by
the callstack at the site where it's raised.

That makes exceptions fall more towards the "easy-to-write, hard-to-read" side
of things; implied side-effects make your code slim in the present,
treacherous as combinatorial elements increase. With error codes you pay a
linear cost for every error, which implicitly discourages letting things get
out of hand, but adds a hard restriction on flow. With goto, because so little
is assumed, there are costs both ways: boilerplate to specify the destination,
and unconstrained possibilities for flow.

Jumping backwards is the primary sin associated with goto, since it
immediately makes the code's past and future behaviors interdependent. There
are definitely cases where exceptions feel necessary, but I believe most uses
could be replaced with a "only jump forward" restricted goto.

------
zabzonk
A brilliant explanation of why no-one uses flowcharts.

~~~
notacoward
...because if they did, they might realize that some of their favorite
structures are a hot mess. Seeing things in a new way often leads to learning,
which a good developer would welcome. Personally I can't remember the last
time I used an actual flowchart, but I do use state machines. Sometimes I'll
go through an exercise of re-casting complex logic as a state machine. Often,
that leads to a realization that making the state machine explicit would yield
far more testable and maintainable code than leaving it in its more common
if/loop/nested-function form.

~~~
AnimalMuppet
> ...because if they did, they might realize that some of their favorite
> structures are a hot mess.

They may only be a hot mess _when expressed as a flowchart_. But so what? I'm
not programming in flowcharts. If exceptions can do what I want cleanly, why
do I care if it's a mess considered as a flowchart? That's not my problem.

This argument is like saying that the library function I call is a mess of a
flowchart. Why should I care? Can I express what I need to express cleanly by
calling that function, or not?

Of all the arguments against exceptions, "it's got a mess of a flowchart" is
the absolutely least relevant one.

~~~
throwaway17_17
The flow charts in the link represent control flow. Compiler optimizations
are, in part, constrained by their ability to map and statically determine
control flow. So I think it is a grave mistake to say that something having a
‘mess of a flowchart’ is the least relevant concern about some PL construct.
The implication that a flowchart representation offers nothing to a
programmer, implies that the performance characteristics of your code does
matter to the programmer. So sure, in some application domains (although I
don’t think there are any) performance may take a backseat to performance and
efficiency, this information is useful and can very well illustrate the
pitfalls and trade offs of using certain constructs in code.

~~~
AnimalMuppet
I'm pretty much never worried about the performance of the "exception taken"
case. The compiler can be really quite inefficient there before I have any
reason to care.

There are places to worry about compiler efficiency. I don't think that this
is one of them.

------
theaeolist
Have a look at these diagrams for PL concepts, but done formally and with
execution rules:

[https://tnttodda.github.io/Spartan-
Visualiser/](https://tnttodda.github.io/Spartan-Visualiser/)

------
MadWombat
I don't know about everyone else, but to me these are confusing as fuck. In
continue/break/early return diagrams, there is no second condition, so it is
not clear when/why the dotted line logic path happens. The "no fall-through"
has so many arrows it takes some zen meditation to figure out what it means.
And the exception diagram is just amazing, does the catch block get executed
no matter whether there was an exception or not? Because there is an arrow
leading to it from the regular, no exception, path. I could go on, but it
becomes progressively more and more bizarre.

~~~
keithb-
I agree. The semantics even within this set of flowcharts is confusing: blocks
start out representing statements but by the time you get to Dependency
Injection, they represent anything (library, service, etc.). Also the dotted-
line vs solid line doesn't help. With Goto or Exception, the dotted line _is_
the process, not the alternative which is solid. Those need to be inverted
because "normal" flow of control from statement to statement is altered and
the "exceptional" flow is now active and other statements are ignored. Just
waiting for someone to say "we tried this with UML already. please just stop."

------
lwb
The point this article makes most clear is exactly to what extent C++ has the
attitude of “screw it, let’s put it in”. There are only a handful of
constructs listed that C++ doesn’t implement.

Also, TIL about “long jump”. Seems like a feature I’d never use in a million
years, but maybe it has its applications.

~~~
TFortunato
LongJmp can be used as a way to implement exceptions at a low level,
especially in languages like C, where you dont have them as a language
construct.

You call SetJmp, where you would want to put your try/catch blocks, then no
matter how deep down some call stack you go, if an errir occurs, you can
immediately jump back to where you called setjmp and handle it / report the
error or whatever, rather than having to handle and bubble up the error by at
every intermediate level, "returning" your way up the call stack.

------
0xff00ffee
Raise your hand if you at first thoroughly expected to mock this, and then
giggled at "function" (vs. subroutine), patted yourself on the back for
knowing everything up until ... ASPECT? DAFUQ IS ASPECT?

Just me? Ok, I'll show myself out.

Great way to visualize. I think Exceptions, Finally (and Promises) got a
little messy, but would make a good poster.

~~~
antpls
The actual question is : did it allow you to quickly understand the "aspect"
concept by using a common graphical representation? If yes, that's a win

~~~
0xff00ffee
No. The explanation isn't clear: why is g() inserted at one point and h()
inserted at another. This needs to be explained.

However, from what I observe, "aspects" look a lot like Common LISP Object
System (CLOS) before/after/around methods.

------
tabtab
The C-style switch/case construct is obsolete and awkward, and cleaner
alternatives exist, such as VB.net's technique. There is no need for "Break".

The following could be added to C-style languages without overlapping with the
existing syntax:

    
    
         select(a) {
            when 1,2,3 {...}
            when 4 {...}
            when 5,6 {...}
            ...
            otherwise {...}
         }
    

C# recently added a pattern-matching alternative, but it's still a bit verbose
for most needs.

~~~
nneonneo
GCC has had case ranges as an extension for a long time now:
[https://gcc.gnu.org/onlinedocs/gcc/Case-
Ranges.html](https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html)

It’s a far cry from general pattern matching (a la Rust, Scheme) but it’s
helpful in some circumstances. Otherwise, you can also stick multiple case
labels together, i.e.

    
    
        switch(x) {
            case 1 ... 4:
            case 7:
            case 9 ... 16:
                ...
            default:
                ...
        }
    

I don’t see why you need to call the C-style construct obsolete and awkward,
though. It works plenty well for enumerations, which is basically what it was
designed for. If you need to do something more fancy, if/else if chains always
work.

~~~
tabtab
Ranges are nice, but not a replacement for sets. (Allow both!) The main "fix"
I propose is to get rid of the Break statement and the very need for it. If
you have set-based lists, you don't need "fall through" behavior because you
can have more than one item per matching expression.

The Break statement is _error prone_ because it's easy to forgot. Some
languages "solved" that by making Break required; but if it's required, then
it's superfluous: code-bloating eye clutter. If you never used a language that
didn't need "Break" you may not understand how annoying it is to go back.

My suggestion above allows C-ish languages to add a modernized version and yet
keep the existing construct for backward compatibility. In other words, the
old one would be "deprecated".

~~~
int_19h
> If you have set-based lists, you don't need "fall through" behavior because
> you can have more than one item per matching expression.

This is not entirely true. Ranges and sets allow you to run the same code for
different inputs. Fallthrough allows you to run _partially_ the same code for
different inputs.

It's a rare case, so I don't think its utility warrants making fallthrough
behavior the default. But it's nice to have it explicitly, like "goto case" in
C#.

Anyway, the reason why it is the way it is in C, is because case labels are
literally _labels_ , and the switch statement itself is just a branched goto -
which is why e.g. Duff's Device is a thing. And that, in turn, is probably
because it's a descendant of "switch" in Algol-60, which was basically an
array of labels.

~~~
tabtab
> Fallthrough allows you to run partially the same code for different inputs.

True, but it's a screwy way to manage such overlaps. The volume of errors and
confusion exceeds the benefits in my opinion. There are other ways to do such.
For non-trivial conditionals (such as overlaps), if-else is often the better
tool anyhow. Case/Switch should only be used for simple mutually-exclusive
value look-ups.

------
raslah
I actually really like flowcharts, though they are somewhat inefficient to
draw out more complex structures, they are great for working out small
sections of thorny logic. The only issue I sort of take those in the article
is the physical flow is a little distracting when decisions don't branch
laterally. having them continue top to bottom sort of throws me off. perhaps
its just because i draw them with branches to the side.

------
inetknght
As someone who's self-taught everything, this is... awesome. It's exactly what
I needed to be able to up my game in describing code to management using the
flowcharts they've asked for.

~~~
zabzonk
In case you haven't gleaned it from the majority of comments here, flowcharts
are a terrible way to describe code, particularly modern, multi-threaded code
that uses exceptions. Don't go there.

~~~
inetknght
I quite agree. Unfortunately management wants single-threaded code and wants
it documented with flowcharts. So right now there's lots (!) of clouds
detailing "this calls into another API documented in another flowchart..."

~~~
AnimalMuppet
To me, that screams "put your resume out on the street".

I mean, I'm not opposed to documentation. It's important. But if your
management thinks that flowcharts are the way to do it, I wonder what
millennium they think it is.

~~~
inetknght
Indeed? Flowcharts aren't the only documentation. Swagger describes the API
parameters, flowcharts describe the API code flow, and text provides a story,
and comments in the code describe particulars.

Flowcharts are just where I've felt that I've significantly underperformed;
where management has asked for more of them.

Putting my resume on the street is something I do often. Want to look at it?

If you think flowcharts aren't the way to describe code flow, then I'm curious
what method you think is better.

~~~
AnimalMuppet
Ah. Flowcharts were all you mentioned, so I assumed (bad move) that they were
all you did.

Depending on what exactly you are doing, parts of UML _might_ be better. It
lets you describe different aspects with different diagrams, so that, if one
kind of diagram is sub-optimal for showing some aspect of what's going on,
another kind might be better.

------
dependenttypes
The break one does not make a lot of sense to me. Shouldn't they use another
rhombus and a solid line instead? Same for continue and early return. Similar
thing holds for goto.

------
edflsafoiewq
Speaking of reasoning, I believe a Hoare proof of correctness for a flowchart
is

* a choice of a precondition and postcondition, Pre(B) and Post(B), for every basic block B

* for every basic block B, a proof that {Pre(B)} B {Post(B)}

* for every arrow a : B1 -> B2, a proof that Post(B1) /\ Cond => Pre(B2) where Cond is the condition for the arrow to be taken

------
azhenley
Best article I have read in a while. I'm going to try to incorporate this into
my PL course.

------
proc0
I love this! Visually seeing computation flow is great for understanding what
the code is doing.

------
edflsafoiewq
The goto diagram contains an erroneous arrow and an extra block. It should
look like

    
    
           o
           |
           v
        +--□
        |
        |  □<-+
        |  |  |
        |  v  |
        +->□--+

------
_0ffh
Pretty decent, but covers the pre-test loop as "loop" (unqualified), omitting
the post-test loop.

------
emmanueloga_
I think flow charts are a lot more useful when the granularity is more coarse:
the boxes should represent functions, not the logic inside.

    
    
        compute thing -> predicate true ------> compute some other thing
            |         -> predicate false ->|
            \---------------loop-----------/
    

... etc ...

------
richdougherty
Pretty neat, but notably missing some of the more functional control flow
concepts: tail calls, call with current continuation, delimited continuation,
etc.

------
PacifyFish
What did the author use to make the charts?

~~~
martinfjohansen
I used "yEd live".

------
DonHopkins
The Nassi-Shneiderman diagram is one way of representing structured program
flow by dividing up a two-dimensional area. It can't represent unstructured
gotos and exceptions, though.

[https://en.wikipedia.org/wiki/Nassi%E2%80%93Shneiderman_diag...](https://en.wikipedia.org/wiki/Nassi%E2%80%93Shneiderman_diagram)

>A Nassi–Shneiderman diagram (NSD) in computer programming is a graphical
design representation for structured programming. This type of diagram was
developed in 1972 by Isaac Nassi and Ben Shneiderman who were both graduate
students at Stony Brook University. These diagrams are also called
structograms, as they show a program's structures.

>Overview: Following a top-down design, the problem at hand is reduced into
smaller and smaller subproblems, until only simple statements and control flow
constructs remain. Nassi–Shneiderman diagrams reflect this top-down
decomposition in a straightforward way, using nested boxes to represent
subproblems. Consistent with the philosophy of structured programming,
Nassi–Shneiderman diagrams have no representation for a GOTO statement.

>Nassi–Shneiderman diagrams are only rarely used for formal programming. Their
abstraction level is close to structured program code and modifications
require the whole diagram to be redrawn, but graphic editors removed that
limitation. They clarify algorithms and high-level designs, which make them
useful in teaching. They were included in Microsoft Visio and dozens of other
software tools, such as the German EasyCode.

>In Germany, Nassi–Shneiderman diagrams were standardised in 1985 as DIN
66261. They are still used in German introductions to programming, for example
Böttcher and Kneißl's introduction to C, Baeumle-Courth and Schmidt's
introduction to C and Kirch's introduction to C#.

>Nassi–Shneiderman diagrams can also be used in technical writing.

When Ben Shneiderman and Ike Nassi (who were grad students at the time)
submitted their paper about them to Communications of the ACM, it was quickly
rejected on October 4, 1972, with the most brutal rejection letter Ben
Shneiderman has ever received.

But then they submitted it to the unrefereed ACM SIGPLAN, where it was
published in August 1973, and since then it has been cited many times, widely
implemented by many tools, and used for educational and documentation
purposes. It's a great example of the importance of persistence for people
whose new ideas are brutally rejected by respected authorities:

Flowchart techniques for structured programming:

[https://dl.acm.org/doi/10.1145/953349.953350](https://dl.acm.org/doi/10.1145/953349.953350)

Scan of the brutal referee's report:

[https://www.cs.umd.edu/hcil/members/bshneiderman/nsd/rejecti...](https://www.cs.umd.edu/hcil/members/bshneiderman/nsd/rejection_letter.pdf)

>My first thought was to write a referees report which would by its sarcastic
nature be funny. For example, I thought of writing that it was a sound, useful
theory, but it wasn't practical because it would be difficult to design
flowcharting templates to be manufactured and sold.

>I guess, however, that it is best to come right out and say that I feel the
best thing the authors could do is collect all copies of this technical report
and burn them, before anybody reads them. My opinion is that it shows the
inexperience and ignorance of the authors with respect to programming in
general, and their misunderstanding of so-called structured programming.

>To say that [BEGIN body END] should be written as [NS diagram] is ridiculous.
Even more ridiculous is having to write [DO i=1 TO N; DO J=1 TO N; S = 0; DO
K=1 TO N; S=S+A(I,K)*B(K,J) END C(I,J)=S END END] as [NS diagram].

>The authors mention that "the ease with which a structured flow-chart can be
translated into a structured program is pleasantly surprising". My retort is
"yes, just erase those silly boxes!"

>Flowcharts are a crutch we have invented to try to understand programs
written in a confusing style. This was due to our ignorance of the programming
process and what was needed -- after all, programming is only 20-30 years old.
So-called "structured programming" helps to limit us to, as Dijkstra calls
them, "intellectually manageable" programs, in which case flowcharts are
completely useless and in fact a hindrance to programming. They shouldn't be
used.

>I shudder at the thought of further explorations revolving around the
context-free nature of this [flowchart] language.

Ben's rejection letter story:

[https://www.cs.umd.edu/hcil/members/bshneiderman/nsd/rejecti...](https://www.cs.umd.edu/hcil/members/bshneiderman/nsd/rejection_letter.html)

>Rejection letter from the Communications of the ACM

>Ben Shneiderman, June 12, 2003

>Our submission of the structured flowcharts to the Communications of the ACM
was quickly rejected, on October 4, 1972, by the Programming Languages Editor
David Gries of Cornell University. He included a single anonymous reference
letter which is on paper that has a Cornell University watermark. I assume
Gries gave our paper to one of his colleagues (you can play the guessing game
too), who wrote the most brutal rejection letter I have ever gotten.

>The reviewer wrote: "I feel that the best thing the authors could do is
collect all copies of this technical report and burn them, before anybody
reads them." As graduate students, this stinging rejection shocked us, but we
kept getting enthusiastic responses from people around us. We sent the paper
to the unrefereed ACM SIGPLAN Notices, where it was published in August 1973.
It didn't take long for others to produce extensions, software tools, and
applications of structured flowcharts.

>The next problem was theft of the idea. I had sent a draft to respected
colleagues, and soon others published slight variations. One of these
respected colleagues was Ned Chapin, who greatly troubled us by publishing
what he called 'Chapin Charts.' A friend of mine sent me his published paper
with a note encouraging me to sue. For several years I feared that Chapin's
reputation and his frequent professional seminars would wind up leaving the
idea tied to his name, but as the decades passed, the ending has proved to be
a happy one. We called the idea 'structured flowcharts, but they are widely
known as Nassi-Shneiderman Diagrams.

>Another problem was the appearances of patents for variations on our idea,
but these have not limited the widespread recognition we have gotten over the
years.

>I wish every graduate student or young inventor would have the pleasure of
seeing his/her ideas spread so far and influence the work of so many people. I
also hope that the story of the bold rejection of our novel idea and its
eventual international success, is an inspiration for anyone whose new ideas
are rejected by some respected authorities.

More on Ben Shneiderman and NS diagrams:

[https://news.ycombinator.com/item?id=11368430](https://news.ycombinator.com/item?id=11368430)

[https://news.ycombinator.com/item?id=11370099](https://news.ycombinator.com/item?id=11370099)

[https://news.ycombinator.com/item?id=22093072](https://news.ycombinator.com/item?id=22093072)

------
Supermancho
This is a wonderful idea, but by the given numbering scheme, I can identify a
slew of problems.

[1a. If] _Note This is the first time we see synchronous calls, decisioning,
and the representation makes no specific callout to the structure. The
structure IS the case.

[4. Break] _Inconsistency _Confusion This is the first time we see a specific
part of the flow called out as the structure (it isn 't a structure). This is
flow control, not a construct. This confusion plagues the rest of the
document.

[7. Switch (No Fall-Through)] _Inconsistency _Confusion This is the same as If
/elseif.../else/, but the post goes with the oddly specific "switch". Would be
more appropriate as [1c. if/elseif.../else] and a [1d. Switch] - The different
terminology if/switch does not change the fact they are the same structure.
Grouping Switch-type by name is a bad choice.

[9. Local Goto] _Omission The Goto example has no exit node.

[11. Exceptions] _Mistake The exception travels the stack, meaning (from the
perspective of the original parent function), from grandchild to the child, to
the parent catch.

[12. Finally] _Omission _Confusion Finally should be it 's own block, since it
is treated as such and has an implicit catch mechanism, even if it's not
always required in the syntax.

[13. Blocking, Synchronous Calls] _Confusion This is rather pointless. All the
flows, prior, were blocking. Now introducing another metaphor that changes
past examples is inconsistent. Blocks which are synchronous (blocking) are
represented by 2 different forms. It's unnecessary.

[14a. Non-Blocking, Asynchronous Call, Callback] _Inconsistency_ Confusion
There is a whole function called, which should be represented by a starting
and ending node.

[14b. Device Input] _Inconsistency_ Confusion This isn't even a different
construct. Event handling is what this is and is poorly represented, in the
same way as 14a.

[15. Framework] _Mistake_ Confusion This isn't a construct, nor is it an
accurate representation. Frameworks are a collection of idioms, which usually
has little to do with constructs (as defined in the beginning). It feels like
the author has gone off the rails here.

[18 Come-from & 19 Aspects & 21 Destructor] _Confusion? The size of the block
is supposed to indicate a specific syntax, which is inappropriate for
describing logical constructs. They should be full sized and optionally
labeled blocks, as they are evaluated.

[20 Dependency Injection] _Confusion This is another "timing adds a new way to
represent things" issue. There's no difference between an Aspect and DI,
logically.

[22. Defer] *Confusion Adding data (even a function) to a separate stack,
which is called later, does not make the diagram presented.

All in all, this needs work. Even the grouping description at the bottom does
nothing to help make this more useful.

