Why imperative code is worse than functional code

Logic gates:

  • students can be introduced to the idea of encoding whether something is true or false using a simple switch and light (or LED).

  • then students can be introduced to those “tried and tested” basic combinations of switches: AND, OR, NOT.

  • at the point when the complexity of wiring diagrams and physical components everywhere has students frustrated…introduce the basics of Boolean algebra!

But I am definitely not an educator: I will defer to their opinion on an approach like this.

What point are you trying to make?

A programming language is not enough for a specification for what it means for a program to be correct.

Imperative language do have operational semantics and I can still write a sorting program and pull my hair out trying to understand and reason about it or even trying to prove it correct according to what it means for sorting to be correct. If I struggle so much to prove something like sorting how can I begin to understand how I can optimize without breaking correctness?

The operational semantics of an imperative language is its downfall.

…actually, it was an attempt at irony - namely that your comment about specification strongly resembles a paragraph from a textbook written 30 years ago. But your response does seem to answer my enquiry regarding:

some theoretical “advance” or “breakthrough” which I’m not aware of.

…there hasn’t.


The operational semantics of an imperative language is its downfall.

…did you mean “denotational semantics” ? I’ve seen a few operational semantics for (small pedagogical) functional languages and they can get quite intricate rather quickly as more “practical” details are included.

Oh okay :grin:

I said operational because that’s usually the case, there aren’t many imperative languages with a denotational semantics

Is there any evidence to back up this conjecture?

1 Like

No and there is even a easy counter example. The annotating session lesson problem refered in this thread.
Unlike any of proposed alternative, the Python solution is trivial to check for correctness.

But when thjngs grow more complex I believe this conjecture holds.

But the Python solution isn’t even correct.

bad = [{"name": "Addition / Subtraction"}, {"name": "Multiplication / Division"}]
sections = [
    {
        "title": "Getting started",
        "reset_lesson_position": False,
        "lessons": [
            {"name": "Welcome"},
            {"name": "Installation"},
        ],
    },
    {
        "title": "Basic operator",
        "reset_lesson_position": False,
        "lessons": bad,
    },
    {
        "title": "Advanced topics",
        "reset_lesson_position": True,
        "lessons": bad,
    },
]
section_counter = 1
lesson_counter = 1

for section in sections:
    if section["reset_lesson_position"]:
        lesson_counter = 1

    section["position"] = section_counter
    section_counter += 1

    for lesson in section["lessons"]:
        lesson["position"] = lesson_counter
        lesson_counter += 1

print(sections)

Will give the lessons in the second section the wrong number

3 Likes

Ah, clever example! (And to save others the investigation, it’s because the same mutable dictionary is iterated over, and modified, twice).

I didn’t say it was correct, only it was easy to check. You prove my point :wink:

2 Likes

I think this is main argument in favor of why some good OOP practices (like encapsulations) are not universal principles but only there to solve some problems due to mutability and therefore less relevant for languages where things are immutable by default.

Great counter-example! Also quite plausible that you’d have a basic coverage over some sequence of topics, then Advanced coverage over exactly the same sequence. For each Advanced area you might start "Recap", "Installation", ... to load some extra packages.

(I was going to ask back on the O.P. with its talk of “key-value” whether the key was ‘lesson name’ or the combo ‘section name+lesson name’.)

This is a bit disingenuous because the Python solution works on a Json input and if Json as been parsed from a file (which I am pretty confident that it is what’s happening in the real project). There is no way in the Json format to describe to reuse and share data.

Would you say the Haskell version does not work if it was accidentally given a cyclic list?

It does work! It would return a lazily-consumable infinite list :slight_smile: EDIT: although on trying it I discover that infinite input seems to require the lazy State monad :frowning:

1 Like

There are a lot of Haskell versions, so I’d need to know which one you’re referring to, but I think a lot of the solutions would give an acceptable answer, especially if what you’re doing is serialising to something like JSON and streaming it.

The original problem is meant to work on a list of sections, where a section is a key-value data structure containing a list of lessons, where a lesson is also a key-value data structure. Nowhere in the problem statement is JSON mentioned, except for

Here is an example input (formatted in JSON for convenience):

and

The output should be (formatted in JSON for convenience):

I will agree that the code as given will probably work for whatever the intended use-case was, but “containing two references to the same object in a collection being traversed while performing mutation” is not some really obscure edge case, it’s something that occurs all the time.

2 Likes

I agree (thus the “a bit” at the begiining of my comment).

…and all of that is “a bit” of a:

Why? Because the questionable use of simple(ton) examples has probably occurred before:

  • no doubt there were various snippets of assembly code which purported to show the superiority of assembly over that “new-fangled” Fortran and LISP stuff.

  • there probably were analogous snippets of code showing the superiority of goto over structured programming.

  • and one can only imagine all the snippets of code purporting to show the superiority of manual memory manglement management over GC!

They are all examples of “storms in teacups”…the teacups found in child’s-toy dollhouses!


Getting back to the actual topic of this thread:

Mutation.

While The Anatomy of Programming Languages also nominates sequencing as being a problem:

  • arbitrary sequencing but no mutation: confluence.

  • arbitrary mutation but no sequencing: nondeterminism!

Standard ML and OCaml both illustrate this point - at first glance, definitions in both languages have little to no resemblance to the rambling sequences of statements found in the likes of Pascal or C. But that lack of visible sequencing is irrelevant: due to the relative ease of access to mutation both provide, Standard ML and OCaml are also imperative languages.


Alright, so what about Haskell and its use of seemingly-inexplicable interfaces?

…for something better (or a proof that none exists, analogous to that for the Entscheidungsproblem).

2 Likes

This is one of my favorite things about functional code. In imperative code, in order to understand the value inside a mutable variable, you must read its entire scope. In functional code, a value’s expression is all you need to understand it. You can navigate to its building blocks’ definitions with ease thanks to LSP too, though I wish that the Haskell language server had a way to also navigate to libraries’ definitions as well.

I don’t use LSP but I use vim and haskdogs which generate tags for all you dependencies, it’s amazing. Also using stack hoogle --server launch a server showing for all libraries (and your own) the doc but more importantly the annotated code (with hyperlink to definition). You can also define quite easly a mapping in your text editor to a web browser to the hoogle definiton of the word under the cursor.
(I don’t know if there is a cabal equivalent).

1 Like

I also use vim and I’ll try haskdogs, it seems like exactly what I need, thanks!

Perhaps the more modern question is not “imperative versus functional”, but formal syntactic code languages (imperative, functional, logical, …) versus machine generated computations (ML, GPT, etc). If one can give plain English descriptions, some AWS keys, and get back “a system”, the languages and transfer formats in-between may lose relevance. That’s if you take the MIT view, anyway. (See Sussman on why they switched from Lisp to Python.)