Halp!
Halp!
One example of a way to structure a functional program is categorising functions according to whether they are a "source" a "filter" or a "sink", (terms stolen from circuit design). The source being your inputs (not pure), filters doing some transformation on the input, over time (pure) and handing off to a sink that causes some side effect (not pure).
In the "real world" You've probably made a lot of simple programs that mostly follow this structure if you've used unix pipes.
If you go along with this, you find that once you start making more sophisticated programs, you need some kind of "memory" so you can progress. Strictly speaking, memory, as we typically use it in imperative languages, is a kind of side effect. In the model above, though, you can make a special kind of "source" which is something like a feedback loop- a source that contains the output of the previous iteration of the program- or some sub portion of the program. Then maintaining a memory becomes a process of continually taking some input, transforming it, creating an output, and then using that output as an input again. (what you might call recursion)
Why would you do this? Because each part of a program like this is self contained, and its ability to do damage to some distant part of a program is extremely limited. It becomes much easier to reason about such a program. When you find a bug (and these are still possible) you can trace the origin from output to source, or source to output. There is no possibility of some phantom distant subroutine screwing up the dataflow.
And this kind of structure is very useful for games, interactive multimedia programs, and other such graphical interactive program.
A commonly cited "real world" example is xmonad. What I find more insightful is this article on replicating pacman in a functional language. http://prog21.dadgum.com/23.html
For a database example, here is the chapter from Real World Haskell about databases (a bit old but still relevant):
http://book.realworldhaskell.org/read/using-databases.html
And here is some example code:
do
conn <- connectSqlite3 "test1.db"
stmt <- prepare conn "INSERT INTO test VALUES (?, ?)"
execute stmt [toSql 1, toSql "one"]
execute stmt [toSql 2, toSql "two"]
execute stmt [toSql 3, toSql "three"]
execute stmt [toSql 4, SqlNull]
commit conn
disconnect conn
As you can see, it looks very similar to how you would write it in a regular imperative language. The main difference is that since they database operations are IO operations then they need to be in the special do-notation block and cannot be mixed with regular code.As for the question of wanting to mix pure and impure code that much I don't think it actually comes up that much. First of all, I find that I don't often find myself with an impure function that I want to add a side effect in the middle seamlessly - firstly, side-effecting stuff tend to be side-effecting from the beggining and secondly if I do want to turn a pure function into an impure one then forcing me to change the interface helps make sure that I am not breaking any code that used to work because it assumed it was working with pure code. Additionally, Haskell is really good at abstracting code so its not that hard to take a big chunk of logic and split it into a pure and an impure part. Finally, converting code from pure to impure is not that bad - sure, you need to change the syntax a bit but the type checker helps you find all the places that need to change and also helps you check all the code that calles the method you changed so you can be sure that you aren't breaking those.
side effecting operations in the IO monad so you can't run them in the middle of any function and instead must run them inside a special "do-notation block" like I did.
You can have impure code call both other impure code and pure functions and all you can't do is call impure functions from inside pure functions. Sure, its a bit annoying if you ever have to convert a pure function to an impure one (you need to change some of the syntax and all the places that call tha tfunction need to start treating it as impure) but its not that bad: first of all the type checker tells you all the spots where you need to update your code and secondly, Haskell is super good at abstracting code so its not hard to take a big chunk of code with some side effects in the middle and split it into a pure and an impure part.
> to academic program
Its a little pet peeve of mine but it seems that nowadays "academic" doesn't have a real meaning and just tends to refer to whatever people don't like. :)
connectSqlite3 "test1.db" >>= \ conn ->
prepare conn "INSERT INTO test VALUES (?, ?)" >>= \ stmt ->
>> execute stmt [toSql 1, toSql "one"]
>> execute stmt [toSql 2, toSql "two"]
>> execute stmt [toSql 3, toSql "three"]
>> execute stmt [toSql 4, SqlNull]
>> commit conn
>> disconnect conn
and it's "just" a bunch of nested lambdas with no "do" in sight. What's different from "regular" code is that those lambdas 1) return IO actions, and 2) are strung together with monadic bind (>>=) to build one big IO action.To be sure, in this case the do version is much easier to read (that's why "do notation" exists). It can be used with any monad, though - nothing ties it particularly to IO.
bar (foo x) --pure Haskell
vs foo x >>= bar --monadic Haskell
vs bar(foo(x)) -- in C you write both cases like this.foo x ++ bar -- "pure" or "monadic"?
foo x >>= baz -- "pure" or "monadic"?
What if (foo x) is a list in both cases?
(>>=) is just an operator. The values that it operates on are anything with a Monad instance, just like the values that (+) operates on are anything with a Num instance. There is nothing voodoo about monads or about bind (>>=); such voodoo as there is lies solely in IO, which is interacted with the same way as other values (though certainly what you can do with (IO a) is more limited than what you can do with (Maybe a)).
That said, the additional cruft (purity doesn't exist, do notation is mandatory, &c) is well worth shedding.
By itself, this function is useless, but "interact" is a function with signature "(String -> String) -> IO ()" which means that it turns our "unwords . map upcase . words" into an interaction with the environment. There are whole debate here as to how interact is actually still pure, but for the purposes of this argument, interact is an impure effect.
The net program however is very simple: "interact (unwords . map upcase . words)". The pure part is decomposed into many other pure pieces, each with simple, small contracts and independent testability. The impure part also has a simple contract which is described in terms of pure computation.
Another great example is some of the streaming libraries available in Haskell today. Pipes for instance will let you compose a pipeline of pure and impure computations and have them work together nicely.
"fibsPipe >-> Pipes.take 20 >-> Pipes.print"
The first two segments are pure, but have empty "placeholder" slots for impure effects (identity monadic layers). These get merged seamlessly with the "Pipes.print" segment which has impure effects. All together, you get an infinite producer "fibsPipe" producing an impure effect "Pipes.print" while having its resource utilization and termination controlled by another pure computation "Pipes.take 20" which is actually maintaining its own implicit state (the current count of numbers it's let through).
But then you can decompose each piece and understand it on its own in a pure or simple impure fashion.
Now this is not to say that my keyboard is purely functional. The shift key creates state. But that state is isolated and short lived to the point where we can treat it as functional across two key combinations. It is the caps lock key which creates an unpredictable state - I do not know what output I will get without checking its status - the problem with state might be illustrated by the results of hitting capslock when I meant to hit tab while typing a program.
I suppose the important point here is it's not possible for something going wrong with your monitor or speakers to suddenly remap your keyboard without warning.
Among it's other strengths is that functional programs can be built bottom up from lots of little pieces and when we get unexpected results, we can track them down. If suddenly my keyboard's "b" key stops working, the problem is almost certainly with the switch beneath it and not because of something the "h" key is doing.
Another strength of the analogy is that the most disorienting behaviors of a keyboard are tied to state: the worst one for me is numlock off where the behavior shifts to dependence on the state of the display.
It's no different than reading from a file - the state is out in the world and to be useful, a functional program must deal with it.
But note that the key difference is that the programmer does not create the state - it comes from the data. Probably also worth noting that the keyboard is used as an illustrative analogy not as example code.
FP has mutable state.
People keep saying it doesn't because it's the first chance they've had to play with a keyboard that doesn't have a caps lock and they've realized that the number of times they accidentally TYPED A WHOLE PARAGRAPH IN UPCAPS has gone to zero and that tiny mental burden of tracking the state of the keyboard has vanished.
Removing mutable state improves trust.
As others have noted, there's really no such thing as a "purely functional program" unless all you're looking for is turning your CPU into a space heater. That said, there's a very real sense in which the parts of your program that ARE pure are much safer and easier to reason about.
The best way to visualize this is that you can theoretically turn an impure function into a pure one, watch:
void printAGreeting(string greeting)
{
IO.printLn(greeting);
}
That's impure. The string goes into a black hole and does nothing from the program's perspective, but changes the state of the world from everyone else's perspective (it probably makes pixels change on a display somewhere).So how do you make that function pure? Like this:
TheEntireUniverse printGreeting(string greeting, TheEntireUniverse u)
{
return u.makeSomeAtomsMoveAroundBobsConsole(greeting);
}
This function takes not only the string greeting, but everything else in the universe. You, me, everyone who's ever lived, bob's console, everything. It performs the same function by returning a new universe with everything the same except for the atoms moved to caused the greeting to appear on Bob's console.Obviously we can't do this, but (in Haskell, say) the IO monad is a context in which we understand that anything done inside of it has the affect of changing the "real world".
If you pretend that you really had to do the whole "universe dragging" thing around for anything in the universe that WASN'T part of your program (we're assuming it itself is in some black hole somewhere to avoid paradoxes and such), then you would darn well make sure you kept this pain-in-the-ass operation as "thin" as possible. Why? Well, you're lazy!
When you look at things that way, there's an analogy to writing stuff on bare metal hardware. You know you're going to have to write some assembly language (or maybe even direct machine code if you don't have an assembler). What you want is to get to the nice cozy world of C as soon as possible, so what you're going to do is use assembly to code only the smallest surface area you need so that writing in C makes sense, and everything else that doesn't need to be in assembly for performance sake you do in C.
So it is in a purely functional language. The idea is to "get out of IO land" as fast as possible. To use a DB as an example, you'd like to live in a world where your little view of the DB could be expressed in purely functional terms (writing functions that result in a modified version of that view) and only at the VERY LAST MINUTE would you send that DB view to an IO function that actually sent the view down the wire to the DB.
The same goes for files, sockets, gamepad IO, you name it.
And you'd be surprised how little you need to actually interact with the world between getting your data and pushing out state changes. A game, for example, can do a WHOLE LOT OF STUFF between reading the input state and writing graphics commands.
But the frameworks are lacking. The only web framework available got dissolved, and I don't have the energy to wire everything myself in a new language and paradigm that I don't even fully understand).
I have a strong feeling that once you understand either Haskell or Clojure, it will be a breeze to learn other.
My point was to get framework for functional programming that deals with a lot of I/O and side-effects and see docs for real-life examples of usage.
Does Clojure use lazy evaluation?
With something like clojure, you at least have a pretty clear deployment story (make a jar, drop jar in a container) -- but it's still not clear how you would best go about writing something like berklydb in clojure - never mind in SBCL (come to think of it, even implementing a couple of text handling utilities like grep or cat is pretty hard to wrap your mind around when working with "old school" lisps).
All that said, I'm pretty excited about racket, and somewhat hopeful that I'll eventually get around to doing some "real" functional programming in the not too distant future.
Clojure seems to be a modern Lisp implementation. Runs on the JVM, has lots of libraries available.
How else will you "get it" without learning a functional language?
That's why I want to learn Clojure without "getting functional programming" yet.
No, but it makes sense to learn haskell to understand functional programming, which is what you asked. Clojure's emphasis on purity is essentially useless without a type system to go with it. The type system is what separates the pure functions from the impure ones.
You can write "pure" functions in imperative languages. Any function which returns the same result for the same arguments is a pure function. You could write an entire pure functional program in C if you wanted.
Yes. I also understand what a petulant child throwing a tantrum is. Nothing you have said is relevant to my post, it is just misguided anger.
Haskell is a very different language from Clojure and learning it will teach you different lessons. Haskell takes an uncompromising approach to purity which can sometimes be frustrating to manage. Haskell's type system is an incredible piece of work, one which some may dearly miss in other languages (Clojure's core.typed does help a lot in this regard).
But the frameworks are lacking.
From what I gather, the Clojure community (and the functional style and community in general) abhors frameworks. Small, reusable, composable libraries are the order of the day.
Rather, turned from a framework into a library. I only recently started with Clojure and found Luminus pretty easy to pick up. It just generates a simple scaffolding project which cherry-picks some good libraries into a project.clj file and gives you a basic "Hello world" web page to get started. Primarily it uses lib-noir on top of Compojure, so pretty much anything that was in Noir, you get here (I guess, I never used Noir directly). Luminus has a pretty good tutorial to cover basic notions of handlers, databases, etc.
No, it's not Rails or Django, and no it doesn't do anything you could do yourself from scratch pretty easily. It is, however, a nice starting point and may help you get over the "I have to wire everything together myself" hump.
Plenty of that here: http://book.realworldhaskell.org/read/
Other batch processing programs works as well.