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.
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.
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.
FWIW, I'm trying to imagine the author's hypothetical. If you think I'm imagining something other than what he proposed, I'm all ears!
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
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.
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)
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.
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`.
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.
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.
* "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.
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.
So called "visual" programming languages like LabVIEW and Visual Basic seem to come very close. There's a supervisory program that manages and parses inputs, and schedules the invocation of functions when those inputs change. I'm not sure what those programs do with the outputs of functions is exactly what the author proposes, but you could let the supervisory program route the outputs of functions to their desired effects.
Definition of the expected input format and its interpretation, and likewise for the output format, would presumably be done in some meta-programming language.
My Visual Basic programs contained little or no input/output code, until I started using VB to control industrial machines.
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.
In the meantime, Dhall is much further ahead, so you could check out more of Gabriel's stuff if you need more convincing.
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.
""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 "
* 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.