News for hackers. Stuff that matters.
Re:Out of the Tar Pit (2006) [pdf] by feoren on Tuesday February 28, 2023 @11:55AM (#34971617)
Plenty of state -- even mutable state -- is essential complexity. Think of an IDE where a user is typing out code: the state is changing all the time. Pure functional programming has a hard time walking the line between "avoiding" mutable state, and "ignoring" mutable state. If you insist on functional purity, you're already admitting defeat in the face of essential mutable state. The walls of functional (and especially reactive) programming are too high -- they currently don't play very well with their Turing and von Neumann neighbors.

Functional programming is only "mathematically pure" because we already have math for it. You can make mutating state "mathematically pure" too, if you just invent new math for it. For example, you can pair state with a "generation" variable and define mutating functions as returning the new state with generation + 1; now all your functions are pure again. That's not all it takes, and it's not easy! But it shows how narrow-minded the current idea of "purity" is.

Re:Out of the Tar Pit (2006) [pdf] by kccqzy on Tuesday February 28, 2023 @12:11PM (#34971818)
There is no essential mutable state in computer science. All essential mutable state can be modeled away to use no mutable state, as you have shown in the generation number idea which is one valid way. (I'm strictly talking about computer science problems not computer engineering problems such as drivers.)

The generation number idea you have shown is an excellent idea. It immediately enables several new capabilities compared with just mutation:

* You are now able to determine which data is older and which is newer without resorting to an external clock.

* If your mutation takes a long time, there is no longer a need to use a long-held mutex to ensure thread safety which would prevent concurrent reads; you can create a new generation separately and then atomically CAS it. Writers don't block readers.

* If you suddenly need undo/redo functionality, it's suddenly trivial. Or diff'ing between versions. Or understanding the reason for changes (basics of auditing).

Re:Out of the Tar Pit (2006) [pdf] by kilgnad on Tuesday February 28, 2023 @12:37PM (#34972227)
Lambda calculus machines can't exist in reality though. Only the von Neumann machine can be actualized.

Thus from a practical perspective the foundations of actual computing must be built on mutable state.

You are wrong about the generations idea. It's a side effect. Functions themselves cannot actually do anything with a generation number. It's completely useless other then for letting you know how many times a value has changed.

A generation number also doesn't negate the need for mutexes. The system is still operating on shared state, a generation number doesn't change this.

Re:Out of the Tar Pit (2006) [pdf] by kccqzy on Tuesday February 28, 2023 @01:35PM (#34973000)
You are not getting the point of either my comment or the original article. You are thinking from an engineering point of view, where if you look through all the layers of abstraction you see mutexes and shared mutable memory. That's not my point; my point is about building up those abstractions so they are sufficiently hidden. Functional programmers routinely operate on a higher level of abstraction, and they have shorter, more maintainable code. In such code, it would be a code smell if you continue to use mutexes, because that should've been an implementation detail. It should've been clear from the outset that the kind of complexity the article is talking about is cognitive complexity.
Re:Out of the Tar Pit (2006) [pdf] by kilgnad on Tuesday February 28, 2023 @10:36PM (#34978469)
I am getting it. I am simply saying that you can't completely hide those details. They leak through.
Re:Out of the Tar Pit (2006) [pdf] by enlyth on Tuesday February 28, 2023 @12:59PM (#34972536)
> There is no essential mutable state in computer science.

Yes, theoretically. Now imagine your mutable state is 2GB in size, have fun creating a copy of it on every change.

Re:Out of the Tar Pit (2006) [pdf] by kccqzy on Tuesday February 28, 2023 @01:41PM (#34973097)
You are not getting the point of my comment or the original article. It's clear to me that the kind of complexity being talked about in the article is cognitive complexity not computational complexity. It's not about choosing between an O(n) copying of data and O(1) in-place mutation; it's about choosing code that's easy to comprehend and maintain.
Re:Out of the Tar Pit (2006) [pdf] by deathanatos on Tuesday February 28, 2023 @02:49PM (#34973902)
Their point is that you can't choose the cognitively simpler choice because of real world design constraints, such as not consuming all available RAM — i.e., because of essential complexity.
Re:Out of the Tar Pit (2006) [pdf] by kragen on Thursday March 02, 2023 @12:42PM (#34999022)
that's complexity introduced by your approach to the solution (using an inadequately large computer) rather than essential to the problem you're trying to solve
Re:Out of the Tar Pit (2006) [pdf] by akkartik on Thursday March 02, 2023 @01:43PM (#34999912)
I think the crux is which side gets to be called "essential". Is it the abstract problem the code is intended for, or is it the real world we're living in. Mike Acton argues for the latter (https://www.youtube.com/watch?v=rX0ItVEVjHc)

And all this is heat rather than light because there is absolutely a significant fraction of concerns that both sides would agree are utterly incidental/accidental complexity.

Re:Out of the Tar Pit (2006) [pdf] by kragen on Thursday March 02, 2023 @02:08PM (#35000266)
i think that's a purely semantic or terminological debate, so it isn't important which part we declare 'essential' and which part we declare 'accidental', and arguments that one definition or the other is better are pointless

what is important is that we understand which definitions people were using in particular utterances, that our definitions have adequate ontological coherence (avoiding "eargrayish" reasoning errors), and that other people can understand which definitions we are using

in brooks's 01986 paper he clearly considers questions like copying 2 gigabytes of data 'accidental' http://worrydream.com/refs/Brooks-NoSilverBullet.pdf

> All software construction involves essential tasks, the fashioning of the complex conceptual structures that compose the abstract software entity, and accidental tasks, the representation of these abstract entities in programming languages and the mapping of these onto machine languages within space and speed constraints. Most of the big past gains in software productivity have come from removing artificial barriers that have made the accidental tasks inordinately hard, such as severe hardware constraints, awkward programming languages, lack of machine time. ... The essence of a software entity is a construct of interlocking concepts: data sets, relationships among data items, algorithms, and invocations of functions. This essence is abstract, in that the conceptual construct is the same under many different representations. It is nonetheless highly precise and richly detailed. ...

so it isn't important what mike acton thinks the terms should mean unless we're trying to understand how he uses them; brooks, moseley, and marks lump space and speed constraints into the category of 'accidental' rather than 'essential', and their arguments need to be interpreted using the definitions they intended to use, not conflicting definitions invented by a youtuber decades later

similarly, people commenting on moseley and marks's paper should be assumed to be using the same definitions as moseley and marks, not conflicting definitions, unless they explicitly state otherwise

Re:Out of the Tar Pit (2006) [pdf] by akkartik on Thursday March 02, 2023 @02:12PM (#35000333)
Totally fair. Sorry, I'm walking in halfway to this conversation with a different axe to grind.
Re:Out of the Tar Pit (2006) [pdf] by kragen on Thursday March 02, 2023 @07:11PM (#35003938)
there's an interesting aspect to recursive decomposition there; an 'essential problem' at one level of abstraction may merely be an accident introduced one level higher up

like, lots of programs are specified to do one or another thing with the filesystem, and have to include extra complexity to do it, but the filesystem is something we introduced and could do without; it doesn't exist in objective reality outside the computer. is that complexity accidental or essential? at the level of the program it's essential (especially if the program is something like find(1) or cp(1), whose job can't be defined at all without presupposing a filesystem) but at the level of the system it's accidental

Re:Out of the Tar Pit (2006) [pdf] by akkartik on Thursday March 02, 2023 @08:17PM (#35004543)
100%

Division of labor: making accidental complexity essential since 1945.

Then again, perhaps we need to account for opportunity cost. Even if persistent storage can take many forms, it's hard to imagine that equivalent features could be 10x simpler. Maybe Chuck Moore would disagree, but his modus is usually to insist you don't need something.. (also totally fair)

Re:Out of the Tar Pit (2006) [pdf] by akimball on Tuesday February 28, 2023 @01:37PM (#34973019)
The optimizer removes that.
Re:Out of the Tar Pit (2006) [pdf] by pletnes on Tuesday February 28, 2023 @02:47PM (#34973891)
Btrfs does stuff like instantaneous snapshots for a multi terabyte size filesystem. I have no idea how they do it, but apparently it’s 100% possible.
Re:Out of the Tar Pit (2006) [pdf] by waynesonfire on Tuesday February 28, 2023 @06:18PM (#34976338)
Well since it's claiming to not mutate state, it must be simple! Go read the code and tell us what you learn.

In the event you find the source code TLDR, indeed it seems implementing an immutable api is complex.

Re:Out of the Tar Pit (2006) [pdf] by kilgnad on Tuesday February 28, 2023 @07:23PM (#34976989)
To see immutability on huge state in action see git.

That is essentially what must be going on under the hood to model mutable state. Every mutation made to a variable is a commit. The only commits that are saved are the ones that have existing references.

Now you're thinking it could be a big memory leak. But you pair this with reference counting you can eliminate the leak.

Essentially when the reference of the earliest commit goes out of scope, the next earliest commit is checked out as the initial root commit and the current commit is freed.

The memory model is then like a moving git window/linked list. Mutations are committed at the tail end while memory is freed at the head.

Re:Out of the Tar Pit (2006) [pdf] by raatuter on Wednesday March 01, 2023 @02:31AM (#34979717)
Immutable data structures are often implemented as persistant data structures, which have the traits of immutability but does not "copy everything on every change" https://en.wikipedia.org/wiki/Persistent_data_structure
Re:Out of the Tar Pit (2006) [pdf] by feoren on Tuesday February 28, 2023 @01:06PM (#34972622)
Engineering is about tradeoffs. There is no One Model To Rule Them All. Your post is a great thing to keep in mind, but it's not a prescription. Engineers need to be able to look at a problem from many points of views at once, and try to find the right path for their current problem. This is why it's so important that models play nicely with one another, something that functional programming is getting better at, but reactive programming still really struggles with.

All of your asterisked points are well-taken, but: do you need that capability? Sometimes; sometimes not.

Re:Out of the Tar Pit (2006) [pdf] by layer8 on Tuesday February 28, 2023 @02:36PM (#34973772)
> All essential mutable state can be modeled away to use no mutable state

However, that modeling introduces complexity of its own that not everyone agrees is entirely essential.

Re:Out of the Tar Pit (2006) [pdf] by ergeysay on Tuesday February 28, 2023 @12:12PM (#34971838)
Not to mention that pure FP completely handwaves away implicit global state such as heap.
Re:Out of the Tar Pit (2006) [pdf] by kilgnad on Tuesday February 28, 2023 @12:29PM (#34972126)
That's the point. This hand waving allows the user to build more complex systems.

The cost is efficiency but make no mistake not thinking about the heap allows people to build much more complex programs. It's a trade off between complexity and efficiency.

Re:Out of the Tar Pit (2006) [pdf] by cubefox on Tuesday February 28, 2023 @01:21PM (#34972816)
Then could it reversely be argued that not worrying about the stack (tail recursion) allows people to build more complex programs? Probably depends on which is more worrisome on average, heap or stack.
Re:Out of the Tar Pit (2006) [pdf] by akimball on Tuesday February 28, 2023 @01:39PM (#34973056)
Au contraire. It is a tradeoff between application programmer effort and compiler writer effort. Amortization infers it is better to have an extremely advanced IR optimizer.
Re:Out of the Tar Pit (2006) [pdf] by kilgnad on Tuesday February 28, 2023 @03:04PM (#34974078)
Then why do zero cost abstractions exist? Languages like rust and C++ make the trade off on the application side rather then the compiler side.
Re:Out of the Tar Pit (2006) [pdf] by kilgnad on Tuesday February 28, 2023 @12:27PM (#34972095)
No. Pure functions are more modular. It allows you to treat your logic like bricks and building blocks. Pure functions are composable purely off of types. Combinators are actually the proper term here.

Mutable state does not do this. Your idea of generations doesn't make sense, because that means every function must take a generation as the input parameter too. Given a different generation number the function must deterministically create the same output.

It is not a complete system. Your equations produce this useless generation number that aren't reused as part of the system, it's just there for you and meta analysis.

That is not to say your first paragraph is completely wrong though. There is a trade off between modularity and efficiency. Mutating a variable is less resource intensive then generating a new variable.

Re:Out of the Tar Pit (2006) [pdf] by feoren on Tuesday February 28, 2023 @01:11PM (#34972682)
> Pure functions are more modular. It allows you to treat your logic like bricks and building blocks.

Mathematical purity is indeed what allows you to treat your logic like bricks and building blocks. My point is that "a monad is a monoid in the category of endofunctors" is not the only "pure math" out there, and also that your domain model is likely the most impure part of your whole program. Functional programming is awesome! But mostly ignores the existence of essential mutable state, and embracing functional programming is only a small part of the "mathematical purity" struggle that is indeed the only way to build truly reusable building blocks. If you're spending lots of time building clean, pure, mathematical data manipulation logic, but the data you're manipulating is "Dog : Animal" and "Cat : Animal", you're in a garbage in/garbage out situation. Worry about the mathematical purity of your data model itself. It will marry and dance and harmonize with the purity of your functional logic!

> Mutating a variable is less resource intensive then generating a new variable.

Not always.

Re:Out of the Tar Pit (2006) [pdf] by mpweiher on Tuesday February 28, 2023 @12:58PM (#34972516)
> Plenty of state -- even mutable state -- is essential complexity.

Arguably most of it!

After all, computer don't compute.

https://www.youtube.com/watch?v=EKWGGDXe5MA&t=297s

One of the miseries of life is that everyone names everything a litte bit wrong, and so it makes everything a little harder to understand in the world than it would be if it were named differently. A computer does not primarily compute in the sense of doing arithmetic. Strange. Although they call them computers, that's not what they primarily do. They primarily are filing systems. People in the computer business say they're not really computers, they are "data handlers". All right. That's nice. Data handlers would have been a better name because it gives a better idea of the idea of a filing system.

Re:Out of the Tar Pit (2006) [pdf] by dagss on Tuesday February 28, 2023 @02:20PM (#34973572)
Related: The Norwegian word for "computer" is "datamaskin". Or, "data machine".
Re:Out of the Tar Pit (2006) [pdf] by SilasX on Wednesday March 01, 2023 @09:17AM (#34982617)
And in the Romance languages, some variant of “ordinator”, meaning someone who orders, regulates, or arranges, closer to the tasks we actually expect of computers.
Re:Out of the Tar Pit (2006) [pdf] by cubefox on Tuesday February 28, 2023 @01:15PM (#34972747)
I always thought the problem with a "purely" functional view is much more practical: From my limited Haskell experience, I gather that you have to use recursion instead of loops, since loops (and GOTOs) work by iteratively modifying some state, unlike recursion. But humans think in loops. If you look in a cookbook for a recipe, it will almost certainly contain loops and rarely any recursions.

Recursion programs are provably Turing equivalent to WHILE or GOTO programs, but that doesn't mean they are equally natural for our brain to think about. (Not to mention things like tail recursion, which is even less intuitive.)

Re:Out of the Tar Pit (2006) [pdf] by dannyobrien on Tuesday February 28, 2023 @01:36PM (#34973006)
I definitely don't think in terms of recursion (and I don't think I've seen it explicitly in a recipe!), but I'm not sure loops and interation are necessarily that intuitive, either. I learned programming at an early age and I still distinctly remember the period when loop constructs "made sense".

I can believe that recursion is even less easy to teach than iteration, but it may be that this is a pedagological issue, or that we have yet to work out the best way to convey or document computation.

Re:Out of the Tar Pit (2006) [pdf] by cubefox on Tuesday February 28, 2023 @03:51PM (#34974651)
Many recipes contain WHILE loops. E.g. while the pan is not full, layer meat sauce, lasagna noodles, and bechamel sauce. FOR loops (do the x process 10 times, or once for everything in a list) are also prefectly natural. Examples for recursion are not as easy to find. This points to recursion itself being harder for us than loops. Understanding loops in recipes doesn't require pedagogical attention.
Re:Out of the Tar Pit (2006) [pdf] by mrkeen on Tuesday February 28, 2023 @01:58PM (#34973311)
> Think of an IDE where a user is typing out code: the state is changing all the time.

That's easy mode. Mutability is often justifiable when one person is doing one thing to a computer at a time. Now extrapolate to multiple people editing the same document at once. Suddenly you're discussing CRDTs and other approaches. And now implement undo on top of that!

Likewise with git, or blockchain, or kafka. They're persistent logs so you can keep your head straight while figuring out what the current state(s) of the system can be. Even with git, when you do an in-place mutation (force-push) there's still an extra log (reflog) behind the scenes trying to keep the changes sane.

Re:Out of the Tar Pit (2006) [pdf] by feoren on Tuesday February 28, 2023 @02:12PM (#34973489)
I think this is a good example of why "simple CRUD systems" are actually much more complex than people usually give them credit for. Anything seems easy if you do it badly. But multiple people editing a document at once with CRDTs and undo is still even easier than a basic CRUD application done properly: now you have multiple people editing multiple different data types at once! In such cases we should be thinking about building CRDTs over all the various operations users may be doing with the data, which means thinking about associativity and commutativity of generic data operations. Git only has to deal with merging text files line by line; CRUD applications have to deal with merging lots of different data types!
Re:Out of the Tar Pit (2006) [pdf] by lucasdicioccio on Tuesday February 28, 2023 @06:09PM (#34976239)
IMHO in the context of the Tar-Pit Paper (programming for services and applications rather than electronics) mutation merely is a mean to an end. The end being: some piece of memory holds the value I expect (e.g., to transmit, store, show information). Thus I disagree that mutable state is essential complexity. For the rest of the post I don't understand what you mean with "walking the line between avoiding/ignoring mutable state" and I wish you ad elaborated about what you mean by "play very well with Turing neighbors" because I cannot really connect with that.

Regarding, functional programming and mutation. In a typed "pure-FP" language like Haskell: - you need to explicitly declare what constitutes a valid value - which drives what valid values are observable between program steps - which informs how corrupted a data-structure can be in your program

For instance, using a tree-shaped structure like `Tree1 = Leaf Int32 | Node Int32 Tree1 Tree1` and you have some `MutableRef (Tree1)`. You know exactly that from the ref you can read a whole Tree1, you can change the content of the MutableRef but you need to give a whole new Tree1. In particular, you are sure to never read "an half-initialized tree" or "a tree that was changed while I was reading it" because these things just do not exist in the realm of valid Tree1s. Such simple guarantees are really important to many programs but are not guaranteed in most programming languages.

Of course, if for some reason (e.g., for performance) you need something more flexible and are willing to pay the extra complexity, you can do it. As an illustration, you can augment your Tree1 with some MutableRef as well. `Tree2 = Leaf Int32 | Node Int32 Tree2 Tree2 | Cell (MutableRef Tree1)`. Here Tree2 contains mutable references to a valid Tree1 so Tree2 is not fractally complicated but you could do that too. With Tree2, you can interleave reads, writes, and other operations while working on a Tree2. These extra effects could be required (for performance reasons for instance) at the expense of inviting surprising behaviors (bugs). In pure-FP it is clear that with Tree2 we lost the possibility to "read/write a whole tree in a single program step". Thus, if the control flow of the code is not linear (e.g., multi-threaded, callback-heavy) you may have fun things occurring, like printing a half of the tree at a given time and the second half of the tree after a mutation, resulting in printing a tree that never existed. Enforcing global properties like the heap-invariant becomes inconvenient because it is actually hard if we let mutation in. I'd go as far as saying that the Tree2 doesn't exist as a tree: given a Tree2 we are merely capable of enumerating chunks with tree-shapes piecewise.

This reply is long already, so I won't go into how Haskell has various flavors of mutable refs in IO, ST, STM and recently even got linear-types to allow some fine-grained composition-of-atomic-mutations/interleaving/prevention of effects. But overall I find pure-FP takes mutability much more seriously than other more mainstream languages. Here, pure-FP just keeps us honest about where non-determinism bites. The collective failure to realize is one of the key reason why we are in The Tar-Pit: violated in-memory-invariants can compound even outside programs by forcing outside/future users to deal with things like misleading information being shown to users, urgently upgrading your fleet of servers, days of one-off data-cleanup and so on and so forth.

Re:Out of the Tar Pit (2006) [pdf] by bitwize on Wednesday March 01, 2023 @12:04AM (#34978975)
Ah, but if you model the mutable state as a function taking prior state to new state (and no cheating and modeling it as World -> World), you can constrain and control it into something you can reason about. You're one step closer to taming the dragon of chaos, bucko!