在 haskell 中使用仿函数

Using functor in haskell

我有一个这样定义的 World 数据类型:

     data World = World
    { resolution :: (Int, Int)
    , direction :: Snake_direction
    , snake_scale :: Int
    , snake :: [(Int, Int)]
    , isStuck :: Bool
    , gen :: R.StdGen
    , food :: (Int, Int)
    , superFood :: (Int, Int)
    } deriving (Read, Show) 

并且我想使用一个仿函数,它在 snake 列表上使用 fmap,它是 world 数据类型的参数。我正在为仿函数语法而苦苦挣扎

instance Functor (World) where
    fmap f (World) = World {fmap f snake}

但是编译器说

error: parse error on input ‘f’

如果我们检查 Functor 类型 class,我们会看到:

class Functor (f <b>:: * -> *</b>) where
  fmap :: (a -> b) -> f a -> f b
  (<$) :: a -> f b -> f a
  {-# MINIMAL fmap #-}

所以f种类应该是* -> *,一个带类型参数的类型。这里不是这种情况,所以我们不能声明 World 一个 Functor 的实例。由于用户可以使用任何函数 f :: a -> bWorld a 映射到 World b,但根本没有 World a

如果您仍想将其设为 Functor,我们可以先 "upgrade" World 类型,使其具有类型参数:

data World <b>a</b> = World
    { resolution :: (Int, Int)
    , direction :: Snake_direction
    , snake_scale :: Int
    , snake :: [(<b>a</b>, <b>a</b>)]
    , isStuck :: Bool
    , gen :: R.StdGen
    , food :: (Int, Int)
    , superFood :: (Int, Int)
    } deriving (Read, Show)

那么我们可以定义一个fmap如下:

instance Functor World where
    <b>fmap f w = w {snake = fmap (\(x,y) -> (f x, f y)) (snake w)}</b>

或者如果您想 fmap 直接越过 snake,而不是越过 snake 坐标

data World <b>a</b> = World
    { resolution :: (Int, Int)
    , direction :: Snake_direction
    , snake_scale :: Int
    , snake :: <b>a</b>
    , isStuck :: Bool
    , gen :: R.StdGen
    , food :: (Int, Int)
    , superFood :: (Int, Int)
    } deriving (Read, Show)

那么我们可以定义一个fmap如下:

instance Functor World where
    fmap f w = w {snake = <b>f (snake w)</b>}

另一种选择是使World成为仿函数,因为您真正想要的只是一种将函数映射到World值。

snakeMap :: ((Int, Int) -> (Int, Int)) -> World -> World
snakeMap f w = w { snake = fmap f (snake w) }