0

I'm trying to update some old code using fclabels, from v0.4 to v2.0 (lts-3.17), that lifts a label/lens to a monad. The old code is:

{-# LANGUAGE TypeOperators #-}
import Data.Record.Label
import Control.Monad (liftM, liftM2)

liftMLabel :: Monad m => a :-> b -> m a :-> m b
liftMLabel l = label (liftM $ get l) (liftM2 $ set l)

So the first thing I did was change label to lens, and set to modify:

{-# LANGUAGE TypeOperators #-}
import Data.Label
import Control.Monad (liftM, liftM2)

liftMLabel :: Monad m => a :-> b -> m a :-> m b
liftMLens l = lens (liftM $ get l) (liftM2 $ modify l)

This gave the following compiler error:

Expected type: (m b -> m b) -> m a -> m a
  Actual type: m (b -> b) -> m a -> m a

Which makes sense, giving how liftM2 would treat each arg of the lens modify function.

The old fclabels used a setter function for creating a label, which accepts a simple value argument. The modify function, used for creating newer fclabels lenses, takes a function for modifying using the existing value, and I see why it too will operate on monadic arguments.

I'll need to perform some extra plumbing for the modify function, and I see that ap does something similar to what I want, but it's not clear to me what the best approach is in totality.

So what is a good way to deal with the modify function, so I can match the expected type?

Cactus
  • 27,075
  • 9
  • 69
  • 149
S i
  • 3
  • 3

1 Answers1

0

Instead of trying to write it all in one go in point-free style, why not just write it in long-hand?

liftMLens :: (Monad m) => a :-> b -> m a :-> m b
liftMLens l = lens (liftM $ get l) $ \f mx -> do
    x <- mx
    let v = get l x
    v' <- f $ return v
    return $ set l v' x

Of course you can then code-golf it if you really want to...

Cactus
  • 27,075
  • 9
  • 69
  • 149
  • That's great, thanks. I had been looking for an overly generic (read impossible) way to do this. – S i Dec 31 '15 at 15:35