Playing with Functional Programming

The topic will be functional programming with emphasis on Haskell. I'm also writing my own language called Mikan and hope to share progress here. So let's have (fun x -> x + 1)!

Saturday, May 22, 2010

Cabal Installing Haskell LLVM Module on Mac OSX

I got stars in my eyes when I saw the LLVM module for Haskell developed by Bryan O’Sullivan and Lennart Augustsson. Think of the possibilities!

Just as a bit of clarification, there is also a recent buzz around the net related to LLVM and GHC/Haskell due to the LLVM code-gen functionality integrated into GHC by David Terei in his thesis. Although that is definitely very cool, it's different than the LLVM module.

There are a few posts by Bryan and Lennart that discuss the LLVM module here (which I'm listing for completeness and also for my own reference):
Unfortunately, despite my initial excitement, I wasn't able to get the LLVM module to ever install via cabal ... until now that is!

I thought I'd write a post on how I did it in case anyone else is having similar issues.

First, you need to install LLVM. I took some cues from the install guide for unladen swallow (the fast Python implementation on LLVM and using a jit compiler that Google is sponsoring) and also from the LLVM getting started page.

Specifically, I installed LLVM 2.7 and also the gcc front end (not required to my knowledge but looked interesting/useful). Here are the commands I used:

> wget 'http://llvm.org/releases/2.7/llvm-gcc-4.2-2.7-x86_64-apple-darwin10.tgz'
> wget 'http://llvm.org/releases/2.7/llvm-2.7.tgz'
> tar xzf llvm-gcc-4.2-2.7-x86_64-apple-darwin10.tgz

Once you've untarred the gcc binaries, move them to the location you want to install them at. Be careful with just dropping folders onto existing folder hierarchies on Mac -- this has an effect of replacing the target folders as opposed to adding the contents to the existing tree (a discovery learned through tears I can tell you). Continuing:

> tar xzf llvm-2.7.tgz
> mkdir llvm_build
> cd llvm_build
> ../llvm-2.7/configure --prefix={prefix path if not installing to default} --with-llvmgccdir={path to llvm-gcc binaries}

If you're installing to the standard locations and installed the gcc front end in a standard area on the path, you don't need the above configuration flags. I decided to install to a custom location and so required the flags myself.

At this point you are ready to compile LLVM. I had successfully downloaded and built llvm many times before and confirmed the tools worked (llc, lli, etc.). However, I could never succeed in getting the Haskell LLVM module to build. The big breakthrough came from this e-mail which indicates a need to declare the variable "UNIVERSAL" as follows:

> UNIVERSAL=1 gmake -j4

This will take a while to compile. The 4 after the j flag indicates the number of processors to use. Once compilation completes, you need to:

> gmake install
> cabal install llvm

and you're done!

Tuesday, March 23, 2010

Something Fishy in Haskell!

Well, here it is. The amazing! The stupendous! The … erm … fishy! That’s right, it’s “Go Fish!”, the amazing and riveting card game we all played as children! (Suzy, do you have any eights? Nope, GO FISH!)

Welcome to the first in a series of blog post using games to explore functional programming code. In this first code, I’ve programmed up “Go Fish” as a multi-player game. Play takes place on the console in this version and there is currently no computer artificial intelligence (AI) so arguably as a game, this leaves much to be desired. However, it has merit in terms of allowing me to get practice writing functional code. I also thought this might be worthwhile sharing to compare insights … so … here goes!

This will be implemented as a single literate haskell module called “GoFish.lhs”. First, let’s load the required functions from other modules.

> import Random(randomR, StdGen, mkStdGen, newStdGen)
> import Data.Map(Map, fromList, (!), empty, insert, keys)
> import Data.List(foldl', partition, group, sort, sortBy,
>   intercalate)
> import Data.Char(isSpace, toLower)

One thing I like to do is explicitly load functions from other modules. This preference is heavily influenced by Python’s “from import ”. This type of importing makes it crystal clear what functions from what modules are being used. It is also useful when trying to follow someone else’s code (or your own code several months down the road). Any functions not appearing in the above import list are either defined in the module or part of Haskell’s standard Prelude. The Prelude is essentially a module which is always imported and contains the most useful core functions. A listing of what’s in Haskell’s Prelude can be found here.

I’ve been studying F# recently and have grown to love the forward pipe operator as well as the forward composition order. These operators better support a left to right data flow which is how my brain wants to work. It’s totally cool that Haskell has the power to define these useful operators “on the fly”.

As you can see, the pipe operator (|>) is infix left while the forward function composition operator (.>) binds to the right.

> infixl 0 |>
> infixr 9 .>

And here are the definitions:

> (|>) :: a -> (a -> b) -> b
> (|>) x f = f x
> (.>) :: (a -> b) -> (b -> c) -> a -> c
> (.>) f g = g . f

Now, let’s get to the cards! I have a data type for card suit and card value and a final data type just for Cards. I’ve derived several type classes for convenience. If you’re not familiar with these, we get free type class implementations through this mechanism (without having to write code). For example, deriving from the type class, Show, allows us to use the “show” function on the type and turn it into a string.

> data Suit = Spades | Clubs | Hearts | Diamonds
>   deriving (Eq, Show, Enum, Ord)
> data CardValue = Ace | V2 | V3 | V4 | V5 | V6 | V7
>   | V8 | V9 | V10 | Jack | Queen | King
>   deriving (Eq, Enum, Ord)

I’ve implemented a custom value of Show for CardValue in order to get string representations more to my liking for each card value.

> instance Show CardValue where
>   show Ace = "Ace"
>   show V2 = "2"
>   show V3 = "3"
>   show V4 = "4"
>   show V5 = "5"
>   show V6 = "6"
>   show V7 = "7"
>   show V8 = "8"
>   show V9 = "9"
>   show V10 = "10"
>   show Jack = "Jack"
>   show Queen = "Queen"
>   show King = "King"

The following two functions are used to manipulate and “standardize” input strings. Note that in the definition below, dropSpace is written in the so-called “point free” format where arguments are not explicitly shown. Because of the potential confusion, I’ve also chosen to write the type signature. The strip function also demonstrates the usage of my custom “forward pipe” implementation. Thus, to strip white space from a string, we take the string, drop spaces from the front, reverse the string, drop spaces from the front again (which is now the rear since the string is reversed), and finally reverse the string so that it is “right way forward” again.

> strip :: String -> String
> strip s =
>   let dropSpace :: String -> String
>       dropSpace = dropWhile isSpace
>   in  s |> dropSpace |> reverse |> dropSpace |> reverse

The fixStr function takes a string and strips and lower cases it to make comparisons easier.

> fixStr :: String -> String
> fixStr s = s |> strip |> map toLower

The purpose of strToCard is to match potential strings and return card values from that. There is probably a more elegant way to do this using the Read type class but this seemed straight forward enough.

> strToCard :: String -> CardValue
> strToCard s =
>   case fixStr s of
>       "ace" -> Ace
>       "jack" -> Jack
>       "queen" -> Queen
>       "king" -> King
>       "2" -> V2
>       "3" -> V3
>       "4" -> V4
>       "5" -> V5
>       "6" -> V6
>       "7" -> V7
>       "8" -> V8
>       "9" -> V9
>       "10" -> V10
>       otherwise -> error "unknown string"

Finally, the card and a few helper functions:

> data Card = Card CardValue Suit
>   deriving (Eq, Ord)
> value :: Card -> CardValue
> value (Card v _) = v
> suit :: Card -> Suit
> suit (Card _ s) = s
> instance Show Card where
>   show (Card v s) = show v ++ " of " ++ show s
> type Deck = [Card]
> type ShuffledDeck = Deck
> type Hand = Deck
> type Name = String
> type Books = [[Card]]

The rules I looked up on the net for “Go Fish” talked about matching “books” of 4 of the same card value so I’ve used that same terminology here.

A player datatype will contain the player’s hand and any matched books thus far. The functions “hand”, “addCardToHand”, and “books” are convenience functions to make it easier to manipulate the Player datatype.

> data Player = Player Hand Books
>   deriving (Eq, Show)
> hand :: Player -> Hand
> hand (Player h _) = h
> addCardToHand :: Card -> Player -> Player
> addCardToHand c p =
>   let h = hand p
>       h' = c : h
>       bks = books p
>   in  Player h' bks
> books :: Player -> Books
> books (Player _ bs) = bs

Finally, a few more convenient type synonyms:

> type PlayerMap = Map Name Player
> type Message = String
> type PlayerNames = [Name]
> type PlayerState = (Bool, Message, PlayerMap)
> data GameState = ContinueTurn | NextPlayer
>   | GameOver deriving (Eq, Show)

Below, I’m using a Haskell list comprehension to build a pack of cards. Because I had the CardValue inherit from Enum (Enumeration), we get these great functions “enumFrom” which allow me to iterate through the potential (finite) card values and suits without having to write any custom code. Brilliant!

> deck :: Deck
> deck =
>   [Card v s |
>       v <- enumFrom Ace,
>       s <- enumFrom Spades]

Pop is a convenience function used in the shuffle function. Can you guess what shuffle does? Actually, what might not be immediately apparent is the method Haskell uses to perform random number generation. A standard random number generator (StdGen) type is needed. The variable “rng” stands for “random number generator”.

> -- Return list with item at idx removed
> --    idx must be one-based (i.e., first item is 1)
> pop :: Int -> [a] -> (a, [a])
> pop idx xs =
>   if null xs
>       then error "cannot pop a null list"
>       else
>           let len = length xs
>               n = idx `mod` (len + 1)
>           in  (xs !! (n - 1), 
>                   take (n - 1) xs ++ drop n xs)
> shuffle :: StdGen -> Deck -> (ShuffledDeck, StdGen)
> shuffle rng d = 
>   let iterPick rng d shuffled =
>           if null d
>               then (shuffled, rng)
>               else
>                   let (n, rng') = 
>                           randomR (1, length d) rng
>                       (nextCard, d') = pop n d
>                   in  iterPick rng' d' $
>                           nextCard : shuffled
>   in  iterPick rng deck []

Deal takes a deck of cards and deals five to each player.

> numCardsToDeal :: Int
> numCardsToDeal = 5
> deal :: Deck -> [Name] -> (Deck, PlayerMap)
> deal deck_ names =
>   let f (d, pm) n = 
>           (drop numCardsToDeal d,
>               insert n
>                   (Player (take numCardsToDeal d) [])
>                   pm)
>   in  foldl' f (deck_, empty) names

The function “groupBy” is actually a standard function available from Data.List. However, it didn’t do what I wanted it to. Specifically, I was hoping groupBy would group all like terms in a list together. Instead, it groups like “continuous” terms together. In other words, group by will only keep grouping until it runs into something outside of the group, then it will start a new group. The “groupBy’” function I wrote below has the desired effect which is to group all terms in the list that meet a criterion together.

> -- a different form of groupBy using partition
> groupBy' :: (a -> a -> Bool) -> [a] -> [[a]]
> groupBy' f [] = []
> groupBy' f (x:xs) =
>   let (matched, unmatched) = partition (f x) xs
>       gr = x : matched
>   in  if null unmatched
>           then [gr]
>           else [gr] ++ groupBy' f unmatched

The functions coming up below are used to perform specific game actions during the “Go Fish” card game turn. The function “askCards” simulates a player asking another player if they have any of a specific card value.

> -- Asker checks if Target Player has any cards of a Certain Rank
> askCards ::
>   Name -> CardValue -> Name -> PlayerMap -> PlayerState
> askCards asker val target pm =
>   let askingPlayer = pm ! asker
>       targetPlayer = pm ! target
>       hAsker = hand askingPlayer
>       hTarget = hand targetPlayer
>       matchingCards =
>           [Card val s | s <- enumFrom Spades]
>       (matched, hTarget') =
>           partition (`elem` matchingCards) hTarget
>       targetHadCards = length matched > 0
>       msg = if targetHadCards
>           then
>               let numCards = length matched
>                   plural = if numCards > 1
>                       then "s"
>                       else ""
>               in  asker ++ " got " ++ show numCards
>                       ++ " " ++ show val ++ plural
>                       ++ " from " ++ target
>                       ++ " ... Continue!"
>           else
>               target ++ " doesn't have any "
>                   ++ show val ++ "s, " ++ "Go Fish!"
>       askingPlayer' =
>           let hnd = hAsker ++ matched
>               bks = books askingPlayer
>           in  Player hnd bks
>       targetPlayer' =
>           Player hTarget' (books targetPlayer)
>       pm' = pm 
>             |> insert asker askingPlayer'
>             |> insert target targetPlayer'
>   in  (targetHadCards, msg, pm')

The function “getValidResponse” is used to continually prompt the user until a valid string is returned.

> getValidatedResponse :: 
>   (String -> Bool) -> String -> String -> IO String
> getValidatedResponse f prompt onFail = do
>   putStrLn prompt
>   response <- getLine
>   if f response
>       then 
>           return $ strip response
>       else do
>           putStrLn onFail
>           getValidatedResponse f prompt onFail

The function “checkForBooks” is used to check if a player has a matching set of four of one card value in their hand.

> checkForBooks :: Player -> (Bool, Message, Player)
> checkForBooks p =
>   let cs = hand p
>       valGrs =
>           let f = \c1 c2 -> (value c1) == (value c2)
>           in  groupBy' f cs
>       grLens = map length valGrs
>       lenVals = zip grLens valGrs -- [(Int, [Card])]
>       (bks, remainder) =
>           partition (\(len, _) -> len == 4) lenVals
>       bks' = map snd bks
>       bkVals = map (\cs -> cs |> head |> value) bks'
>       remainder' = map snd remainder
>       anyBks = length bks' > 0
>       msg =
>           if anyBks
>               then "You've matched " ++ 
>                       show (length bkVals) ++ " books: "
>                       ++ show bkVals
>               else "No books matched..."
>   in  (anyBks, msg,
>           Player (concat remainder') (bks' ++ books p))

The function unique groups all in a given list together and takes the head of each group so that there is only one of each kind of element in the list.

> unique :: (Eq a) => [a] -> [a]
> unique xs = xs |> group |> map head

The “announceWinner” helper function counts the number of books matched by each player to yield the final scores.

> announceWinner :: PlayerMap -> IO ()
> announceWinner pm = do
>     let ks = keys pm
>         nBks = map (pm !) ks |> map books |> map length
>         score = zip ks nBks
>     putStrLn $ "Final score:\n\t" ++ show score

The following two convenience functions prompt the active player as to which other player they should target to trade cards and prompt the active player to ask what kind of card they want.

> askForTarget :: Name -> [Name] -> IO Name
> askForTarget playerName validNames = do
>   let msg = "* Who will you ask for cards? "
>           ++ (show validNames)
>       errMsg = "... that cat doesn't exist, try again"
>   getValidatedResponse (`elem` validNames) msg errMsg
> askForValue :: Name -> [Name] -> IO Name
> askForValue target validRanks = do
>   let msg = "* What will you ask " ++ target
>           ++ " for? " ++ show validRanks
>       errMsg = "...Not a valid rank or you don't hold\
>               \ that card"
>   getValidatedResponse (`elem` validRanks) msg errMsg

The function isGameOver checks if conditions for Game Over have been reached: namely, if a player has matched all the cards in their hand.

> isGameOver :: Name -> PlayerMap -> Bool
> isGameOver playerName pm =
>     if (playerName |> (pm !) |> hand |> length) == 0
>         then True
>         else False

The function “goFish” simulates having to fish for a card out of the main deck.

> goFish :: 
>   Name -> Deck -> CardValue -> PlayerMap ->
>       (GameState, Message, Deck, PlayerMap)
> goFish playerName playDeck val pm =
>   let playDeck' = tail playDeck
>       fishedCard = head playDeck
>       player = 
>           addCardToHand fishedCard (pm ! playerName)
>       (hadBooks, bkMsg, player') = checkForBooks player
>       pm' = insert playerName player' pm
>   in  if val == (value fishedCard)
>           then -- fished card is what you wanted!
>               let msg = "\nYou caught a "
>                       ++ show fishedCard
>                       ++ ", just what you were fishing "
>                       ++ "for!\n" ++ bkMsg
>               in  if isGameOver playerName pm'
>                       then (GameOver, msg,
>                               playDeck', pm')
>                       else (ContinueTurn, msg,
>                               playDeck', pm')
>           else -- fished card wasn't what you wanted!
>               let msg = "\nYou caught a " ++
>                       show fishedCard ++
>                       ", not what you wanted..." ++
>                       "\nNext cat up!"
>               in  (NextPlayer, msg, playDeck', pm')

The function “allHands” is a convenient debugger function used to show the value of all player’s hands. It should only be used for debugging purposes.

> allHands :: PlayerMap -> IO ()
> allHands pm = do
>   let ks = keys pm
>       ps = map (pm !) ks
>       vs = map (hand .> map value .> sort) ps
>       sortFun = \(k1, _) (k2, _) -> compare k1 k2
>       kvs = sortBy sortFun $ zip ks vs
>   putStrLn $ show kvs

Finally, “play’”, “play”, “playerNames”, and “main” are all used to implement the actual game and set up useful defaults so one can just get started without having to define players.

> play' :: PlayerNames -> Deck -> PlayerMap -> IO ()
> play' ns playDeck pm = do
>   let playerName = head ns
>       validNames = tail $ ns
>       yourHand = playerName |> (pm !) |> hand |> sort
>       yourRanks = map value yourHand
>       validRanks = yourRanks |> unique |> map show
>       hbar = take 40 $ repeat '-'
>   putStrLn $ hbar ++ "\nCurrent Cat: " ++ playerName
>       ++ "\n" ++ hbar
>   putStrLn $ "Your Hand: "
>       ++ (intercalate ", " $ map show yourRanks)
>   -- allHands pm
>   target <- askForTarget playerName validNames
>   valStr <- askForValue target validRanks
>   let val = strToCard valStr
>       (gotCardFromTarget, msg, pm') =
>           askCards playerName val target pm
>       nextPlayers =
>           target : filter (\n -> n /= target) ns
>   -- allHands pm'
>   putStrLn msg
>   if gotCardFromTarget
>       then do -- got desired card from target
>           -- something seems wrong here:
>           let (hadBooks, bkMsg, player) =
>                   checkForBooks (pm' ! playerName)
>               pm2 = insert playerName player pm'
>           -- allHands pm2
>           putStrLn bkMsg
>           if (player |> hand |> length) == 0
>               then announceWinner pm2
>               else play' ns playDeck pm2
>       else -- GoFish!
>           if null playDeck
>               then play' nextPlayers playDeck pm'
>               else
>                   let (gameState, msg, playDeck', pm2) =
>                           goFish playerName playDeck
>                               val pm'
>                   in  case gameState of
>                           GameOver -> do
>                               putStrLn msg
>                               -- allHands pm2
>                               announceWinner pm2
>                           ContinueTurn -> do
>                               putStrLn msg
>                               -- allHands pm2
>                               play' ns playDeck' pm2
>                           NextPlayer -> do
>                               putStrLn msg
>                               -- allHands pm2
>                               play' nextPlayers
>                                   playDeck' pm2
> play :: PlayerNames -> IO ()
> play ns = do
>     rng <- newStdGen
>     let ns' = map strip ns
>         (shuffledDeck, rng') = shuffle rng deck
>         (playDeck, pm) = deal shuffledDeck ns'
>     play' ns' playDeck pm

As you read my blog, you’ll probably learn to expect this. It turns out that the players are all cats (except for Michael). Come now, haven’t you wanted to play go fish with cats?

> playerNames :: [Name]
> playerNames = ["Moonlight", "Mew", "Michael", "Cali"]

From ghci, you can merely type “main” to start the program and try this out!

> main :: IO ()
> main = do
>     play playerNames

Sunday, March 21, 2010

The New Haskell Platform is Out!

I was excited to see today that the new Haskell Platform containing version 6.12.1 of the (Glorious) Glasgow Haskell Compiler (GHC). And it truly is glorious since GHC now supports unicode IO!

Anyhow, without further ado, download it here!

Monday, March 16, 2009

Has this happened to you...?

OK, so this is twice now in one week, so I'm curious if this happens to anyone else:

A: "Hey, man, I'm learning a new computer language called Haskell!"
B: "Really? That's interesting because my company was just looking into using a new software package written in Pascal."
A: "Um, no not Pascal. Haskell. Haskell with an 'H'..."
B: "Oh... never heard of it."

The other fun one I get is:

A: "Hey, I'm learning about functional programming!"
B: "Functions? Oh yeah, I use functions. They're great!"
A: "..."

I think we still need more "rah rah rah" on the functional programming and Haskell advocacy side... On a positive note, maybe Pascal is coming back. (That is positive, right?)

Saturday, February 28, 2009

A More Elegant Solution for "Keyword Arguments" in Haskell?

In my last post, I showed a way to use "dummy" types (a "description type" called Desc) to annotate a function's arguments similar to the keyword arguments used in Python.

However, as I began to think about it more, I began to wonder if I was missing the point of the wonderful elegance of the Haskell type system. Putting it another way, I think there's a more elegant solution than the one proposed in my last post:
Haskell Code:

-- ClarityTake2.hs
type City = String

sanFrancisco :: City
sanFrancisco = "San Francisco, CA"

denver :: City
denver = "Denver, CO"

type CatName = String

moonlight :: CatName
moonlight = "Moonlight"

data From a = From a deriving (Show)

data To a = To a deriving (Show)

data Who a = Who a deriving (Show)

fly :: Who CatName -> From City -> To City -> String
fly (Who n) (From c1) (To c2) = n ++ " flies from " ++ c1 ++ " to " ++ c2

data Travel t1_who t2_place = Travel {
who :: t1_who,
from :: t2_place,
to :: t2_place
}

fly2 :: Travel CatName City -> String
fly2 t = (who t) ++ " flies from " ++ (from t) ++ " to " ++ (to t)

main :: IO ()
main = do
putStrLn $ fly (Who moonlight) (From denver) (To sanFrancisco)
putStrLn $ fly2 (Travel {who=moonlight, from=denver, to=sanFrancisco})
-- main =>
-- Moonlight flies from Denver, CO to San Francisco, CA
-- Moonlight flies from Denver, CO to San Francisco, CA

So in the code above, we use the type system itself to make it clear what we're trying to do. Why does this or should this work? Well, if one is using the excellent ghci (Glasgow Haskell Compiler interpreter), then one only needs to type ":i " and you can get a nice type signature back. Consider the following interactive ghci session:

GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Prelude> :l ./ClarityTake2.hs
[1 of 1] Compiling Main ( ClarityTake2.hs, interpreted )
Ok, modules loaded: Main.
*Main> :i fly
fly :: Who CatName -> From City -> To City -> String
-- Defined at ClarityTake2.hs:21:0-2
*Main> :i fly2
fly2 :: Travel CatName City -> String
-- Defined at ClarityTake2.hs:30:0-3
*Main> :i Travel
data Travel t1_who t2_place
= Travel {who :: t1_who, from :: t2_place, to :: t2_place}
-- Defined at ClarityTake2.hs:23:5-10
*Main> putStrLn $ fly2 (Travel {to="New York, NY", from=denver, who="Mittens"})
Mittens flies from Denver, CO to New York, NY

In the interactive session above, we can use the "info" command (:i ) to check what the type signature of a given function is. If we write our types with clarity, it then becomes obvious how we should use the function. So for example, with the function "fly", we can see we need to provide a "Who" type with CatName, a "From City" and a "To City".

Unfortunately, however, although the type names have been clearer, we still have to remember argument order if we just use plain types. This is where the record syntax comes to the rescue. The "Travel t1 t2" type holds all of the function arguments which can be obtained using the accessor functions "who", "from", and "to".

By doing a little mental gymnastics piecing together the ":i Travel" and ":i fly2" information, we can see that Travel defines "who" as a "CatName" (standing in for t1_who) and "from"/"to" as type "City" (standing in for "t2_place"). The record arrays give then give us the flexibility to define arguments in whatever order we desire. As a last example, let's look at the last call to fly2 in the ghci interactive session above. Here, Mittens (another super cat) will fly from Denver to New York. However, we were able to call the function with arguments in a different order! This could be handy.

The nice thing about both solutions (Record syntax and descriptive type constructors like "From a" and "To a") is that the code readability is greatly enhanced. One can now come upon a piece of code like the following, and quickly parse out what it's doing just by reading it and that's nice:
  • drive (Who michael) (From denver) (To ftCollins) -- descriptive type constructors
  • drive (Travel {who=michael, from=denver, to=ftCollins}) -- record syntax
Cheers!

Friday, February 27, 2009

Key(word)s to success!

One of the things I've always loved about Python is the keyword arguments. I just love it when code is self-documenting. Keyword arguments in Python seem to make it clearer what's going on and allow the function caller to use a function without fear of transposing argument order. For example, here are two calls to the same function, one without keyword arguments and the next with:
  • c = Cylinder(10.0, 20.0)
  • c = Cylinder(radius_cm=10.0, height_cm=20.0)
(Note: the units of radius and height are in centimeters or cm).

To dig into this a bit more, let's consider the following problem. We have to get Moonlight, a super-hero cat, from the city he's currently in, Denver, to San Francisco, where a kitten is stuck up a tree. Let's say we have a function to help us out:
  • fly -- takes a cat to a destination city from a starting city
Let's say Moonlight is going to have to fly to San Francisco from Denver to perform the rescue -- and time is of the essence! OK! So let's call our fly function:

fly(moonlight, sanFrancisco, denver)

But wait a second, which city goes first when we call fly? Is it the destination city? Or the start city? Ooh, I don't remember... But no fear! Keyword arguments to the rescue!
python code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

sanFrancisco = "San Francisco, CA"
denver = "Denver, CO"
moonlight = "Moonlight"

def fly(who, to, from_):
return "%s flies to %s from %s"%(who, to, from_)

print(fly(who=moonlight, from_=denver, to=sanFrancisco))
# =>
# Moonlight flies to San Francisco, CA from Denver, CO

Note in the code above, we have to use "from_" instead of "from" because "from" is a reserved word that is part of the syntax of the Python language. When we call the fly function, we can include keywords or not to our liking (so long as a non-keyword argument does not come after a keyword argument).

Let's implement a similar sort of behavior in Haskell:
Haskell code:

data Dscr = Who | To | From deriving (Show, Eq)

type City = String

sanFrancisco :: City
sanFrancisco = "San Francisco, CA"

denver :: City
denver = "Denver, CO"

type CatName = String

moonlight :: CatName
moonlight = "Moonlight"

fly :: Dscr -> CatName -> Dscr -> City -> Dscr -> City -> String
fly Who n From c1 To c2 = n ++ " flies from " ++ c1 ++ " to " ++ c2
fly Who n To c2 From c1 = fly Who n From c1 To c2

main :: IO ()
main = do
putStrLn $ fly Who moonlight To sanFrancisco From denver
-- main =>
-- Moonlight flies from Denver, CO to San Francisco, CA

This is only a toy example and I haven't used anything like this in production code but it seems like it could be a nice way to describe a function call. This piece of code also demonstrates a limited ability to flip arguments around (the From and To arguments can be transposed as demonstrated in the function calls) although note that one has to program the combinations manually.

At any rate, this kind of coding style does come in handy when you have to send your super cat off to rescue kittens in San Francisco on short notice... Cheers!

Sunday, February 22, 2009

Counting Cats and Listing Snakes

In today's installment, I thought I'd explore how Haskell lists would look if they were implemented in Python. Per the spirit of this blog, it's my intention to take ideas in Haskell and put them in Python and vice verse in an attempt to better understand functional programming.

Python has a lot of nice tools for the functional programming paradigm -- there's a nice overview from A.M. Kuchling on the Python website here. I'm curious if Haskell enthusiasts reading this functional programming overview experience a few "cringe points" (I did). Even so, all-in-all I thought this is a very nice document showing the power of Python to implement programs in the functional style. That being said, there's definitely a "middle-path" feel to Python where pure functional or object-oriented concepts are sacrificed in the name of "the greater good" (i.e., clarity and practicality). This is perhaps why Python is so successful -- it takes the middle path...

But anyhow, back to the task at hand: how would Haskell linked lists look if implemented in Python? Also, a few posts back, I explored how one could combine "add as addition" (Haskell operator "+") and "add as append" (Haskell operator "++") into one function in Haskell (similar to how Python uses the "+" operator for numbers and for lists, respectively). However, we never did the reverse for Python. If we implemented our own custom numbers and lists in Python and then wanted to implement "add as addition" for numbers and "add as append" for lists both using the (+) operator, what would this look like? Note that this is what the "+" operator does normally in Python. It would be even nice to define the Haskell "+" and "++" in Python but unfortunately, we can't define a "++" operator in Python to my knowledge.

OK, so let's define some number type, Num, that we can perform addition with:
class Num:
"""A number"""
def __init__(self, val):
self._val = val

def __add__(self, other):
return Num(self._val + other._val)

def __str__(self):
return str(self._val)

The method "__add__" is a convention in Python that gets called when "+" is applied to an instance of the user-defined type (a.k.a., class) defining it. In our case, we'll only be adding Nums to each other so I won't worry about type conversion, mixing numerical types, etc. Similarly, the "__str__" function is called when str(inst) is called where inst is an instance of the user-defined type that defines the __str__ method.

We can exercise instances of the Num type as follows:

n1 = Num(1)
n2 = Num(2)
n3 = n1 + n2
print(n3)
#---------
# => 3

Now to the linked list. Let's consider that we have a list of instances of type Cat (yup, you heard me right. We're talking about our cute furry friends with whiskers). A linked list is a data structure that holds a reference to the head of the list and the tail of the list which itself is a list -- the rest of the cats (i.e., the whole kit and caboodle).

We will signify that we're at the end of our list by using Python's built in instance, None (an instance of NoneType).
class LinkedList:
"""A list"""
def __init__(self, head, tail):
self._head = head
self._tail = tail or None

def __add__(self, other):
"""Addition as appending"""
if self._tail is not None:
return LinkedList(self.head(), 
LinkedList(self._tail.head(),
self._tail.tail()) + other)
else:
return LinkedList(self._head, other)

def head(self):
return self._head

def tail(self):
return self._tail

def __str__(self):
return "%s:%s"%(str(self._head), str(self._tail))

def __len__(self):
if self._head is not None:
if self._tail is not None:
return 1 + len(
LinkedList(self._tail.head(), self._tail.tail()))
else:
return 1
else:
return 0

The "tail or None" bit in the code above can be thought of as an application of the "or" function. If tail comes in as something that evaluates to false (like "[]", the empty list), we're going to use "None" instead. I could have used "[]" instead of "None", I just need to be consistent to identify the end of the list. (Note that we can get tripped up here if someone threw in "False" for example. This code is not meant to be production, just a learning exercise).

For convenience, we'll define a function, listToLinkedList, that takes a Python list and returns an instance of type LinkedList.
def listToLinkedList(lst):
"""Takes a Python indexable object to a List object"""
if len(lst) > 0:
return LinkedList(lst[0], listToLinkedList(lst[1:]))
else:
return None

And just so we have something interesting to list, let's define a new user defined type -- cats!
class Cat:
def __init__(self, name):
self.name = name

def __str__(self):
return "Cat %s"%repr(self.name)

def __repr__(self):
return "Cat(%s)"%repr(self.name)

The __repr__ is a special function (called with repr(instance)) that returns the string representation of an object. The string function is meant to be human readable while the repr function is more machine readable. If you'd like to read more about it, check the Python docs here.

And now to exercise the code:
listOfCatsOne = listToLinkedList([Cat("Mittens"), Cat("Nippers"), Cat("Bob")])
print(listOfCatsOne)
# => Cat 'Mittens':Cat 'Nippers':Cat 'Bob':None

I've also implemented a LinkedList function, len, to count the number of members of the list. This is created via recursion.

Let's exercise it:
print("There are %d cats in list one\n"%len(listOfCatsOne))
# =>
# There are 3 cats in list one

Let's say we have a list of three kittens (Mittens, Nippers, and Bob) and we want three more cats (O'Mally, Nosey, and Fuzzbee) to join them at the end of the line (i.e., we want to append the LinkedList of O'Mally and others to the end of the LinkedList of Mittens and others). This will look as follows:



So in the upper part of the image, we can see the structure of a linked list. A linked list consists of a head containing Mittens and a tail consisting of the linked-list with a head of Nippers and a tail consisting of a linked-list with a head of Bob and a tail consisting of the empty list. The "empty list" is a special flag of sorts that lets us know the end of the list has been reached. Cool, huh?

When we append lists (show at the bottom part of the figure), we create a new list structure consisting of each element of the original list successively appended until we hit the end of list one (i.e., the empty list) and in place of the "empty list" we pop in the second list (which ends in an "empty list").

And the code:
listOfCatsTwo = listToLinkedList([Cat("O'Mally"), Cat("Nosey"), Cat("Fuzzbee")])
listOfCatsThree = listOfCatsOne + listOfCatsTwo
print(listOfCatsThree)
# =>
# Cat 'Mittens':Cat 'Nippers':Cat 'Bob':Cat "O'Mally":Cat 'Nosey':Cat 'Fuzzbee':None

Yippee! So now we can append lists of cats to each other and count them up! In Haskell, we can create our own linked list type to demonstrate the same effect.
#!/opt/local/bin/runhaskell
module LinkedList where

import Text.Printf (printf)

type Name = String

data Cat = Cat Name

data List a = Cons a (List a) | Nil

instance Show Cat where
show (Cat n) = "Cat " ++ (show n)

instance (Show a) => Show (List a) where
show (Cons x xs) = (show x) ++ ":" ++ (show xs)
show Nil = "[]"

(+++) :: (List a) -> (List a) -> (List a)
(+++) Nil ys = ys
(+++) (Cons x Nil) ys = Cons x ys
(+++) (Cons x1 (Cons x2 xs)) ys = Cons x1 $ (Cons x2 xs) +++ ys

len :: (List a) -> Int
len Nil = 0
len (Cons x xs) = 1 + len xs

main :: IO ()
main = do
let catListOne = Cons (Cat "Mittens") $ Cons (Cat "Nippers") $ Cons (Cat "Bob") Nil
let catListTwo = Cons (Cat "O'Mally") $ Cons (Cat "Nosey") $ Cons (Cat "Fuzzbee") Nil
print (catListOne +++ catListTwo)
printf "There are %d cats in list one\n" (len catListOne)
-- =>
-- Cat "Mittens":Cat "Nippers":Cat "Bob":Cat "O'Mally":Cat "Nosey":Cat "Fuzzbee":[]
-- There are 3 cats in list one

As you can see, we get the same result. Note that all of this functionality is built into Haskell and could be written in a more concise format:
main2 :: IO ()
main2 = do
let catListOne = [Cat "Mittens", Cat "Nippers", Cat "Bob"]
let catListTwo = [Cat "O'Mally", Cat "Nosey", Cat "Fuzzbee"]
print (catListOne ++ catListTwo)
printf "There are %d cats in list one" (length catListOne)
-- =>
-- [Cat "Mittens",Cat "Nippers",Cat "Bob",Cat "O'Mally",Cat "Nosey",Cat "Fuzzbee"]
-- There are 3 cats in list one

Similarly, the append function of Python is also pre-defined and could have been written as:
listOfCatsFour = (Cat("Mittens"), Cat("Nippers"), Cat("Bob"))
listOfCatsFive = (Cat("O'Mally"), Cat("Nosey"), Cat("Fuzzbee"))
print(listOfCatsFour + listOfCatsFive)
print("There are %d cats in lists four and five"%(len(listOfCatsFour + listOfCatsFive)))
# =>
# (Cat('Mittens'), Cat('Nippers'), Cat('Bob'), Cat("O'Mally"), Cat('Nosey'), Cat('Fuzzbee'))
# There are 6 cats in lists four and five

So to summarize, as a means of better understanding Haskell's lists, I've implemented them in Python (with cats!) as well as (re-)implemented them in Haskell (with more cats!). So what have we learned? First, everything is better with cats!

But seriously, this exercise was aimed at bringing more clarity to what Haskell LinkedLists do. Since Python is easy to read and understand, re-writing Haskell in Python can sometimes bring further clarity to what's going on (at least if you come from an imperative programming language background like me). So for what it's worth, there it is! Meow!

About Me

My Photo
Michael O'Keefe
A comic artist, sometimes programmer, and engineer in the field of renewable energy.
View my complete profile

Tags

© 2009, 2010 Michael Patrick O'Keefe, all rights reserved

Interesting Shtuff

Followers