haskell - 如何修复这种嵌套 fmap 仿函数的困惑?

标签 haskell functional-programming nested functor map-function

data Color = White | Black deriving (Eq, Show)
data Role = King | Queen | Rook deriving (Eq, Show)

data Piece = Piece { color :: Color, 
                     role  :: Role } deriving (Eq)

data Piese = Piese { piece :: Piece,
                     coord :: Coord } deriving (Eq)

data ColorMap a = ColorMap {
    white :: a,
    black :: a
  } deriving (Eq)

instance Functor ColorMap where
  fmap fn (ColorMap white black) = 
    ColorMap (fn white) (fn black)

colorMapFromList :: (a -> Color) -> [a] -> ColorMap [a]
colorMapFromList fn lst = ColorMap 
    (filter ((== White) . fn) lst)
    (filter ((== Black) . fn) lst)
                  
data RoleMap a = RoleMap {
    king  :: a,
    queen :: a,
    rook  :: a }

instance Functor RoleMap where
  fmap fn (RoleMap king queen rook) = 
    RoleMap (fn king) (fn queen) (fn rook)

roleMapFromList :: (a -> Role) -> [a] -> RoleMap [a]
roleMapFromList fn lst = RoleMap
    (filter ((== King ) . fn) lst)
    (filter ((== Queen) . fn) lst)
    (filter ((== Rook ) . fn) lst)

mapso :: [Piese] -> ColorMap (RoleMap [Coord])
mapso lst = 
 fmap (fmap (fmap coord))                -- ColorMap (RoleMap [Coord])
   (fmap (roleMapFromList (role . piece))  -- ColorMap (RoleMap [Piese])
         (colorMapFromList (color . piece)   -- ColorMap [Piese]
              lst))                            -- [Piese]

我刚刚跳入 Haskell,它可以编译,但对我来说似乎很容易出错。这里有我可以简化的模式吗?特别是 mapso 函数。

最佳答案

您可以利用仿函数组合这一事实。虽然您可以通过使用 Data.Functor.Compose 定义新类型来相当明确地表示这一点,但实际上,这仅意味着您可以将 fmap 与自身组合。

mapso :: [Piese] -> ColorMap (RoleMap [Coord])
mapso lst = fmap (fmap (fmap coord))
  (fmap (roleMapFromList (role.piece)) (colorMapFromList (color.piece) lst))

变成了

mapso = (fmap . fmap . fmap) coord . 
        fmap (roleMapFromList (role.piece)) .
        colorMapFromList (color.piece)

或者进行一些重构:

mapso = let fffmap = fmap . fmap . fmap
            makeColorMap = colorMapFromList (color.piece)
            makeRoleMap = roleMapFromList (role.piece)
        in fffmap coord . 
           fmap makeRoleMap .
           makeColorMap

我已切换到无点形式来突出显示三个阶段:

  1. 创建颜色映射
  2. 创建角色映射
  3. coord映射到包装在RoleMap内的[Piese]值,包装在ColorMap内。<

我们使用函数组合来减少 mapso 定义中显式嵌套的数量。

如果您还不习惯思考函数组合,您可以在 let 表达式中定义更多临时变量:

mapso lst = let fffmap = fmap . fmap . fmap
                makeColorMap = colorMapFromList (color.piece)
                makeRoleMap = fmap (roleMapFromList (role.piece))
            in let colorMap = makeColorMap lst
                   rolemap = makeRoleMap colorMap
            in fffmap coord roleMap

(我们需要两个 let 表达式吗?不需要。但是将辅助函数与辅助函数计算的值分开可能会有所帮助。)

关于haskell - 如何修复这种嵌套 fmap 仿函数的困惑?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69092450/

相关文章:

c# - 将父、子、孙数据存储在内存中

haskell - Haskell 中函数的推断类型

haskell - Aeson:将动态键解析为类型字段

javascript - JavaScript 中的函数式编程是否可以具有任何类型的多态性?

arrays - 使用函数式编程按索引对数组数组中的元素进行平均

ios - 带有嵌套项的 RestKit 动态映射

haskell - 字节串到 [Word16] 的高效转换

haskell - 如何编写任意修改其任意输入的 QuickCheck 属性?

design-patterns - 您是否在具有闭包/委托(delegate)/函数指针的编程语言中使用模板方法模式?

c# - 从 MongoDB 查询嵌套对象