Well, unless by perfect world you mean telepathic computers (yikes!), then you'll require some way to process user I/O - I'll assume something like orthogonal persistence has subsumed the more boring file I/O...
Let's start with input...because it already has one solution. From page 4 of 11 in Conal Elliott's and Paul Hudak's pioneering paper Functional Reactive Animation:
lbp; rbp : Time → Event Event ( )
which, in Haskell, would look something like:
-- read left and right mouse button-press events
lbp, rbp :: Time -> Event (Event ())
So for input from the keyboard:
kbd :: Time -> Event Char.
Other inputs can be dealt with in similar fashion.
So...what about output? The actual word doesn't appear anywhere in the paper (neither does "I/O" for that matter) - we'll have to figure this one out ourselves. But this time, it's our Haskell translation:
lbp, rbp :: Time -> Event (Event ())
providing the hint - Event ()
- the unit-event. That can serve as the result of sending a Char
off, to appear somewhere on your screen:
viewChar :: Char -> Time -> Event ()
Again, other outputs can be dealt with using similar techniques.
...what's that - it isn't denotative?
Because viewChar
is...what - impure?
If so, that means lbp
and rbp
are also impure - are you sure about this?
Alright...let's have a type for taking in a series of those mouse button-press, or other events:
type Intake a = [a]
lpb, rbp :: Intake (Event (Event ())
Is that any better? Good! Well, sort of -
what happens if the mouse is unplugged? That
could put parts of a program into a spin waiting for input (and using []
would permanently end the series - no more button presses!).
We need to change Intake
:
data Intake a = None (Intake a) | Next a (Intake a)
Now unplugging the mouse results in None …
appearing, which a program can detect and react accordingly e.g. yielding its OS thread,
suspending itself, etc.
So, what about output? Well, output devices can often be unplugged too. Taking a hint from Intake
:
data Outlet a = Wait (Outlet a) | Went (… (Outlet a) …)
It's similar to unplugging an input device - upon encountering
Wait …
, a program can pause transmission.
So what should the type of Went
be? Well, an Outlet
accepts values incrementally to allow Wait …
to appear if needed - the accepting of each value should present us with the rest of the Output
. Therefore:
data Outlet a = Wait (Outlet a) | Went (a -> Outlet a)
Bringing that altogether:
data Intake a = None (Intake a) | Next a (Intake a)
lbp, rbp :: Intake (Event (Event ())
data Outlet a = Wait (Outlet a) | Went (a -> Outlet a)
viewChar :: Outlet Char
So is all this valid? If you're not sure, see section 20.4.2 (page 86 of 263) of Fudgets - Purely Functional Processes with applications to Graphical User Interfaces by Magnus Carlsson and Thomas Hallgren - if Intake
and Outlet
look dubious then so is what can be seen there, in the paper...