keskiviikko 15. kesäkuuta 2011

Rx for Haskell - My First Monad

A while a ago I hacked quite a lot on Reactive Extensions for Javascript, which resulted in writing a simple game Worzone and posting a bunch of blog posts. Lately I've been more interested in learning Haskell.
A few days ago I decided to try to write a version of Rx in Haskell, and that's what I'm going to tell you about in this post. In the following, I expect you to understand some Haskell and to know the basic concepts of Reactive Extensions.
Briefly, Rx is a framework for reactive programming. The main concept there if Observable, which is a source of events that you can subscribe to. The thing that makes Rx interesting is the concept of Combinators. For instance, in Rx for Javascript, you can create Observables for click events of HTML buttons. In the following example (Stolen from Joni Freemans's learn-rx slide, thanks) these clicks are first mapped in to integers +1 and -1 using the Select combinator, then merged into a single Observable usingMerge, then converted into a sum using Scan. Finally, a side-effect is added using Subscribe: a label is updated so that it will display a counter that can be increased and decreased by pressing the + and - buttons.
$(function() {
  function always(x) { return function(_) { return x }}

  var incr = $('#incr').toObservable('click').Select(always(1))
  var decr = $('#decr').toObservable('click').Select(always(-1))

  incr.Merge(decr)
    .Scan(0, function(total, x) { return total + x })
    .Subscribe(updateCount)

  function updateCount(total) {
    $('#count').html(total)
  }
})
Please read some of Matt Podwysocki's postings on Rx, such as this one. I've also written some rants about Rx on my blog, such as this one

Rx with typeclasses

So, with my background in OOP, I began by declaring a typeclass for Observable. Like this:
class Observable a observable where
  subscribe :: observable -> Observer a -> IO Disposable

type Observer a = (a -> IO ())
type Disposable = IO ()
type Subscribe a = (Observer a -> IO Disposable)
I had the idea that with the Observable typeclass it should be easy to make your own Observables just by declaring an instance for them. So, I wrote an instance for List:
instance Observable a ([a]) where
  subscribe list observer = do
    mapM observer list
    return (return ())
And in GHCI:
Rx> subscribe ["a", "b"] putStrLn
a
b
So, I can subscribe an Observer (here the putStrLn function) to an array. Promising start. Note that I needed to turn on some GHC flags to allow stuff like multiparameter typeclasses and making instances for arrays. So, in the beginning of my module, I had this:
{-# LANGUAGE MultiParamTypeClasses,FlexibleInstances,TypeSynonymInstances #-}
module Rx where
import Control.Monad
Next, let's write some combinators.. Like
select :: Observable a a' => (a -> b) -> a' -> Subscribe b 
select func observable = (\ observerB -> subscribe observable (convert observerB))
  where convert observerB = observerB . func
This is supposed to convert an Observable a into an Observable b using a given mapping function. Should work, right? However,
*Combinators> subscribe (select show [1, 2, 3]) putStrLn
<interactive>:1:11:
    No instance for (Observable a [t])
      arising from a use of `select' at <interactive>:1:11-31
    Possible fix: add an instance declaration for (Observable a [t])
    In the first argument of `subscribe', namely
        `(select show [1, 2, 3])'
    In the expression: subscribe (select show [1, 2, 3]) putStrLn
    In the definition of `it':
        it = subscribe (select show [1, 2, 3]) putStrLn
This is where I got stuck for quite a while. It should be just fine, I reasoned:
  • [1, 2, 3] has an instance for Observable Int
  • (select show [1, 2, 3]) should be Observable String, because show maps Int to String Still, it does not compile. It took quite a while to figure out what's wrong. See:
*Combinators> let selectSpecific = select :: (Int -> String) -> [Int] -> Subscribe String
*Combinators> subscribe (selectSpecific show [1, 2, 3]) putStrLn
1
2
3

Enter simplistic approach

It seems that my example compiles and runs if I just add some type annotations. For me this seems like the type inference system in Haskell (GHC) is lacking. It's of course more probable that I just don't get it :) Anyways, having to annotate the code using this "API" is unacceptable, so I decided to try to do this without the typeclass:
type Observer a = (a -> IO ())
type Disposable = IO ()
type Subscribe a = (Observer a -> IO Disposable)

observableList :: [a] -> Subscribe a
observableList list observer = do
    mapM observer list 
    return (return ())
And then some combinators:
select :: (a -> b) -> (Subscribe a) -> Observer b -> IO Disposable
select convert subscribe observer = subscribe (observer . convert)

filter :: (a -> Bool) -> (Subscribe a) -> Observer a -> IO Disposable
filter predicate subscribe observer = subscribe filteredObserver
  where filteredObserver a = if (predicate a) then (observer a) else return ()
For me, this seems a bit less elegant, as I have to explicitly say observableList to be able to subscribe to a list. But the upside is that it works and does not require type annotations in the client code:
*Combinators> select show (Combinators.filter even $ observableList [1, 2]) putStrLn
2

Monads Gonads

Now that's convenient. But, as Joni has convinced me, Observables are actually Monads and Functors too, so I wanted to make an instance for each. Like
instance Functor Observable where
  fmap = select
But alas again, won't compile. I twiddled around quite a while again only to come into the conclusion that I cannot make the Observer (which is just a subscribe function really) an instance of MonadFunctor or anything. So, I had to convert it into a data:
data Observable a = Observable {subscribe :: Subscribe a}
type Observer a = (a -> IO ())
type Subscribe a = (Observer a -> IO Disposable)
type Disposable = IO ()
Then, having refactored my select and filter combinators to operate on this new definition of Observable and writing the selectMany combinator, I'm finally able to proudly declare:
instance Functor Observable where
  fmap = select

instance Monad Observable where
  return a = observableList [a]
  (>>=) = selectMany
Good shit! I don't have a very good idea what I'll gain by Monads and Functors, but I'm going to find out. To get back to the original one-liner for printing list items into the console, here's how it's done with the "final" solution:
*Rx> subscribe (observableList ["a", "b"]) putStrLn
1
2
3
And, here's an example with the select and filter combinators:
*Rx> subscribe (select show (Rx.filter even $ observableList [1, 2])) putStrLn
2
Pls find the source code in Github.

lauantai 23. huhtikuuta 2011

Strings attached

Working with strings in Haskell is initially a great source of confusion. At first one learns that

    type String = [Char]

Aha! That's pretty elegant. String is just a type alias for a list of characters. Therefore all the familiar functions for a list work for a String too (e.g. Data.List).

So far so good.

The confusion starts when encountering an API which does not use a String, but a ByteString. Turns out, a plain String is not very efficient. It is a linked list internally and takes up more memory than an optimized structure. Also, some common operations are slow. Linked list has O(1) head and tail, but most of the operations are O(n), including getting a character at a specific index.

ByteString is an optimized String, internally a byte array. It provides a fast random access among other optimizations.

The flavors of ByteString

ByteString is a rather more sophisticated type as a String. First of all, it supports various encodings. Note, qualified imports are often used with these. Many function names conflict with functions from Prelude.

    import qualified Data.ByteString as B

This ByteString uses Word8 arrays. It is good for binary IO.

    import qualified Data.ByteString.Char8 as B

This ByteString uses Latin-1 Char8 arrays. It is good for non-internationalized text.

    import qualified Data.ByteString.UTF8 as B

This ByteString uses UTF8 encoded arrays. It is good for internationalized text.

Perhaps a bit more esoteric aspect of ByteString is, all those come as strict and lazy versions. Strict here means the ByteString is fully loaded into memory. Lazy ByteString loads the backing array elements to memory as needed, in buffered chunks.

    import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Lazy.Char8 as B
import qualified Data.ByteString.Lazy.UTF8 as B

This has to do again with optimizations. Strict version is good for smaller strings. Random access is fast for arrays fully loaded into memory. Lazy version can represent huge strings without exceeding the memory limits.

ByteString can be converted to a byte array (binary IO) or String (text IO), and vice versa.

    > :m Data.ByteString
> :t pack
pack :: [GHC.Word.Word8] -> ByteString
> :t unpack
unpack :: ByteString -> [GHC.Word.Word8]

> :m Data.ByteString.Char8
> :t pack
pack :: String -> ByteString
> :t unpack
unpack :: ByteString -> [Char]

OverloadedStrings

GHC has an extension called OverloadedStrings. It enables a use of String literal syntax for custom types. ByteString library makes use of it too.

    {-# LANGUAGE OverloadedStrings #-}

import qualified Data.ByteString.Char8 as B

someText :: B.ByteString
someText = "I'm a ByteString"

Now the String literal is automatically converted to a ByteString, no need for an explicit pack function call. What's nice about OverloadedStrings extension is that it is available for any type having an instance of IsString type class. This is how ByteString does it:

    instance IsString ByteString where
fromString = pack

Let's finish this post with a small example which puts OverloadedStrings feature to work. CasePreserving is a type which stores a String in two versions, the original format and a lower case version.

    {-# LANGUAGE OverloadedStrings #-}

import GHC.Exts (IsString(..))
import Data.Char

data CasePreserving = CasePreserving { original :: String,
lowerCase :: String }
deriving (Eq, Show)

instance IsString CasePreserving where
fromString s = CasePreserving s (map toLower s)

caseTest :: CasePreserving
caseTest = "Helo Wolrd"

Load the file into REPL to test it.

    *Main> :l Test.hs
*Main> caseTest
CasePreserving {original = "Helo Wolrd", lowerCase = "helo wolrd"}

keskiviikko 16. maaliskuuta 2011

Parsing command-line arguments with Haskell

I'm writing this because I found yet another great thing about Haskell: it's actually a nice language for writing command-line tools! A command-line tool written in Haskell reads like the manual for the tool. Pattern matching makes it easier and cleaner than in any other language (prove me wrong).

You can match exact strings for commands like "init", "update", "status", as well as any number of following arguments that you can use in the implementation of that command.

    main = getArgs >>= rebass

rebass ["init", name] = do
-- init using given name, ignore rest of arguments
rebass ["update"] = do
-- update, ignore arguments
rebass ["status"] = do
-- show status, ignore arguments
rebass _ = do
-- show usage as none of the patterns matched

Here's what I'm working on btw: https://github.com/raimohanska/rebass

tiistai 15. maaliskuuta 2011

Cooking delicious fish

Many Haskell newcomers stumble when presented with wonderful but abstract utility functions from the stdlib. This is mostly because Haskell documentation lacks easy to understand usage examples. In this short entry I will introduce one such nice function and an example how to use it.

Like a good Haskell citizen, let's start with its type signature:

    (a -> m b) -> (b -> m c) -> (a -> m c)

So, those are the ingredients that go along with a fish >=>, the name of the function1. Operators (functions with symbolic names) in Haskell are used in an infix form:

    x1 >=> x2

Here x1 has a type (a -> m b) and x2 has a type (b -> m c). The type of x1 >=> x2 is therefore (a -> m c). Now let's put all that aside for awhile and approach this example from a different angle.

Parse, convert and validate

A common programming task in a networked world is to parse some input data coming from a user. It needs to be parsed, converted to a proper type and often validated too.

    parseAge :: String -> Maybe Int
parseAge s = case reads s of
[(age, "")] -> Just age
_ -> Nothing

maxVal :: Int -> Int -> Maybe Int
maxVal x y | y > x = Nothing
| otherwise = Just y

minVal :: Int -> Int -> Maybe Int
minVal x y | y < x = Nothing
| otherwise = Just y

I'm using a Maybe type here since it is the most simple type which can represent failed computations. It is easy to replace that with a more sophisticated type, one capturing the failure reasons and other stuff but to keep the example short we will use a simpler type now. Anyway, given a set of such utility functions we would like to compose more complex functionality. This is where our fish excels.

    parseAge >=> (minVal 18) >=> (maxVal 24)

The above expression has a type (String -> Maybe Int) and it works as expected. We'll get Just x if age can be parsed as an integer, and it is at least 18 but at most 24. Otherwise we'll get Nothing.

    > :m +Control.Monad
> let pAge = parseAge >=> (minVal 18) >=> (maxVal 24)
> pAge "20"
Just 20
> pAge "20a"
Nothing
> pAge "17"
Nothing
> pAge "25"
Nothing

Conclusion

Functions are often composed in Haskell with . function. However, that simple composition function won't work if the functions composed are monadic functions (a -> m b). Monadic functions can be composed with >=> function.


  1. The real name of the function is Kleisli composition, but the operator symbol selected for that function looks a lot like a fish. ↩

maanantai 14. maaliskuuta 2011

Haskell Knows Your Latvian Name

My colleague presented a devilishly clever, yet simple algorithm for generating a Latvian version of your name. Here's the implementation in Haskell:

    import System.Environment(getArgs)
main = do
args <- getArgs
putStrLn $ latvian args
where
latvian = unwords . map (++ "s")

Copy this code to a file, say latvian.hs then

    runhaskell latvian.hs <your name>

And you'll know what you'd be called in case you were lucky enough to be born in Latvia.'

sunnuntai 13. maaliskuuta 2011

How to set up Haskell on Mac OS X

This guide assumes, that you have the brilliant Homebrew installed. If not, I recommend that you go to the Homebrew homepage and install it first.

Resetting Cabal and GHC package database

Sometimes you fiddle around your installation, and end up with messed up system that won't compile anything non-trivial. Cabal might have gotten into twist. In this case, the easiest solution sometimes is to do a reset and reinstall. It won't take much time, but allows you to have clean slate.

Magic incatations needed for cleanup are following:

    brew uninstall haskell-platform ghc  
rm -rf ~/.ghc ~/.cabal

These commands uninstall ghc (the haskell compiler) and haskell-platform (the standard library) and remove local databases.

Set up Haskell

Using brew, install haskell-platform with ghc (if brew formula for ghc is missing, it's likely that ghc has been rolled into haskell-platform).

    brew install ghc haskell-platform

After the haskell-platform has been installed, update cabal database with command

    cabal update

Add cabal binaries to PATH

This step depends a lot on your default shell. For bash, open ~/.bashrc to an editor of your choice, and add following line to the end:

    export PATH=$PATH:~/.cabal/bin

Next, take the new PATH to use with command

    source ~/.bashrc

Final words

Now you should have a working Haskell installation. Please note that sometimes your cabal installation may go corrupt (unsatiable or conflicting dependences) - in this case, you may need to use the reset described in the (first section.