Saturday, August 11, 2012

Haskell Prologue: Introducing Functional Programming

All programs are IO

The questions is: how or why?

     
     Yep, that's about it. All a computer program is is an automated method of taking some input (usually from the user), processing it according to some algorithm, and spitting out a result (usually onto the monitor) -- class dismissed! ...jk. 

     Now to the interesting part: How this is done. Now that you have this input-output model in your mind, let's talk about instructions. You need to give the computer instructions for following a sequence of actions. This makes your program useful. You tell the computer that you want to take a number from the user, multiply it by two, and print out the results onscreen.There are two basic ways of giving instructions to someone or something. 

     The first way is to give them a recipe that they must follow: measure a cup of flour, mix it in a large bowl with baking soda, yeast and water etc. , bake and you have bread. These are called imperative instructions. Most people default to this kind of instruction-giving. It's easy to understand, fast to learn, and does not require you to understand why the recipe works. It just works, and that's all that matters. This is the way that the entire body of human knowledge was originally built. Homo-erectus had no idea why mandrake root caused immediate death, they just knew that if you ate it, you died.

     Reason, logic and science gave us a second type of instruction: declarative instructions. These are tricky ideas to get your brain around -- it would be safe to say that they don't come all that naturally. Declarative information is based around some set of fundamental principles in nature. For example: if you drop an apple from a tree, it speeds to the ground, bounces a bit, and then stays there for awhile until something else disturbs it. The declarative principle -- or rule -- behind this is gravity. Once you understand gravity, you know how to make an apple drop violently to the ground; just let gravity do the work. Declarative information seeks to explain why something happens. By contrast, imperative information seeks to explain how to achieve an effect. In short: declarative: why, imperative: how.

     Most popular programming languages (C, C++, Python, Java) are based around imperative instructions. As a result of using them, you get used to telling the computer how you want it to act. The problem with imperative instructions is that they are not very well defined. For instance, every one has their own ideas around how you should bake bread. Bread in France is very very different from bread in Ethiopia. That's because imperative data never tells you why one action leads to a certain result. Simply put: the why is missing! 

     It's not all bad though. One of the really nice things about imperative programming languages is that they feel natural to use. A good example is Python. Python is designed to feel like a spoken language. It takes and processes instructions in a way that is somewhat similar to how you might write out a good recipe.  Besides, there are no purely imperative or purely declarative instructions. In reality, all instructions contain a little of both imperative and declarative knowledge. However, most lean heavily on one type or another. Physics papers lean heavily on declarative data. Mapquest leans heavily on imperative info. Likewise, Python leans on imperative data, but it also features nifty declarative devices like functions. 

     Imperative-styled programming languages have worked pretty dang well for humanity so far. The problem is, when you start to create really large and complex programs (like artificial intelligence routines or video games) imperative languages start to get incredibly messy. The whole program begins to look like one big sloppy cookbook with little discernible structure. When problems start cropping up, it's extremely difficult to troubleshoot them in a largely imperative programming environment. Every little result is based around the results of hundreds of other little combined programs and it gets to where finding the original source of the problem (the straw that broke the camel's back so to speak) is an epic task. In short, all this happens because there is too much how-to in the program and not enough why should I? OK that's not the only reason: side-effects are a big culprit in debugging disasters; but forget about that for now. 

     If you want a program to think for itself, you have to tell it why you want it to do something, in addition to what you want it to do. This has some amazing benefits. With a small group of declarative descriptions, you can create a whole wide range of behavior. You can even use declarative data to improve imperative instructions. Not only can you tell a computer what you want it to do, and how to do it, in a declarative language you can tell the computer what each item is supposed to be and do and the language can then infer all the different ways that the items can interact with each other, without you ever writing it down. The computer understands why things work the way they do, what they are composed of, and how they can then be combined to achieve a task. As a result: you can write a computer program with way less instructions, you can organize the code into logical chunks, and you can tell the program how to deal with strange circumstances which you didn't expect. The best part of all is that declarative programs (when written well) come together to form one large functioning organism; wherein each chunk of code knows how the neighboring chunk works and what can, can't, and shouldn't be done with it.

     So why isn't everyone using this elegant approach to programming? The issue here is that humans just aren't used to declarative thinking. We already have brains that know why we should and shouldn't do stuff. When we give instructions to other humans, we rely on this fact and only tell them what we want them to do. Occasionally we'll tell them how to do it, but we rarely tell them why. After all, they have brains too, they usually know why we want them to do something. The problem is that computers don't have brains. They only know exactly what you (or someone else) tells them and no more. In short, declarative programming just feels unnatural. That's a big problem for most people. If you want to unleash the power of declarative programming, you have to change the way you think. You have to force yourself to adapt to a different way of viewing the world.

     Finally, how Haskell fits into all this. As you probably already knew, Haskell is a functional programming language. Haskell uses the functional programming paradigm in a way which makes it possible to create a declarative language. It does this by combining a strict and well described set of internal rules along with a very robust static typing system. The static type system in Haskell allows you to describe how you expect a program to function according to the types of data you use it with; instead of by merely issuing a set of rote orders which you expect to be followed blindly. There are a set of declarative rules behind every data-type. There are also many layers of gradually more abstract rules which govern how data-types and primitive operations can interact with each other. Haskell already knows why 1 + 1 = 2 and also why it is stupid to try and find the square root of "bananas". So, if you tell Haskell what every function you create is made of, and what you want it to do, Haskell can then refer to it's list of why and why-not and make decisions about what should and shouldn't occur in your program. In Haskell, the language itself makes decisions. Although this is true in lesser degrees with previous languages, Haskell forces the programmer to describe her program modules in terms which Haskell can then evaluate in logical terms according to rules instead of instructions.

    The core unit of Haskell is the mathematical function. So, instead of creating instructions, you just create functions. Haskell then infers what you are trying to do as well as what you are NOT trying to do. It takes a lot of getting used to; but that's what this book is for.

     Because imperative instructions come so naturally to humans, there can be no better way to introduce you to Haskell than to begin with Input/Output. The reason for this is that Haskell uses an imperative structure for all of it's IO actions. Like so many other features of Haskell, this makes good logical sense. IO is like a conversation between two parties. One party asks a question, waits and (with a little luck and good manners) the other party answers. This is a very linear process. Imperative programs tend to also be pretty linear. So, you get to start Haskell in the imperative realm. But, as you progress, you will see Haskell quickly giving-way to a more functional/declarative architecture. This is good. It allows you to transition into the world of function-think in a slightly less-jarring manner.

     Besides all that, IO is what every program does. The bulk of novice programs you will want to attempt involve some IO (unless you are only using Haskell for mathematics). There are so many cool and creative things you can do once you understand IO; and so many of them require only a novice-level understanding of the fundamental Haskell operations. Educators need to promote enthusiasm about the subject they teach; because enthusiasm brings with it creativity -- and Haskell was made for creative thinkers. Why would anyone start writing a book on Haskell any other way? Oh wait, I know the answer: because it's hard to do. But, you don't need to worry about that: that's my problem. Just sit back and enjoy Chapter 1. Leave the details to me.

     My approach to teaching Haskell is to create a sort of recipe sandwich. I will try and tell you the broad concepts for each task in declarative terms. Then, I will show you  exactly how to accomplish a specific task, then I will show you why the recipe I just gave you works. Stay tuned!

     

     

     

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.