
Why do our programs need to read input and write output? - nicolapcweek94
http://www.haskellforall.com/2017/10/why-do-our-programs-need-to-read-input.html
======
dahart
What's the appeal of taking pure functional all the way to an executable
tautology? Inputs can be passed around in a pure FP world without reducing
purity, so why remove them?

We never got around to output at all. Assuming the compiler can't write the
output file before the program runs, how would you see the result of any
program run in this world?

What about UI or network input?

I'm imagining that if only the compiler were allowed to do I/O, I'd have to
rebuild Chrome whenever I wanted to see a new site, or even see an update to a
site, or change my preferences. Wait, scratch that, I can't change
preferences, because it can't save. Is the shell allowed to do I/O? What about
output to the monitor, does that count? It's not a file, but it's Turing
equivalent to an output file.

~~~
Gabriel439
Inputs in a purely functional world are limited to things that you can
serialize and deserialize, which does not include functions or types. If you
ingest values via imports instead of via traditional I/O then you can transmit
any language feature

You're right that I didn't get to output at all. That's my mistake since I
wrote the post in a hurry. The idea is that the interpreter of this language
is not a general purpose compiler but is a specialized application with a
built-in output. For example, one such interpreter might be a browser that
just displays the result to the user.

Using Chrome as an example, the Chrome executable would be an interpreter.
Chrome itself would be compiled. For simplicity I'll assume that we browse
static HTML pages. Your URL bar would be replaced with a code bar that accepts
any arbitrary Dhall expression that builds a DOM. However, since Dhall accepts
URLs in place of expressions it is still a URL bar (as long as the URL you
input refers to an expression that assembles a DOM). The browser would then
interpret that expression to build the DOM and render it as a web page

There are several ways you could configure preferences but I'll throw out a
simple idea just to convey the point. Chrome itself could be configured via a
Dhall expression that assembles a giant record of user settings. Since a Dhall
expression can also be a file you can configure user settings via a path to a
file (as long as that file you refer to contains an expression for building
that record). That file could itself contain references to other files in
order to delegate the configuration of certain options.

That's not the only way you could do it, though. Web pages could be pure
functions of user settings, too. CSS would just becomes a special case of
treating a DOM as a pure function of style settings.

UI or network input is trickier, not because it's hard to model UI or network
input in a purely functional setting but rather because you want to do it in a
different way on a case-by-case basis. For example, some types of UIs are
unchangeable requirements that you have to adapt to (like a game: the UI _is_
the requirement). However, other forms of UIs or network input are just work-
arounds for inability to compose code effectively. For example, batch network
input can be replaced by just importing URLs as code. Similarly, batch user
input can be replaced by just importing files as code.

~~~
dahart
All inputs are serializable, including functions and types. We can already
transmit language features without imports.

If you compile your inputs into your program, what you're left with is a fancy
constant. You might as well let the compiler take the final step of running
the program for you and printing the output.

If you think Dhall is great and more people should use it as their I/O
library, that's awesome, maybe show some of the cool things it does.
Motivating it's usage by trying to make I/O a functional vs imperative issue
just seems really contrived, and mostly wrong and prone to argument. I/O isn't
a functional issue, and it is one of the main reasons to write a program: to
do some work parameterized over different inputs.

~~~
Gabriel439
Your program can be a pure function which somebody else can invoke, so it's
not necessarily a constant

However, Dhall does have the ability to normalize under lambda when possible
so it will actually do this for you if it can! For example, if you try to
interpret this program, which is a function:

    
    
            let replicate = https://ipfs.io/ipfs/QmQ8w5PLcsNz56dMvRtq54vbuPe9cNnCCUXAQp6xLc6Ccx/Prelude/List/replicate
        
        in  let exclaim = λ(t : Text) → t ++ "!"
    
        in  λ(x : Text) → replicate +3 Text (exclaim x)
    

The interpreter will simplify that down to:

    
    
        λ(x : Text) → [x ++ "!", x ++ "!", x ++ "!"] : List Text
    

... although it can't do anything else until somebody else comes along later
and calls the function on a specific input

------
maxxxxx
I don't understand the article. It's still input and output but in a more
convoluted way and therefore better? Reminds me a little of articles you see
from time to time like "if-less programming".

~~~
Gabriel439
Author here: this is written for a primarily functional audience who already
take for granted that it's good to minimize effects, but let me try to
rephrase it another way for people who don't have that background

Typically there are two ways that our programs can ingest values:

* statically, via imports

* dynamically, via reading values

Why not ingest all values via imported code? If the import system is
sufficiently lightweight this is simpler and easier than reading values the
traditional way, plus you can read in things that are not plain values (like
functions and types)

In that sense, reading/downloading text and parsing it becomes an
implementation detail of the compiler and from the programmer's point of view
what you're left with is a network of pure functions that can refer to each
other across impure boundaries

~~~
1_2__4
You seem to be just playing with semantics and claiming it offers a new way of
thinking about I/O.

~~~
Gabriel439
I don't want to think about I/O. That's the point

I want to focus on connecting pure code together without thinking about the
details of how that happens. I want this for the same reason that I don't want
to think about manually allocating registers, managing memory, or caring about
evaluation order

Also, like I mentioned, traditional I/O can't import functions or types, so
this is strictly more powerful

------
rocqua
So, I am missing how this would work. Let me pick 2 examples.

The first is about networking. How would I build, say, an http server in this
thing? From the rest of this thread, it seems like you would advocate
essentially a separate compiler for networking, and to 'read' from a socket,
you just import. That seems like you are moving a lot of complexity to the
world of compilers.

Moreover, I don't see any way to then 'write' to that socket. Does the
compiler essentially take a return value of main and then say 'well, guess
this is what I want to send back'. That would suggest you can't do something
like 'recieve handshake' 'send handshake' 'receive request' 'respond to
request'. It seems to block interspersing input with output.

My second example is a gui. It feels to me like your program couldn't in any
way define the interface layout. All it could do is 'receive' and 'issue'
events. It would take a specialized compiler, and a whole separate mark-up
language to actually define the interface. How do you change the interface in
response to input?

I love pure code, and see the value in pushing the boundary of pure code as
close to the edges as possible. However, code still needs side-effects to be
useful, and I'd like to specify those side-effects in the same language as I
specify my pure code. I'd like to decide for myself how far out to push the
boundary of purity.

~~~
Gabriel439
Yes, this entails moving more logic into compilers/interpreters. For example,
Dhall is actually designed this way: not only is it a command line interpreter
but it's also a Haskell library that you can use to interpret Dhall
expressions and marshal them into Haskell programs. So the Haskell language
combined with the Dhall library becomes an "interpreter builder": a way to
crank out a large number of specialized applications that are customized via
an effect-free programmable configuration language

For the specific example of a server, there would be two layers:

* in Haskell you would implement a server configured by a Dhall expression (i.e. a hybrid server/interpreter)

* end users program in Dhall, not Haskell

* end users provide a record of pure functions (one for each API endpoint) that translate user input to output

However, I think that's still just a superficial answer to the question. The
next level is to ask: why do we even need a server? A server is just a way to
distribute values, but Dhall already has a way to distribute values (and
code): we just import them by reference. So why not just use that directly
instead of standing up a server to reimplement what the Dhall interpreter
already does internally

You can encode markup in a purely functional languages. That's actually the
easy part since you can treat it as an ordinary data structure. Actually, the
harder part is coming up with a user-friendly way to transform a stream of
events into a stream of outputs within a purely functional language. In my
opinion, there is no clear consensus on the "right" way to do this, but the
field of functional reactive programming is based on searching for clean
solutions to this problem

However, again, you need to step back and ask: what did we need this GUI for?
In some cases, the GUI _is_ the requirement (like in a game) so there's not
much you can do to simplify things. However, in other cases -the GUI is just a
poor man's interface to composing values and code, in which case we might just
be able to replace it with importing URLs and paths using Dhall's built-in
mechanisms (and perhaps replace all these bespoke GUIs with a general purpose
GUI for manipulating Dhall expressions)

~~~
cortesoft
You say 'importing urls', but what is going to serve the content at that URL?
A server.

I dont know what it means by saying there is no input or output... reading
from a URL is an input. It is an I/O operation to pull data from a network
stream.

Even the whole writing functions to files things is an input and output
situation. Just because you change the words doesn't change what it is doing.

~~~
Gabriel439
Yes, but it's a restricted set of input and output: the only thing you can do
is import other code. You can't, say, launch missiles or delete files unless
the compiler/interpreter allows it

------
hexmiles
Like others i do not understand this article.

It seem to propose an abstraction where we separate the I/O part from the
"elaboration" part of a programs, so that our programs are entirely pure. But
i don't understand why the import system and why only the compiler can read
and write?

Why can't we have the same benefits by using multi-threading/process? we can
have "pure" thread and "i/o" thread that communicate with message passing? i
feel like I'm missing something from the article.

~~~
Gabriel439
There are two separate questions here:

* "Why should we limit I/O to the compiler"?

Think of the compiler or interpreter as a small trusted kernel. It's a mostly
fixed code base that you can inspect and audit. The programs that it compiles
or interpret on another hand don't have to be trusted because they are
perfectly sandboxed. They are effect-free purely functional code

* "Why not use multi-threading for communication?"

This is (in my eyes) the real problem that I'm trying to solve. Using effects
as a message bus for composing code seems incredibly awkward and primitive,
like explicitly managing stack registers.

------
Etheryte
Perhaps I simply didn't understand the argument, but doesn't this simply move
reading and writing to a different abstraction? Perhaps someone can reword the
author's point, I feel like I'm missing something here.

~~~
qbrass
He just described batch processing like it was a new thing.

~~~
tome
What part of the article makes you think the author believes he's describing
something new?

~~~
qbrass
The lack of any acknowledgement that it had been done before.

If you were trying to convince someone your concept is possible, wouldn't you
point to an already existing example of it if you could?

------
tree_of_item
Excellent, I've actually been working on something similar and I'm glad to see
it validated.

As I expected though, most people in this thread don't get it, which is
understandable since it's pretty out there compared to the mainstream. I think
they will once larger, "real" programs are written.

I don't think I'm gonna be able to "reword" this for people here so it
suddenly clicks, you're probably just going to need to see it in action to
understand it.

~~~
maxxxxx
Why don't you describe a "real" program that uses this approach so we can see
the benefit?

~~~
Risord
Giving ~precise and practical example can be suprisingly hard when you are
working with some very different kind of thinking for a long time. At the end
idea should be used in real world of course but it can take a lot of time to
recreate lot of solutions which are resolved in mainstream but "reopened" in
your world.

I think this article is quite clearly just reprsenting idea for people working
with functional side and maybe thinking something similiar already. So if you
"cannot get it" and author cannot give ~easy answer don't judge yourself or
the idea/author too much but accept that idea can be still very early phase
and work-in-progress.

~~~
maxxxxx
I just don't understand what's funktional or novel about this. That's why I
asked for an example that could illustrate it. The good thing is that the
author has no obligation to listen to me so let's see where this ends up.

------
dredmorbius
Dismissing inputs for the moment: if you cannot write or transmit outputs, how
does your program actually perform anything. If your answer is "side effects",
you've merely redefined the term "outputs".

As I see it, you'd have to have a program which was supplied its input, by the
compiler, was evaluated by the compilier, and had its output imputed, by the
compiler. You've only shifted the problem of input and output sanitisation
elsewhere.

~~~
Gabriel439
The way I would phrase it is that you've concentrated your input and output
sanitisation in a trusted kernel (i.e. the compiler/interpreter) and that puts
an upper bound on the amount of code that you need to audit (just the
compiler/interpreter code base). That's more realistic than auditing all
programs written within the compiled/interpreted language

------
robgibbons
How would this IO model affect, say, Unix piping on the command line?

------
mcnamaratw
My favorite part is that injecting an attacker's code would not cause a
security problem, because the platform is immune to side effects.

~~~
marcosdumay
The compiler is perfectly able to create side-effects.

~~~
mcnamaratw
I'm not making the claim. It seems to be in the linked article:

""That's a security vulnerability!", you protest. "You are ... literally ...
injecting remote code into your program."

Playing the devil's advocate, I ask you what is wrong with remote code
injection

"Well, for starters, if the URL is compromised the attacker can run arbitrary
code like ..."

... reading and writing files? Dhall is totally pure and doesn't support any
effects at all (besides heating up CPUs ).

This brings us full circle back to our original "

~~~
marcosdumay
Oh, ok. On the specific case of Dhall, it can not.

------
millstone
Say the user clicks somewhere in my program’s window. How does my program
discover the location of the click without reading some input?

~~~
tree_of_item
As one example, the program might be written in model-view-controller style:

* Your "model" is a type `Model`.

* Your "view" is a function of type `Model -> Display`.

* Your "controller" is a function of type `(Model * Event) -> Model`.

So you don't need to perform IO to get the mouse click, since your program
definition was already setup to handle incoming events. The runtime is
responsible for feeding your controller events to get a new model, then
running your view on the new model to create a display, then actually
rendering that display.

~~~
JetSpiegel
Isn't that just pushing the side-effects to the runtime? What does it achieve?

~~~
tree_of_item
It achieves the goal of making your code side-effect free. The explicit goal
of these sorts of projects is to push the side-effects to the very edge of the
stack.

------
tosstossy
I want my five minutes back.

