
Can functional programming be liberated from the von Neumann paradigm? (2010) - stevekrouse
http://conal.net/blog/posts/can-functional-programming-be-liberated-from-the-von-neumann-paradigm
======
yason
The question is valid if you intend to stick with one paradigm and keep it
pure. For any practical purposes, that's useless crap.

Nothing wrong with writing the gist of your program written in functional way,
keeping things immutable, allowing for orderless execution and lazy
evaluation. But because your program interfaces with the real, consequential
world it must realise all that computation at the pivot points where there is
I/O and it suddenly matters that the lazy evaluation is complete.

Have a procedural loop to drive your events and other practical stuff and,
from there, launch functional computations to calculate a completely or
partially new state for your program. Keep things mostly immutable but do
mutation in certain specific locations where it makes sense.

Clojure is good at drawing the line where the programmer sees it fit best.
Multi-paradigm has always beaten pure ideologies hands down when it comes to
delivering useful programs that actually work without making programmers do
mental gymnastics for the sake of purity. (They should reserve their efforts
to be mentally gymnast in the real-world problem they're solving.)

You don't want to write all of your program in object-oriented, procedural,
functional, declarative, or whatever paradigm you have a thing for. They all
do some things well and suck at others. Use them like tools, the right one for
the right job.

~~~
arambhashura
Genuine question: what are some accessible examples of real-world programs
that use multiple paradigms "well"?

~~~
rocqua
Scala maybe? It does both OO and functional.

------
hyperpape
This piece is hard to evaluate: it seems more like an expression of hope or a
manifesto than something I can engage with.

It argues that the idea that there is an ineleminable imperative core in our
programming model is just an assumption today, and I agree.

But that doesn’t mean it’s right or wrong, just that we have to wait to prove
or disprove that assumption. People should keep trying to disprove it, but I
can’t fault the majority of developers for continuing to take it as an
assumption.

------
michaelmrose
Not at all programming related but I found this quote incomprehensible

"Belief, as I use the word here, is the insistence that the truth is what one
would “lief” or wish it to be. The believer will open his mind to the truth on
the condition that it fits in with his... "Faith, on the other hand, is an
unreserved opening of the mind to the truth, whatever it may turn out to be.
Faith has no preconceptions; it is a plunge into the unknown. Belief clings,
but faith lets go. In this sense of the word, faith is the essential virtue of
science, and likewise of any religion that is not self-deception." – Alan
Watts

Faith as every human on earth uses it is a belief that that person has opted
to exempt from questioning. It means in fact the opposite of his usage and all
belief systems are necessarily self deception stories we make up and tell
ourselves to render sensory soup a coherent narrative in which we have a part.

~~~
ppseafield
I tried to find a little more context on the quote. Note that Alan Watts was a
major popular figure for bringing zen Buddhism to western audiences. Turning
things like definitions on their heads is definitely in line with that goal.
:)

> We must here make a clear distinction between belief and faith, because, in
> general practice, belief has come to mean a state of mind which is almost
> the opposite of faith. Belief, as I use the word here, is the insistence
> that the truth is what one would “lief” or wish it to be. The believer will
> open his mind to the truth on the condition that it fits in with his
> preconceived ideas and wishes. Faith, on the other hand, is an unreserved
> opening of the mind to the truth, whatever it may turn out to be. Faith has
> no preconceptions; it is a plunge into the unknown. Belief clings, but faith
> lets go. In this sense of the word, faith is the essential virtue of
> science, and likewise of any religion that is not self-deception.

The previously missing sentence and the very beginning of your quote seem to
address your comment: "in general practice, belief has come to mean a state of
mind which is almost the opposite of faith. Belief, as I use the word here,
..."

My impression is that belief has become according to Watts as an assertion of
a specific hypothesis: I will open myself up to this idea if and only if the
idea fits with my preexisting ones. If you are testing a hypothesis, this is a
poor attitude to have, and it can affect how you perform the test: the test
could be wrong but agree with your preconceptions. This is how "belief
clings".

Faith, as the "essential virtue of science", according to his definition would
be detachment from the outcome of testing a hypothesis, "whatever it may turn
out to be", with "no preconceptions". You have a question, and you are
employing a method, and the outcome remains unknown until you complete the
test (and perhaps even after). This is how faith "lets go".

Religion of "self deception" is one where those practicing it only open their
minds to any particular idea "on the condition that it fits with" their
preexisting ideas. Religions that do not deceive themselves must then be
religions that open themselves to new ideas, whether or not those new ideas
agree with ideas they already hold.

------
lbj
Understanding and modelling IO is a big part of why Clojure took of like it
did. Its functional, its pure-ish but it has an understanding of time,
concurrency and IO baked in. When you do IO time matters, take a look at how
Datomic models this (incrementally).

But even from the early days, Rich Hickey said that no pure functional system
exists as even an empty HD emits heat, which is a form of IO and I think we
should listen to Hitchens and abandon our "fantasy of purity" when it comes to
writing real software.

~~~
mbrock
I don't think that's what functional purity means. Actually I think there are
a lot of huge misunderstandings about this.

It's kind of like, think about grammars of human languages. Obviously human
communication involves commands. But you might still have a good reason to
desire a grammar that is "purely functional" in its mathematical formulation.

Pure functional programming is about establishing a certain level of formal
discourse where expressions involving applications of functions reduce to
values. It's not a fantasy where effects don't exist—it's a way of being
precise about effects in a particular style of formalism.

These expressions should be able to describe all kinds of systems, as indeed
Haskell's IO type is extremely capable when it comes to describing
multithreaded sequential computations involving mutable state and external
effects.

Conal is suggesting that the IO type is a bit crude compared to the full
potential of "functional reactive programming" which is for sure a powerful
paradigm.

~~~
lbj
Im just going by the formal definition of purity

1\. Its return value is the same for the same arguments (no variation with
local static variables, non-local variables, mutable reference arguments or
input streams from I/O devices). 2\. Its evaluation has no side effects (no
mutation of local static variables, non-local variables, mutable reference
arguments or I/O streams).

As soon as I/O is involved, functional purity is technically broken and since
any real-world system will have I/O I consider the quest for purity to be a
fantasy in the same vein as developers who invest a lot of resources in
"proving" their functions.

~~~
mbrock
Functional purity isn't broken by Haskell-style I/O. In Haskell, I/O is not
done through side effects, and I/O effects are not functions, so your points 1
and 2 are not violated. It's not a "fantasy." You just don't understand it.

~~~
platz
The post is really talking about choosing the best kind of composition to
solve problems in (purity was a by-product of being mathmatical).

You guys are arguing over the monadic io that he dismisses in the first couple
couple paragraphs.

All he really cares about is achieving program composition.

------
deepakkarki
Was about to mention I heard about this in the future of coding podcast[0]
last week, then realised this was posted by Steve (who runs the podcast).

[0] : [https://futureofcoding.org](https://futureofcoding.org)

Strongly recommend anyone interested in such topics (programming languages,
rapid prototyping, HCI, low code platforms, etc) to check out his work. Steve
is doing some really great work as a individual researcher!

------
carapace
Cf. [http://conal.net/papers/compiling-to-
categories/](http://conal.net/papers/compiling-to-categories/)

Abstract

> It is well-known that the simply typed lambda-calculus is modeled by any
> cartesian closed category (CCC). This correspondence suggests giving typed
> functional programs a variety of interpretations, each corresponding to a
> different category. A convenient way to realize this idea is as a collection
> of meaning-preserving transformations added to an existing compiler, such as
> GHC for Haskell. This paper describes such an implementation and
> demonstrates its use for a variety of interpretations including hardware
> circuits, automatic differentiation, incremental computation, and interval
> analysis. Each such interpretation is a category easily defined in Haskell
> (outside of the compiler). The general technique appears to provide a
> compelling alternative to deeply embedded domain-specific languages.

------
kuwze
I keep hoping to read something about bypassing the von Neumann bottleneck[0].

[0]: [https://whatis.techtarget.com/definition/von-Neumann-
bottlen...](https://whatis.techtarget.com/definition/von-Neumann-bottleneck)

------
dahart
> A person convinced of (and perhaps ego-invested in) the nonexistence of new
> possibilities is a person actively resisting their opportunity to discover
> possibilities — too stuck in the obvious to discover new truths.

Since this article is almost 10 years old... has any progress been made in
demonstrating the existence of pure functional I/O? Reading files and writing
to the console is one thing, I’m more curious about human input, GUIs and
language. Think about apps Photoshop, Super Mario Bros, or Sublime text. The
ways we _want_ to interact with them seem more fundamentally sequential. Not
only can I not see how to make them pure functional, I can’t see what that
even means or why it would ever be desirable.

> Hand-waving is an important factor in staying stuck in impossibility
> thinking, since rigorous argument uncovers unconscious limiting assumptions.

You have to admire the effort to discredit any rational thinking before it
starts.

~~~
heavenlyblue
>> Reading files and writing to the console is one thing, >> Think about apps
Photoshop, Super Mario Bros, or Sublime text.

Well, there's literally no difference between reading from a file or from a
queue of requests from the user on any level of abstraction.

E.g. you could (functionally) apply a list of operations to an image in
Photoshop that the user has supplied you with.

~~~
dahart
There is a big difference between reading files and human interaction.

You don’t normally have to output intermediate state while reading a file, but
you do with human interaction. Human interaction is usually responding and
reacting to intermediate the state of the program. Think a bit more about
Super Mario Bros.

A file can be read as a single chunk of memory, and it doesn’t need to consist
of a queue of sequential operations. We can decide and design what files are
and what’s in them. An array in a file doesn’t need to be read one number at a
time.

Human interaction, unlike a file, cannot happen all at once, needs
intermediate feedback, and cannot be reordered or easily reformulated into
another representation, _because_ it’s responding to the intermediate state of
the system.

~~~
zachrose
But we can imagine a program that reads a file byte-by-byte and where some
internal state of the program changes depending on those bytes. This program
could even exit when given three “#” characters, which might mean you need an
internal counter for each “#”.

In that sense Mario isn’t really that different!

(Subtly, a purely functional Mario game has more inputs than just the
controller. You’ll need a clock signal for the Koopas to walk around at a
given pace, but that’s not too different from a file of sequential numbers
that’s read at a particular speed.)

Backstory: I used to think that front-end JavaScript programming was too
stateful and inherently messy to be compared with, say, handling HTTP
requests. But the Elm language (and to a lesser extent React/Redux) has shown
me that the messiness is accidental. You actually can apply the techniques of
purely functional programming to create arbitrarily sophisticated GUI
programs. Here’s a simplified version of Mario in Elm:
[https://github.com/avh4/elm-mario.](https://github.com/avh4/elm-mario.))

~~~
dahart
No, you missed the point.

The human input _depends_ on the intermediate state, where the file input does
not.

Manufacturing a case where you output the intermediate state as you read a
file is missing that crucial dependency.

You must also assume that the human input cannot be predicted before the
intermediate state is known. Assuming that it’s a function of time is
cheating.

> You actually can apply the techniques of purely functional programming to
> create arbitrarily sophisticated GUI programs.

No, you can’t. That’s exactly what this article is talking about. React is a
state machine under the hood. You can handle each request in a functional way,
but you can’t build the request processing behind the scenes in a function
way, it’s not currently possible.

However, from your point of view, all of _your_ code can appear to be
functional, so some of the comments here are, reasonably, asking the question,
why does it matter?

~~~
zachrose
> The human input depends on the intermediate state, where the file input does
> not.

Not sure what you mean by “depends” here. If I’m at a game controller I can
smash buttons for years without caring what the state of the game is. It’s up
to the game to make sense of those button presses and do the right thing.

~~~
dahart
It’s a two-way dependency. You are taking about the game responding to the
human, but I’m talking about the human responding to the game. Both need to
happen, they are entangled, which is why you fundamentally can’t separate them
and treat them as an abstract function of time, nor can you re-order the
events (which may be necessary for pure functional interaction).

Just because you can smash buttons randomly doesn’t prove anything, that’s not
normally how people play games. Normally, they see what’s happening and
respond in order to succeed.

~~~
zachrose
Ok that’s something else then. I thought we were just talking about crafting a
program that accepts user input (sensical or otherwise) and draws the right
graphics on a screen.

If you want a complete cybernetic framework to model the player and the game,
I wouldn’t know where to begin.

~~~
dahart
I don’t want a model of the player at all. (And I don’t think it’s possible to
make one that will solve this problem.) We are talking about how to increase
the scope of FP to include the I/O layer, because it currently ends at the I/O
interface. I’m arguing that it might be fundamentally, perhaps provably
impossible to have pure FP when there’s unpredictable human interaction, due
to the coupled dependency on intermediate state.

------
siceviq
I don’t know a lot about Haskell or pure functional programming. Composing
entire programs sounds really desirable though, and while imagining what that
would look like I came up with this:

Suppose we had a runtime comprised of objects, and execution was represented
as messages sent between objects, as in Smalltalk. Unlike Smalltalk, however,
objects would never be created or destroyed. All of our program’s logic and
behavior would be represented as a pure transformation on every message
between objects. In pseudo-Haskell:

    
    
      -- A message is a receiver identifying an object, like “Program”, and a selector, perhaps containing data
      myProgram :: Msg -> Msg
      myProgram (Program {begin}) = Window {createWindowWithName="myWindow"}
      myProgram (Program {windowCreatedWithName=name}) = (Window {windowWithName=name, setTitleTo="Hello world"})
      myProgram (Program {windowWithName=name, didChangeTitle=title}) = (Console {print=title})
      myProgram msg = msg
    

Here the runtime begins execution by trying to send the message [Program
begin], but myProgram([Program begin]) evaluates to [Window
createWindowWithName:"myWindow"], so that is sent instead. The “Window”
object, when sent [Window createWindowWithName:String], both puts a new window
on the screen, and sends the message myProgram([Program
windowCreatedWithName]), and so execution continues.

It would be easy to compose programs written using this paradigm, and not hard
to imagine what composition of executable programs would mean. For example, to
log the name of every window created to the console:

    
    
      logWindowName :: Msg -> Msg
      logWindowName (Program {windowCreatedWithName=name}) = Multicast {messages=[Program {windowCreatedWithName=name}), Console {print=name}]}
      finalProgram = logWindowName . myProgram 
    

(Here “Multicast” is supposed to be an object that can take several messages
and, with no notion of order, send them out all at once.)

We could add yet another object representing mutable state, with messages
describing changes to it and responses containing data stored there. Without
using something like this to explicitly model it, I don't see any explicit
sequencing of instructions as in the IO monad.

Are there obvious problems with this approach, or has anything very similar
been tried?

~~~
jsmith45
Interesting. So would there really only be a fixed set of existing objects,
and the interaction layer of the program would be simply a function that is
applied to messages passed between the objects?

The message that is actually left gets sent to the object, which generally do
impure things?

Would there be any way to define such objects? (Any such definition would of
course basically just be a message translator like the program function, but
if users cannot define such an object I'd imagine some real pushback, or at
least some complaints.

Without a built in object to handle say MyFavoriteDatabase, doing the
equivalent via a message to Network, and its reply message back to Program
would feel a lot like completely useless boilerplate. (especially since i
would need to transform the high level query to something low level like raw
packet contents before i could send it to Network, and do the reverse on the
reply, while a MyFavoriteDatabase object could do that all for me.

------
crimsonalucard
How about

    
    
      print :: a->a
    

or some variant.

Or

    
    
      print:: a->(a->b)->a
    

where (a->b) is a function that extracts some value 'b' out of 'a' and that
value 'b' is printed.

I mean pretty straightforward. id functions in your compositions don't effect
the overall program.

This is only for the O part of IO. Input is a different problem.

~~~
rocqua
These are pretty close to the debug statements in haskell.

This is partially problematic because of lazy evaluation. It is not clear when
the print-statement will actually print. This could even lead to a re-ordering
of the print statements.

Another issue occurs when output is piped to input. In bash you could do
something weird like:

    
    
        foo -i bar.txt >> bar.txt
    

where -i is an input argument. I have to admit that I can't come up with a
reason to run the above command. Really, maybe just ignore this part and
consider the ordering issue.

------
ankurdhama
I just don't get the idea that "sequential" is somehow a bad thing? The whole
concept of an algorithm/procedure to perform a computation is about performing
a series of operations in a specific order. You have to think about the
operations and their order as well to come up with an algorithm.

~~~
ghusbands
Things are only sequential when you're programming in imperative style.
Haskell, for example, supports plenty of algorithms that don't have a specific
order in which things happen. Prolog, too. And Halide. Also, Oz. There are
plenty around, and they are both useful and fascinating.

~~~
wwweston
Not sure Prolog is a good example here; my experience with it involved having
to spend essentially as much time reasoning about how the
unification/backtracking algorithm was going to visit different clauses and
facts (and how I might want to cut/control that) as an imperative programmer
might think about the order in which statements are executed. Maybe more,
considering the process was less linear and obvious.

(Not that I didn't like Prolog -- I did -- I just felt far from freed of
mundane concerns of execution order.)

------
platz
Is this about FRP

