Lets say I have:
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
import Control.Lens
data T a b = T { _foo :: a
, _bar :: a -> b
}
makeLenses ''T
a
appears in both foo
and bar
, so updates have to be "simulatenous", as it were. This is possible without lenses:
eg1 :: T a (b -> c) -> b -> T (a, b) c
eg1 (T foo bar) b = T (foo, b) (uncurry bar)
But how can I do this with lenses? The following doesn't work with an occurs check:
eg :: T a (b -> c) -> b -> T (a, b) c
eg t b = t & foo %~ (, b) & bar %~ uncurry
You can't do this using the automatically generated lenses for T
. If you want to stretch things a bit, you can first define
data T' a b c = T' { _foo' :: c, _bar' :: a -> b}
tt :: Iso (T a b) (T a' b') (T' a b a) (T' a' b' a')
tt = dimap (\(T x g) -> T' x g) (fmap (\(T' x g) -> T x g))
Then you can (automatically) build type-changing lenses for T'
and use tt
to use them to modify values of type T a b
through the isomorphism.
For example, rearranging the arguments a bit, you can write
eg :: b -> T a (b -> c) -> T (a, b) c
eg b = over tt $ (foo' %~ (,b)) . (bar' %~ uncurry)
Another approach that will likely be better if you don't need to fuss around with T
too much is to define it as a newtype
around T'
:
newtype T a b = T { getT :: T' a b a }
Then you can skip the Iso
and just compose things. Rearranging the arguments the same way,
eg' :: b -> T a (b -> c) -> T (a, b) c
eg' b = T . (foo' %~ (,b)) . (bar' %~ uncurry) . getT
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With