How to map a function over a part of a nested data structure?

时间:2019-01-07 13:56:08

标签: haskell data-structures

I want to map a function of type Float -> Float over a part of a data structure which looks like this.

VDstruct { _onBuild = Just True                                                                                          
         , _imageName = Just "arc.png"                                                                                        
         , _category = Just "All"                                                                                         
         , _mmo = Just 210                                                                                                     
         , _structTypes = Just                                                                                                  
               ( Mage                                                                                                           
                   { _ict = Nothing                                                                                            
                   , _create = Just 1.24                                                                                          
                   , _sh = Nothing                                                                                                                                                                                     
                   }    
         }

I want to apply that function to _ict, _create and to _sh.

I know how to do that for each one of those. I'm using Lenses to help me out with that. The result has _create = Just 5.4 which is exactly what I expect from plusXPercent function. This is what I'm using right now.

setSer x = x & (structTypes . _Just . create) %~ fmap plusXPercent

What I want to do is instead of naming every _ict, _sh, etc. I want a way to 'map' that function over the entirety of Mage structure.

How should I go about doing that?

Edit: _ict, _create and _sh have type Maybe Float

and Mage is defined like this

data Mage = Mage { _ict :: Maybe Float
                 , _create :: Maybe Float
                 , _sh :: Maybe Float
                 } deriving Show

2 个答案:

答案 0 :(得分:4)

如果您想使用mono-traversable包,则可以为MonoFunctor定义一个Mage实例。

type instance Element Mage = Maybe Float

instance MonoFunctor Mage where
    omap f (Mage x y z) = Mage (f x) (f y) (f z)

然后,您可以使用omapfmap (+1)应用于(例如)每个字段 的Mage

 omap (fmap (+1)) (Mage { _ict = Nothing                                   
                        , _create = Just 1.24                                                                                          
                        , _sh = Nothing
                        })
   == Mage { _ict = Nothing, _create = Just 2.24, _sh = Nothing }

然后,我想,你会写(对不起,只是猜测,镜头不是我的强项):

--setSer x = x & (structTypes . _Just . create) %~ fmap plusXPercent

setSer x = x & (structTypes . _Just) %~ (omap (fmap plusXPercent))

但是,MonoFunctor可能会过大。您可以使用Applicative函数实例来完成相同的事情。

foo :: (Maybe Float -> Maybe Float) -> Mage -> Mage
foo f = Mage <$> f . _ict <*> f . _create <*> f . _sh

答案 1 :(得分:3)

您始终可以手动编写遍历,这就是需要这样做的地方。镜头有一个适用于此的课程,Each

instance (a ~ Float, b ~ Float) => Each Mage Mage a b where
    each f (Mage i c s) = Mage <$> traverse f i <*> traverse f c <*> traverse f s

您也可以在不使用类的情况下编写遍历,但也可以在适当时重用该名称。

相关问题