fmap应用于类型变量以外的类型

时间:2017-12-07 20:44:22

标签: haskell

我有一个数据类型可以跟踪一个'块'数字(有点像矩阵)。

newtype Block a = Block [[a]]

我想将其作为Functor的一个实例。但是,我尝试以fmap可以应用于整个列表块的方式执行此操作,以便fmap具有类型fmap :: [[a]] -> [[b]]而不是类型fmap :: a -> b

原因是因为我想要映射到我的Block仿函数函数,例如transpose,它们适用于列表块[[a]]而不是每个元素a。也就是说,我希望能够定义像

这样的函数
transposeBlock :: Block a -> Block a
transposeBlock = (transpose <$>)

我试图将我的仿函数实例声明为以下内容。

instance Functor (Block [[a]]) where
  fmap f (Block x) = Block (f x)

但是我遇到了尝试编译它时出现的类型错误。

error:
• Expecting one fewer argument to ‘Block [[a]]’
  Expected kind ‘* -> *’, but ‘Block [[a]]’ has kind ‘*’
• In the first argument of ‘Functor’, namely ‘Block [[a]]’
  In the instance declaration for ‘Functor (Block [[a]])’

有哪些方法可以将函数映射到[[a]]类型的列表块Block

3 个答案:

答案 0 :(得分:9)

抱歉,您无法调用该功能fmap。但那没关系,还有很多其他好名字。

onBlock :: ([[a]] -> [[b]]) -> Block a -> Block b
onBlock f (Block v) = Block (f v)

如果你愿意的话,你甚至可以给它起一个充满错误标点符号的名字。

(<#>) :: ([[a]] -> [[b]]) -> Block a -> Block b
f <#> Block v = Block (f v)

答案 1 :(得分:3)

其他人已经评论过使用fmap的机会。

包装/展开newtype很无聊,但安全的强制可以立即实现。

import Data.Coerce

newtype Block a = Block [[a]]

onBlock :: ([[a]] -> [[b]]) -> Block a -> Block b
onBlock = coerce

确实,人们甚至可以完全避免onBlock,并根据需要直接使用coerce

答案 2 :(得分:2)

实现这一目标的最简单方法是

newtype BlockWr a = Block a deriving (Functor)
type Block a = BlockWr [[a]]

现在transposeBlock = (transpose <$>)可行。

但我不推荐这个。 BlockWr只是身份仿函数,这不是很有用。

真的,重点是Block aisomorphic[[a]]。你可以见证:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens
import Control.Lens.TH

newtype Block a = Block {_blockMat::[[a]]}
makeLenses ''Block

transposeBlock :: Block a -> Block a
transposeBlock = blockMat %~ transpose