For my GSoC project, I have to deal with named things, and I tried to generalized the traditional (String,a) object and I came with an interesting Named a data.

The problem

We often deal with named things, on which we want to perform some operations, without changing its name. For example, I want to benchmark libraries, so I will start with something like (String, Benchmark), the String being the library name, and come out to a (String, Double), with the Double being the time of taken by the function.

( For the sake of simplicity, lets imagine we have a function:

bench :: Benchmark -> IO Double

)

The Named Data

I started to write the Named data:

data Named a = Named String a

and one needed instance:

instance Show (Named a)
  show (Named n _ ) = n

And then, the main idea: applying a function f: a -> b to a Named a will produce a Named b without changing the name. My Haskell alarms ringed and detected a Functor instance:

instance Functor Named where
  fmap f (Named n x) = Named n $ f x

Well, so thought I was able to write a pretty function like:

benchN :: Named Benchmark -> IO (Named Double)

But recall, bench is of type Benchmark -> IO Double, so, using only fmap will lead to:

uglyBenchN :: Named Benchmark -> Named (IO Double)
uglyBenchN = fmap bench

So…

The Traversable instance

The solution is to provide sequence :: Monad m => Named (m a) -> m (Named a), which is inside the Traversable class. As its type says, sequence will “strip-out” the monadic effect. So we have:

instance Foldable Named where
  foldMap f (Named _ x) = f x

instance Traversable Named where
  -- Applicative f => Named (f a) -> f (Named a) 
  sequenceA (Named n m) = Named n <$> m

And poof, I am able to write:

notSoUglybenchN :: Named Benchmark -> IO (Named Double)
notSoUglybenchN = sequence . fmap bench

Because this is a common thing to do, we can use a classic function, traverse, and hence:

benchN :: Named Benchmark -> IO (Named Double)
benchN = traverse bench

Hooray ! But can we do better ? Named seems a pretty interesting object…

Is Named a Monad ? (Answer, no it is a CoMonad)

An interesting Functor is sometimes a Monad. So I started my checklist: to provide a Monad I have to provide the return and the bind function:

return :: a -> Named a
>>= :: Named a -> (a -> Named b) -> Named b

I don’t have a standard way to name things, and neither to get a name out of two named things… But I can easily strip out the name, and conserve the name:

notReturn :: Named a -> a
notBind :: Named a -> (Named a -> b) -> Named b

It seems pretty much like something dual to a monad. And this is called a Comonad :)

The instance definition:

instance Comonad Named where
  -- Named a -> a
  extract (Named _ obj) = obj
  -- Named a -> (Named a -> b) -> Named b 
  (=>>) named f = Named (show named) $ f named

Well we have a Comonad instance, we can redefine the Eq instance, given a little function:

liftExtract :: (Comonad w) => (a -> b -> c) -> w a -> w b -> c
liftExtract f a b = f (extract a) (extract b)

instance Eq a => Eq (Named a) where
  (==) = liftExtract (==)

liftExtract can in fact be used to define every lifted instances of transformers

And can even redefine the Functor instance:

instance Functor Named where
  fmap = liftW