News aggregator

Unable to make a HList-like to deal with KleisliArrows and State

haskell-cafe - Wed, 06/11/2014 - 8:31pm
Hi all, My goal is to compose Kleisli arrows with (State s) as parameteric Monadic type. So, I end up with the following code: data REnv :: [*] -> * -> * -> (* -> * -> *) -> ((* -> *) -> * -> * -> *) -> * where Last :: k (s t) i o -> REnv (t ': '[]) i o s k RCons :: k (s t) i o' -> REnv ts o' o s k -> REnv (t ': ts) i o s k Which, of course, gives me the following error when I try to play with it: *H> :t Last genRanking <interactive>:1:6: Kind incompatibility when matching types: s :: * -> * -> * StateT Ranking :: (* -> *) -> * -> * Expected type: Kleisli (s t) Ticket Ranking Actual type: Kleisli (State Ranking) Ticket Ranking In the first argument of ‘Last’, namely ‘genRanking’ In the expression: Last genRanking <interactive>:1:6: Kind incompatibility when matching types: t :: * Data.Functor.Identity.Identity :: * -> * Expected type: Kleisli (s t) Ticket Ranking Actual type: Kleisli (State Ranking) Ticket Ranking In the first
Categories: Offsite Discussion

Haskell Toronto... is there a community here?

Haskell on Reddit - Wed, 06/11/2014 - 8:17pm

Hey guys.

I'm a developer who over the past year has built an interest (and even some basic skills!) with Haskell. I'm also someone who has really enjoyed the javascript community around Toronto and would love to hang out with any haskell devs, aspiring haskell devs whether its over a beer, specific topics, or what have you.

If this post is completely inappropriate for this sub I apologize and any mod please feel free to chastise me and take the post down. I'm just in search of a communty with which to grow and share the passion and thought maybe others would want the same.

So... if anyone knows of a small group Toronto Haskell devs or would like to create a casual meetup of some kind let me know! Cheers!

submitted by phaggocytosis
[link] [6 comments]
Categories: Incoming News

Opening for OpenGL, C++ Development.

haskell-cafe - Wed, 06/11/2014 - 6:04pm
Hello, We are looking for Software Developers with OpenGL experience. - Should have experience in C++/Xcode, OpenGL/DirectX/IOS. - Experience with Shafer programming, Multi-threaded applications, Graphics in CAD domain. - The role is of Software Development on primary CAD application like CATIA. - Good skills in Geometry, algorithms, Mathematics, API's, Graphics-will be a added advantage. Experience : 2 to 6 years. If anyone is interested on learning more about this opportunity and potential salary ranges, please email me at : manoj_chaudhari2< at > Regards, Manoj Chaudhari _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe< at >
Categories: Offsite Discussion

Magnus Therning: More Visitor (in Python)

Planet Haskell - Wed, 06/11/2014 - 3:07pm

Right after writing the previous post on the Visitor pattern in Python I picked up another paper on the same topic, Visitor Combination and Traversal Control. Of course this one also used Java for its examples, so once again I decided to use Python to explore the ideas presented.

The first part of the paper is all about writing small visitors that then are combined into more complicated ones. This part is nice but not that exciting. The interesting bit is when it gets to controlling traversal, which means it’s possible to remove the traversal code that usually appear in the accept method each visited type has to implement. Let’s see how that can look in Python.

The full code in this post is available at

First we need a structure to traverse, a simple tree will do.

class Tree(Visitable): def __init__(self, left, right): self.left = left self.right = right   class Leaf(Visitable): def __init__(self, value): self.value = value   def build_tree(): l0 = Leaf(0) l1 = Leaf(1) t0 = Tree(l0, l1) l2 = Leaf(2) t1 = Tree(t0, l2) l3 = Leaf(3) l4 = Leaf(4) t2 = Tree(l3, l4) return Tree(t1, t2)

But before this we really should define Visitor, the base class for visitors, and Visitable, the base class of everything that can be visited.

class Visitor: def visit(self, obj): getattr(self, 'visit_' + obj.__class__.__name__)(obj)   def visit_Tree(self, t): pass   def visit_Leaf(self, l): pass   class Visitable: def accept(self, visitor): visitor.visit(self)

We’ll throw in a visitor for printing the whole tree too:

class Print(Visitor): def visit_Tree(self, t): print('Tree (%s)' % hex(id(t)))   def visit_Leaf(self, l): print('Leaf (%s): %i' % (hex(id(l)), l.value))

Due to the lack of traversal in the accept methods it’s easy to be underwhelmed by the Print visitor:

In [32]: build_tree().accept(Print()) Tree (0x7f1680681a90)

To address this we first need a visitor combinator that runs two visitors in sequence. Unsurprisingly we’ll call it Sequence. Its constructor takes two visitors, and for each node in the tree it passed each one to the node.accept method.

class Sequence(Visitor): def __init__(self, first, then): self.first = first self.then = then   def visit_Tree(self, t): t.accept(self.first) t.accept(self.then)   def visit_Leaf(self, l): l.accept(self.first) l.accept(self.then)

The next building block is a visitor that descends one level down from a Tree node.

class All(Visitor): def __init__(self, v): self.v = v   def visit_Tree(self, t): t.left.accept(self.v) t.right.accept(self.v)

At this point it’s worth noting that the name All probably isn’t very well chosen, since we don’t really get all nodes:

In [33]: build_tree().accept(All(Print())) Tree (0x7f1680681278) Tree (0x7f1680681be0)

We only descend one level, but we still keep the name since that’s the name they use in the paper.

With this in place it does become possible to create combinations that do traverse the full tree though. It even becomes rather simple. Traversing top-down is a simple matter of using a sequence that ends with All, and bottom-up is a matter of using a sequence starting with All.

class TopDown(Sequence): def __init__(self, v): Sequence.__init__(self, v, All(self))     class BottomUp(Sequence): def __init__(self, v): Sequence.__init__(self, All(self), v)

First top-down:

In [34]: build_tree().accept(TopDown(Print())) Tree (0x7f1680681ef0) Tree (0x7f16806814a8) Tree (0x7f16806813c8) Leaf (0x7f1680681278): 0 Leaf (0x7f1680681550): 1 Leaf (0x7f1680681a90): 2 Tree (0x7f1680681f28) Leaf (0x7f1680681ba8): 3 Leaf (0x7f1680681a20): 4

Then bottom-up:

In [35]: build_tree().accept(BottomUp(Print())) Leaf (0x7f1680681ba8): 0 Leaf (0x7f16806814a8): 1 Tree (0x7f1680681a90) Leaf (0x7f16806813c8): 2 Tree (0x7f1680681550) Leaf (0x7f1680681278): 3 Leaf (0x7f16806a1048): 4 Tree (0x7f16806a1198) Tree (0x7f16806a1390)

That’s all rather cute I think.

Categories: Offsite Blogs

Haskell on Reddit - Wed, 06/11/2014 - 1:59pm
Categories: Incoming News

Deploy Haskell application

haskell-cafe - Wed, 06/11/2014 - 11:24am
Hi guys! Is there a procedure to deploy a Haskell application? I have an Amazon EC2 micro instance to run my application, but it's way too small to compile it using cabal (compilation takes half a day rouhgly), so I compile it on my computer. Is there a convenient way to bundle the executable with the resources and ship it to the server? Thanks! Corentin _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe< at >
Categories: Offsite Discussion

Mike Izbicki: do your lenses even do notation?

Planet Haskell - Wed, 06/11/2014 - 10:18am

It’s round 5 of typeparams versus GHC. We’ll be extending our Functor and Applicative classes to define a new Monad class. It’s all pretty simple if you just remember: lensified monads are like burritos with fiber optic cables telling you where to bite next. They’re also just monoids in the category of lens-enhanced endofunctors. Piece of cake.

some naughty extensions

We’ll be using all the same extensions as before:

> {-# LANGUAGE TemplateHaskell #-} > {-# LANGUAGE ScopedTypeVariables #-} > {-# LANGUAGE KindSignatures #-} > {-# LANGUAGE TypeFamilies #-} > {-# LANGUAGE MultiParamTypeClasses #-} > {-# LANGUAGE UndecidableInstances #-} > {-# LANGUAGE FlexibleInstances #-} > {-# LANGUAGE RankNTypes #-}

But we’ll be adding some pretty nasty ones today:

> {-# LANGUAGE OverlappingInstances #-} > {-# LANGUAGE RebindableSyntax #-}

We need RebindableSyntax to get do notation, but OverlappingInstances is just a product of the Monad class’s definition. I’ll give infinite haskell points to anyone who can refactor this code so we don’t need the extension!

We’ll also be needing all of our previous work on Functors and Applicatives. It has been uploaded to hackage and is sitting in the appropriate modules:

> import Control.Category > import Prelude hiding ( (.), id, Functor(..), Applicative(..), Monad(..) ) > import qualified Prelude as P > import GHC.Exts > import Data.Params > import Data.Params.Applicative > import Data.Params.Functor

And we’re off!

joins and cojoins

We will define our monads in terms of their join function. In the standard libraries, join has the type:

join :: m (m a) -> m a

The input has the same type as the output, except that the Monad m is repeated twice. There are two differences in the lensified join function: First, the monad we’re working with might be nested arbitrarily deeply in other data types. Second, the argument it is monadic in might not be the last one. Here is an example of what the join type signature would look like for the Left Either monad sitting within a Maybe Monad:

join :: TypeLens Base (Param_a (Param_a Base)) -> Maybe (Either (Either String Int) Int) -> Maybe (Either String Int)

Since we’re all wannabe category theorists here, we’ll create a CoJoin type family that transforms the output of the join function by duplicating the type at location specified by the lens:

> type family CoJoin (lens :: * -> Constraint) t > type instance CoJoin lens t > = SetParam' > lens > ( SetParam' > ( Objective lens ) > ( GetParam lens t ) > ( GetParam (RemoveObjective lens) t ) > ) > t

(We covered the Objective and RemoveObjective families in a previous post. As a reminder, the Objective family returns the innermost type lens from our input, and the RemoveObjective family returns the lens that results when the innermost lens is taken away.)

CoJoin only has one instance, so we could have just used a type synonym. That would make debugging harder, however. The advantage of a type family is that when we ask GHCi what the type is, it will perform the substitutions for us. For example:

ghci> :t undefined :: CoJoin (Param_a Base) (Maybe (Either String Int)) :: Maybe (Maybe (Either String Int)) ghci> :t undefined :: CoJoin (Param_a (Param_a Base)) (Maybe (Either String Int)) :: Maybe (Either (Either String Int) Int) making the monad

Now we’re ready to see our new Monad class:

> class Applicative lens tfb => Monad lens tfb where > join :: > ( tffb ~ CoJoin lens tfb > ) => TypeLens Base lens > -> tffb -> tfb

The Left and Right Either instances are:

> instance Monad (Param_a Base) (Either a b) where > join lens (Left (Left a)) = Left a > join lens (Left (Right b)) = Right b > join lens (Right b) = Right b > instance Monad (Param_b Base) (Either a b) where > join lens (Right (Right b)) = Right b > join lens (Right (Left a)) = Left a > join lens (Left a) = Left a

And here are some examples of join in action:

ghci> join _b (Right $ Right "monads") :: Either String String Right "monads" ghci> join _b (Right $ Left "are") :: Either String String Left "are" ghci> join _a (Left $ Left "so") :: Either String String Left "so" ghci> join _a (Right "awesome") :: Either String String Right "awesome"

The instances above don’t consider the case when our lenses point inside of the Either type. We’ll need to define two new recursive instances to handle this case. These instances are the reason we needed the OverlappingInstances language extension:

> instance > ( Monad p a > , Either (CoJoin p a) b ~ CoJoin (Param_a p) (Either a b) -- follows from the lens laws > ) => Monad (Param_a p) (Either a b) > where > > join lens (Left a) = Left $ join (zoom lens) a > join lens (Right b) = Right b > instance > ( Monad p b > , Either a (CoJoin p b) ~ CoJoin (Param_b p) (Either a b) -- follows from the lens laws > ) => Monad (Param_b p) (Either a b) > where > > join lens (Left a) = Left a > join lens (Right b) = Right $ join (zoom lens) b

The equality constraints in the instances above are implied by the lens laws. As we discussed yesterday, with the type rules language extension, those constraints could be removed completely, making the code a bit nicer.

Here are some examples of using join in the nested case:

ghci> join (_a._b) (Left $ Right $ Right "lenses") :: Either (Either a String) b Left (Right "lenses") ghci> join (_a._b) (Left $ Right $ Left "are") :: Either (Either String b) b Left (Left "are") ghci> join (_b._b) (Left "neat") :: Either String (Either a String) Left "neat"

Sometimes we will get the same answer if we join in two separate locations. In the first example below, we join the second two Right constructors, whereas in the second example, we join the first two Right constructors. The results are the same:

ghci> join (_b._b) (Right $ Right $ Right "easy") :: Either a (Either a String) Right (Right "easy") ghci> join _b (Right $ Right $ Right "peasy") :: Either a (Either a String) Right (Right "peasy")

We’ll also be needing a Monad instance for Maybe, so here it is:

> instance Monad (Param_a Base) (Maybe a) where > join lens Nothing = Nothing > join lens (Just Nothing) = Nothing > join lens (Just (Just a)) = Just a > instance > ( Monad p a > , Maybe (CoJoin p a) ~ CoJoin (Param_a p) (Maybe a) -- follows from the lens laws > ) => Monad (Param_a p) (Maybe a) > where > join lens Nothing = Nothing > join lens (Just a) = Just $ join (zoom lens) a bind

From join and our Applicative instance, we can derive our monadic bind function. We don’t want to use the traditional (>>=) operator for bind just yet. We will need to do something fancy with it to make do notation work out. So instead, we will use the (\\=) operator for bind. Its definition is:

(\\=) :: ( Monad lens tb , a ~ GetParam lens tfa , {- ... lens laws go here ... -} ) => ta -> (a -> tb) -> TypeLens Base lens -> tb > infixl 1 \\= > (m \\= f) lens = join lens $ fmap lens f m

We will create the “minus bind operators” in the same way we created minus operators for the Applicative class. Remember, the minus sign points to the parameters that will get a lens applied to them because they are “minus a lens”. These minus operators are defined as:

> infixl 1 \\=- > infixl 1 -\\=- > infixl 1 -\\= > (m \\=- f) lens = ( m \\= \a -> f a $ objective lens ) lens > (m -\\=- f) lens = ( m lens \\= \a -> f a $ objective lens ) lens > (m -\\= f) lens = ( m lens \\= \a -> f a ) lens example time

For our example, we’ll build a simple monadic filter. The filterSmall function below sits in the Either Monad, but we’ll be using Left to represent successes (the input passes through the filter), and Right to represent failure (the input doesn’t pass through).

> filterSmall :: (Show a, Ord a) => a -> a -> Either a String > filterSmall k x = if x > k > then Left x > else Right $ show x ++ " is too small"

We can call our function using the monadic bind by:

> chain1 :: Either Int String > chain1 = at _a $ Left 20 \\= filterSmall 10 ghci> chain1 Left 20

Instead of using the Left constructor, we can make things a little more generic by using the return function. As usual, it is equivalent to pure:

> return :: Monad lens t => GetParam lens t -> TypeLens Base lens -> t > return = pure

Sine pure’s last parameter is a type lens, we must use the left-minus (-\\=) variant of bind to sequence the computation:

> chain2 :: Either Int String > chain2 = at _a $ return 20 -\\= filterSmall 10 ghci> chain2 Left 20

Similarly, all the bind operators take a type lens as their last parameter. So any future binds must also use left-minus bind:

> chain3 :: Either Int String > chain3 = at _a $ return 20 -\\= filterSmall 10 -\\= filterSmall 15 ghci> chain3 Left 20

And so on:

> chain4 :: Either Int String > chain4 = at _a $ return 20 -\\= filterSmall 10 -\\= filterSmall 15 -\\= filterSmall 25 ghci> chain4 Right "20 is too small"

We can easily nest our monads. Let’s put all of the computations above inside a Maybe wrapper. All we have to do is change the type signature and the lens:

> chain2' :: Maybe (Either Int String) > chain2' = at (_a._a) $ return 20 -\\= filterSmall 10 > chain3' :: Maybe (Either Int String) > chain3' = at (_a._a) $ return 20 -\\= filterSmall 10 -\\= filterSmall 15 > chain4' :: Maybe (Either Int String) > chain4' = at (_a._a) $ return 20 -\\= filterSmall 10 -\\= filterSmall 15 -\\= filterSmall 25 do the notation

We’re using the RebindableSyntax language extension to construct a custom do notation. We do this by defining our own (>>=) operator. The most generic bind operator we have is the double minus bind (-\\=-). Sometimes we will want to feed a lens to both sides of the bind, so that’s what we’ll use:

> infixl 1 >>= > (m >>= f) lens = (m -\\=- f) lens

Notice that our (>>=) operator and the one from Prelude take different numbers of arguments! GHC is awesome enough that this is not a problem.

RebindableSyntax also requires us to define functions for failed pattern matching and if statements. Our definitions will be pretty simple:

> fail = error > ifThenElse False _ f = f > ifThenElse True t _ = t

Now, we can take our chain2′ function above and rewrite it in do notation. Here it is again for easy reference:

chain2' :: Maybe (Either Int String) chain2' = at (_a._a) $ return 20 -\\= filterSmall 10

First, rewrite it to use (-\\=-) instead of (-\\=) by causing the right hand side to take a lens parameter even though it won’t use it:

> chain2'' :: Maybe (Either Int String) > chain2'' = at (_a._a) $ return 20 -\\=- (\x lens -> filterSmall 10 x)

Then, rewrite it using do notation:

> chain2''' :: Maybe (Either Int String) > chain2''' = at (_a._a) $ do > x <- return 20 > \lens -> filterSmall 10 x

It looks a little bit nicer if we use const to absorb the lens parameter:

> chain2'''' :: Maybe (Either Int String) > chain2'''' = at (_a._a) $ do > x <- return 20 > const $ filterSmall 10 x

Here is our other examples converted into do notation using the same technique:

> chain3''' :: Maybe (Either Int String) > chain3''' = at (_a._a) $ do > x <- return 20 > y <- const $ filterSmall 10 x > const $ filterSmall 15 y > chain4'' :: Maybe (Either Int String) > chain4'' = at (_a._a) $ do > x <- return 20 > y <- const $ filterSmall 10 x > z <- const $ filterSmall 15 y > const $ filterSmall 25 z

And here is a more complicated expression with a nested do:

> chain5 :: Either a (Either a (Maybe (Either Int String))) > chain5 = at (_b._b._a._a) $ do > x <- return 20 > y <- do > a <- const $ filterSmall x 10 > b <- const $ filterSmall 1 3 > return $ a+b > z <- const $ filterSmall y x > return $ z-x

But there is still a limitation. Due to the way the types work out, the first line of a do block must always be a return statement when using the at function to specify our lens. This is a by product of the extra lens parameter our (>>=) operator is passing around. Fortunately, we can automate this construction with the following function:

> atM lens m = at (removeObjective lens) $ do > return $ at (objective lens) $ m

This lets us rewrite chain5 as:

> chain5' :: Either a (Either a (Maybe (Either Int String))) > chain5' = atM (_b._b._a._a) $ do > let x = 20 > y <- do > a <- const $ filterSmall x 10 > b <- const $ filterSmall 1 3 > return $ a+b > z <- const $ filterSmall y x > return $ z-x

Now we fully support do notation!


Tune in next time…

How do we get rid of those ugly const functions?

Can optimus prime use type lenses to save our purity from the effects of the evil decepticons?

Does any one actually care about lensified arrow-do?

Stay tuned to find out.

Categories: Offsite Blogs

Functional Jobs: Developer / DevOps Engineer at Klarna (Full-time)

Planet Haskell - Wed, 06/11/2014 - 5:22am

Our FRED teams are responsible for building and operating Klarna’s highly scalable and highly available purchase-accepting system. By combining Erlang, RIAK and RabbitMQ this business critical, multi-node, no-master system handles our exponential growth of real time transactions. The teams have end-to-end responsibility and believe in relentless automation, shipped code and reusability.

We are looking for a functional programmer that wants to take on some operations responsibilities and who believes that you should be responsible for what you create. The role will primarily involve developing infrastructure for logging, monitoring, testing, deployment and operations in order to support the system and contributing feature teams, but a willingness to understand the underlying business-logic and when necessary contribute some feature development is also important. We are looking for someone who can grow into being a Hamiltonian salty mechanic in a mission critical helicopter, so to speak.


  • B.Sc/M.Sc in Computer Science
  • experience with a functional programming language (Erlang, a LISP, an ML, Haskell, etc)
  • comfortability with shell scripting
  • extremely high attention to detail
  • intellectual curiosity
  • a preference for simple, elegant, and reusable solutions
  • a get-things-done attitude

Nice to have

  • some experience with configuration management (Chef, Puppet, ansible, cfengine, etc)
  • some exposure to Java, Erlang or Ruby
  • experience with graphite, collectd
  • experience with virtualization (CloudStack, AWS, etc)
  • nagios/op5 experience
  • experience with Splunk API
  • RabbitMQ experience
  • Riak experience (or experience with distributed databases NoSQL/SQL)
  • proven technical writing ability

Would be cool

  • history of giving entertaining technical talks at conferences

If you have any question please email Philip Alsén at: philip [dot] alsen [at] klarna [dot] com. Apply as soon as possible since we are working continuously with the recruitment process.

Location Stockholm, Sweden

Get information on how to apply for this position.

Categories: Offsite Blogs

non-exhaustive pattern match(es)?

haskell-cafe - Wed, 06/11/2014 - 12:17am
Dear Haskell Cafe, In the following program f :: Int -> [(Int,Int)] -> [(Int,Int)] -> [(Int,Int)] f _ cs [] = cs f _ [] _ = [] f i (c:cs) ((i',t):os) | i < i' = [] | i == i' = [] | i > i' = []
Categories: Offsite Discussion

JOB: AHRC Doctoral Studentship in ComputationalMusicology

haskell-cafe - Tue, 06/10/2014 - 4:00pm
STUDENTSHIP ADVERTISEMENT With apologies for cross posting AHRC Doctoral Studentship in Computational Musicology London, UK (with UK residency requirement unfortunately) Although this is not an explicitly Haskell programming job, the student would be free to work in whatever language they wish. Furthermore, there would be encouragement to consider a functional and/or logic programming approach to research. Award: fees and tax-free stipend at £15,726 p.a. (inc. of London weighting) Application deadline: Tuesday 1 July 2014 Expected start date: October 2014 We invite applications for a Doctoral Studentship, funded by the Arts and Humanities Research Council, in Computational Musicology, located at Queen Mary University of London, under the supervision of Professor Geraint Wiggins. The studentship is part of the "Transforming Musicology" project, including Goldsmiths, University of London, Quee
Categories: Offsite Discussion

System.Directory.removeDirectoryRecursive and symlinks

libraries list - Tue, 06/10/2014 - 2:42pm
Hi all, A crime scene: Prelude System.Directory> :!mkdir a-directory Prelude System.Directory> :!touch a-directory/a-file.txt Prelude System.Directory> :!ln -s "a-directory" "a-symlink-to-a-directory" Prelude System.Directory> :!ls a-directory a-file.txt Prelude System.Directory> :!ls a-symlink-to-a-directory a-file.txt Prelude System.Directory> removeDirectoryRecursive "a-symlink-to-a-directory" *** Exception: a-symlink-to-a-directory: removeDirectory: inappropriate type (Not a directory) Prelude System.Directory> :!ls a-symlink-to-a-directory Prelude System.Directory> :!ls a-directory Prelude System.Directory> :!ls -a a-directory ... Prelude System.Directory> :!ls -a a-symlink-to-a-directory ... Prelude System.Directory> removeDirectoryRecursive is removing all contents *of the directory linked* but is unable to remove the symlink itself. This behavior is surprizing if not dangerous. I understand that this mimics behavior of unlink/rmdir and DeleteFile/RemoveDirecto
Categories: Offsite Discussion

Call for participation: HLPP 2014 - 7th Symposium on High-Level Parallel Programming and Applications

General haskell list - Mon, 06/09/2014 - 10:29pm
=========================================================================== CALL FOR PARTICIPATION HLPP 2014 7th International Symposium on High-level Parallel Programming and Applications Amsterdam, Netherlands July 3-4, 2014 =========================================================================== Early registration deadline: June 16 =========================================================================== Keynote: Frank Schlimbach, Intel: Design and Implementation of Intel Concurrent Collections (CnC) Abstract: CnC (concurrent collections) is a parallel language in the sense that its goal is parallel execution but in fact a CnC program does not indicate what runs in parallel. Instead, it identifies what precludes parallel execution. There are exactly two reasons that computations cannot execute i
Categories: Incoming News

New gtk2hs 0.12.4 release

gtk2hs - Wed, 11/21/2012 - 12:56pm

Thanks to John Lato and Duncan Coutts for the latest bugfix release! The latest packages should be buildable on GHC 7.6, and the cairo package should behave a bit nicer in ghci on Windows. Thanks to all!


Categories: Incoming News