Applicative Transformers: IdentityT

Posted on June 28, 2014 by Nadir Sampaoli

Recently on Twitter, after reading about someone using Monad transformers for their first time, I’ve written some, as random as usual, tweet about the possibility of having applicative transformers.

Promptly @bitemyapp gave me some food for thoughts. He suggested to start simple, by trying to rewrite an Applicative version of IdentityT, which is the identity tranformer for monads.

.@nadirsampaoli a monad transformer takes a monad and returns a monad. Start simple - can you make IdentityT applicative?

— FKA da bear ʕノ•ᴥ•ʔノ (@bitemyapp) June 27, 2014

The initial questions

Everything started with some questions thrown a bit carelessly:

  1. Do Applicative transformers exist?
  2. Would they be useful?
  3. Does it even make sense to think about such a thing?

With some new (for me!) concepts to learn about, I entered the cabal sandbox I use for experiments, fired up a vim buffer, and started rewriting IdentityT as an Applicative Functor, instead of a Monad.

What exactly is IdentityT?

First of all, I needed to figure out what exactly IdentityT was about. A quick Hoogle search brings up the Hackage page for the package named Control.Monad.Trans.Identity.

Without reflecting too much, I started writing down some mindless implementation of IdentityT as a Functor and Applicative instance:

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE KindSignatures #-}

module ApplicativeTransformers
    ( IdentityT(..)
    ) where

import Control.Applicative

newtype IdentityT (f :: * -> *) a = IdentityT { runIdentityT :: f a } deriving (Show)

instance Functor f => Functor (IdentityT f) where
    fmap h (IdentityT f) = IdentityT $ fmap h f

instance Applicative f => Applicative (IdentityT f) where
    pure = IdentityT . pure
    (IdentityT h) <*> (IdentityT f) = IdentityT $ h <*> f

Note:
GHC extension -XKindSignatures lets you constrain IdentityT’s type parameter f to that kind (i.e. the kind required to create Functor and Applicative instances).

Now, this was just an exercise to ensure I understood what I was actually putting together (keep reading, it’s going to be no time before I realize I didn’t really understand what’s going on). GHC has another extension, named -XGeneralizedNewtypeDeriving that allows you to avoid the chore of writing by hand type class instances that could be inferred by the compiler. So the previous snippet becomes:

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE KindSignatures, GeneralizedNewtypeDeriving #-}

module ApplicativeTransformers
    ( IdentityT(..)
    ) where

import Control.Applicative

newtype IdentityT (f :: * -> *) a = IdentityT { runIdentityT :: f a }
    deriving (Show, Functor, Applicative)

Implementing the actual transformer

Ok, now with that in place, the next thing to do would be to write a transforming function that operates on IdentityT. What’s its type signature?

mapIdentityT :: (f a -> g b) -> IdentityT f a -> IdentityT g b
mapIdentityT h x = undefined

I couldn’t find a way to apply h to x using just Applicative functions: the problem is that fmap, pure, etc. don’t operate on IdentityT but on the type it wraps. I thought it should have been possible to just do something like:

mapIdentityT = fmap

Here, by the way, is where I realized the Applicative instance for IdentityT refers directly to the type parameter of the applicative functor wrapped by the newtype.

Since the IdentityT f a implementation of fmap expects a function a -> b instead of f a -> g b I couldn’t use it. What I came up with wasn’t anything better than unwrapping and re-wrapping the IdentityT instance and just apply the function to the applicative that lives inside the newtype wrapper:

mapIdentityT :: (f a -> g b) -> IdentityT f a -> IdentityT g b
mapIdentityT h = IdentityT . h . runIdentityT

There is apparently really not much difference between a monadic and a “just” applicative implementation of the identity transformer. This somehow answers the first of those three question I asked initially or, at least stands for the Identity transformer.

Can I use it?

Let’s see what we can do with this applicative-only IdentityT.

> let foo = IdentityT (Just 5)
> foo
IdentityT {runIdentityT = Just 5}

Ok, now we need a function f a -> g b (let’s call it bar for the sake of meaningful names), which in this case would be Num a => Maybe a -> g b. Lets say we want a list of strings, so the signature ends up being:

bar :: (Num a, Show a) => Maybe a -> [String]

Its implementation is as follows:

> let bar = maybe [] (pure . show)

Finally we map bar to foo:

> mapIdentityT bar foo
IdentityT {runIdentityT = ["5"]}

At least it works, although I’m not sure this answers the second of my questions (Is it useful?).

Wrap up and next steps

Next thing to do is figuring out how to implement more complex transformers and probably learning about Yoneda and Coyoneda, whatever they are.

There’s something that does not feel right with this exploration, I’m probably missing something (part of the problem has to do with the fact I haven’t actually ever used monad transformers). So, feel free to let me know about nonsense I might have written here; I’m on Twitter @nadirsampaoli.


The source code for the thing I called ApplicativeTransformers is on Github: nadirs/applicative-transformers