aL_ wrote:
how do you do ui(say a textbox) without mutable state?
You use a monad to
model the mutable state!
Btw, F# isn't purely functional, so you would just use standard .Net functions, but if you do want to understand how the purely functional langauges do it, read on!
Basically, how would you do IO in a purely functional language? Well it's tricky isn't it? You need to ensure that functions only depend on their inputs, so how would you go about reading a line of text from the terminal, say? You could do something like this:
getLine :: World -> (String,World)
Basically a function that takes in the "World", and returns a string and a new World (one in which the input buffer of your computer contains a couple of characters fewer). This is just a semantic model, obviously the compiler doesn't
actually pass around the whole world! So it's just a trick that makes "getLine" behave like a real function, even though it reads from the terminal.
Then you would just pass this new World into the next IO function which returns the next World and so on. Of course, there's an obvious problem here. What happens if you accidentally pass in an old World into a function? Oops! We blow up! There's no way of actually storing the whole world, so this only works if the "World" is just some abstract "token" that we pass around to make our semantics hold. If we use an "old" world then we need to retrieve a state of the world that no longe exists!
So... Monads to the rescue! What if the monad, called "IO a" (see "M a" in the video) keeps track of the World object for us? So
getLine :: IO String
would represent an
action which will return a string from the "World", and keep track of the new World returned. The bind operator (>>=) would be in charge of passing the World value from action to action. So as we compose actions together using >>=, we ensure that we always pass along the "new" World to the next monadic value down the line, and thereby guarantee that there is no way to use an old "World" value. Note that "getLine" is a value which
represents the action of reading from the terminal, it doesn't actually
do anything (it just
models it, sort of like a shell script - they're just completely inert sequences of characters, you need to actually
run them before they do anything. In Haskell, only the compiler knows how to run IO actions, so to the programmer everything is perfectly pure).
Here's how a simple hello world program looks in Haskell without syntactic sugar:
main = getLine >>= ( \ name ->
putStr ("Hello, " ++ name ) )
So first we grab the monadic value "getLine" and compose it with the lambda on the right (see the video). The >>= is implemented so that the "putStr" function gets passed the World that is returned by the getLine behind the scenes, but the user never has to see the World being passed around. All that is hidden by the bind operator!
Here's the version with syntactic sugar:
main = do
name <- getLine
putStr ( "Hello, " ++ name )
This looks very similar to any imperative language right? So voila! We have successfully embedded IO in our purely functional language, by hiding the "World passing" inside a monad! This is pretty much the main reason why monads are such a big deal in purely functional lanuages, but as we have seen there are many other monads as well. One of the more interesting ones is the ST monad. This monad just models mutable references. The difference between this one and the IO monad is that a value in the ST monad can actually be run by the programmer, which means that you can write functions that use mutable state, but as long as that mutable state doesn't leak out (the compiler checks this) you can wrap them up behind a pure function interface (using a function called "runST").
Also, regarding the "imperative languages are a superset of functional" statement. That's not necessarily true. Imperative langauges sacrifice referential transparency, so because it doesn't have everything purely functional languages has, it's not a superset.
Also, as I've alluded to, pretty much all of the useful things about imperative programming (mutable state, IO) can be encapsualted in a purely functional language using monads, so in that sense imperative programming is a
subset of functional languages!