Haskell Tutorial IV : If This is A Case of Functional Programming, Then I am Switching to Lazy Evaluation

Part III: How Haskell Monads are Like a Muppet       Part: V   (coming soon) >

Finally, with the forth part of the tutorial, I get to describe something that will make my PHP-coding reader want to use Haskell:  lazy evaluation.

But first, let’s go back to two different ways I’ve approached the problem of evaluating a keyboard input from a user in our rogue-like RPG game.    In tutorial #2, I created a function called move that used guards to discern what action to take based on a given input.    Here is move:

move :: Char -> String
move a
    |  a == 'w'       = "UP!"
    |  a == 's'       = "DOWN!"
    |  a == 'a'       = "LEFT!"
    |  a == 'd'       = "RIGHT!"
    |  otherwise      = "HANG AROUND AND DO NOTHING!"

In tutorial #3 I switched to a series of if / else if / else statements.

forever (do
    char <- getChar
    if char == 'q' then putStrLn "Thanks for Playing!"
         else if char == 'w' then up y >> output x >> output y
         else if char == 's' then down y >> output x >> output y
         else if char == 'a' then down x >> output x >> output y
         else if char == 'd' then up x >> output x >> output y
         else putStrLn "you did not type a correct entry")

This version had the additional side effect of requiring a manual escape of the compiler program in order to quit (something that is easily fixable, but still).    Now I have a new function ‘dashboard’ that uses lazy evaluation to compute values based on a given input.   Without beating around the bush, here it is:

dashboard :: Char -> MVar Int -> MVar Int -> IO ()
dashboard 'q' _ _ = exit "Thanks for Playing!"    // stop the program
dashboard 'w' x y = up y >> putStr " Coordinate: ( x = " >> output x >> putStr " , y = " >> output y >> putStr ")."
dashboard 's' x y = down y >> putStr " Coordinate: ( x = " >> output x >> putStr ", y = " >> output y >> putStr ")."
dashboard 'a' x y = down x >> putStr " Coordinate: ( x = " >> output x >> putStr ", y = " >> output y >> putStr ")."
dashboard 'd' x y = up x >> putStr " Coordinate:  ( x = " >> output x >> putStr ", y = " >> output y >> putStr ")."
dashboard w x y = putStr " Coordinate: ( x = " >> output x >> putStr ", y = " >> output y >> putStr ")."   // keep the Coordinate values the same

So, let’s think about our problem for a second.    So far, we have been focussed on getting our character to move around a map.    But what if we want to do something completely different, like say show an inventory list if the user hits ‘i’?    Or maybe cast a spell?     We could have a bunch of if – then statements or a whole bunch of guards, but it all could end up being a mess and impossible to read.

Fortunately, Haskell let’s you specify particular actions based on the constructors (entry values) for your function.    In the case above, dashboard accepts 3 values — one CHAR and two MVar Ints (ie. a mutable variable that has to be an integer — see the previous tutorial).   If the dashboard receives a ‘q’ for a value, it will say ‘thanks for playing’ and quit the program (the two ‘_’s mean it doesn’t matter what the values are that follow it).   The next entries do what we’ve come to expect -> change the value of the MVar based on the movement and then output the current coordinate’s status.    The last statement is a catch-all, asking Haskell to output the current spot.

Why might this way of doing things be better than using (say) guards?    Well, the first reason is that it gets to the point.    if the user hits ‘q’, Haskell will not attempt to evaluate any of the other function calls.   Admittedly, this function isn’t really helped performance-wise by lazy evaluation -> but what if moving in a certain direction (in the spot where a monster was) involved a long series of recursive computations.    We would not want Haskell to be using up its resources trying to figure out what it’s supposed to do when all the user wants to do is quit.

However, there’s still alot that could be added here.   What if we want to do something when the user hits ‘s’ and y is equal to zero.    Then we could do something like this:

dashboard 's' 0 y putStrLn "You are blocked and can go no further."  >> putStr " Coordinate:  ( x = " >> output x >> putStr ", y = " >> output y >> putStr ")."

Again, this can have really great performance benefits for your program.    If dashboard gets an ‘s’ and a ‘0’, it will look no further and do the easy job of outputting the coordinate as is, rather than attempting to go through all kinds of irrelevant processes.   UPDATE:  Well, actually, this is all wrong.    Because ‘0’ is not an MVAR.    Mind you, there is a way to get the function to accept two Ints instead and convert the MVAR to an Int for our purposes.    That’ll be another tutorial though.

The last benefit is that it gives a nice human-readable understanding of what happens when the function receives what, without a whole range of complex nested elements.

The downside, of course, is that because Haskell is type-safe, you must give this function a CHAR (not a Maybe Char, or a String) and two MVAR Ints (not an MVAR String or ordinary Int) or you will get a type error.       Things could also get more confusing if you decide that you want to return an IO (String) or other value instead of just outputting something to the screen.    But handling that problem might be for another tutorial.

I should, again, remind you that my up-to-date work on the rogue-like game called rasghiosse is available in a patch-tag repository.   Please feel free to add to it as you wish.

Part I: Is There a Such Thing as Real World Haskell?

Part II:   How not to Start Your Haskell Program >

Here’s a bit of sardonic code, that I’d like to propose to any Haskell advocate out there.

data Works = Works | Does_not
computerApp a ::  Maybe a -> Works
computerApp a
     | isJust a = Works
     | otherwise = Does_not

I have been playing around with functional programming in Haskell.    I have to say that it has more than certainly improved my ability to code in other languages, and probably has reduced the number of bugs I have to fix after the fact.    On the other hand, it has driven me absolutely batty.

To be fair, I need to say that I am not a computer engineer.    I have a BA in English.    My Masters are in Public Administration and Information Management.      I engaged in Haskell code simply as a curiosity and a challenge.    I love math, and became curious about Monads and Lambda calculus.    I am probably not smart enough to be a great Haskell programmer.   However, I do understand two things.    1)  Not-smart-enough people can and want to participate in application development   2)  Coders, while making apps that do what they expect them to do, do not always understand (or care) about the sustainability and/or scalability of their code.

Web Development is an important test case.      Just about anyone, with a reasonable amount of time and effort, can learn to develop a website in PHP, probably supported by some content management system as Drupal or ModX.    Somewhere, their development goes overboard, the system does an upgrade to support some security risk or vulnerability, and ‘pop’ –>  all that likely un-documented and messy code goes nowhere and wheels need to be reinvented.

That’s why learning Haskell is probably a good idea.    Without getting into the code itself, it insists that a function always causes the same result to happen with any given input(s).    Once developed, the documentation pretty much always exists in a minimal form (via Type declarations).    So many bad habits would disappear if only people were forced into developing this way.

The problem, unfortunately, is that Haskell coding is confusing.     There is no popular development framework to use it.     Once you try to apply the examples provided in text books to real world development, things go wonky.    I won’t go into the many reasons why, but I do have an observation based on what I’ve seen in responses from various gurus to newbies like me.

It’s this ->   Users think computers do things.    Computer engineers think computers solve problems.     In Haskell terms, any interaction between users and engineers results in a type error.    Somewhere along the line, an IO() monad needs to be created to turn what engineers like about Haskell into something that users will like about it.

I would like to propose a management framework, similar to extreme programming, to manage the development of functional code for regular people.   While Programming it in Haskell is not a bad start, it uses a problem solving model, rather than a ‘how do you make the software do x’ model.    It focuses on mathematical abstractions rather than simple actions.     For instance, I would like to see a book that uses the development of a rogue-like rpg game in Haskell as an example.    Instead of worrying about efficient computation, abstractions about ‘laziness’ and recursive factorial examples, the writer would have to focus on managing complex (a tuple of a lists of tuples) types, worrying about random numbers and IO issues that are inherent to Haskell.   In approaching such a game, should I worry about creating newtypes first, or work from what I want main to do and fill in the gaps?

But while I make this suggestion, I really have no idea of what kind of advice I can offer your typical new-to-haskell coder.     But I have some hypotheses:

  • work from the main :: IO() first and build a framework of functions to develop your outputs.
  • possibly create type variables for each of your functions, making it equal in Type to a typical output you would like to see.    Then work backwards from there to create a lazy output, then involve possible recursion and so on.
  • use generic types (eg. Int, String, Char etc.) with comments first, then develop types to make your code more clear.
  • unit tests should include the System.IO.Unsafe module (cheating should be allowed when you are testing your code – let the learning happen when you are developing real code)

I’ll add what I can as I continue to learn more about coding in Haskell.    The bottom line is that I think more people should be coding in a language like Haskell, but they are unlikely to work with it if they end up spending a bajillion hours just to get it to choose randomly from a list of monsters (for example).   Especially when they can learn how to do the same in three minutes using an imperative language like Python.

For the greater good and more sustainable code overall, what high-level tips or approaches can you offer any newbie coders of Haskell, so they can develop without becoming absolutely bogged down in failure with their Haskell programs?

UPDATE:   After writing this, I found a great powerpoint tutorial by Graham Hutton that uses Hangman as an example of interactive Haskell code development.