我有一个数据类型可以跟踪一个'块'数字(有点像矩阵)。
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
?
答案 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 a
是isomorphic到[[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