创建此Haskell Wiki页面中描述的基于数据型的异质列表,可以将数据类型T
定义为:
data T = ConsInt Int
| ConsString String
| ConsChar Char
然后使用以下方式创建一个对象:
ConsInt 200
然后如何将对象T
倍增?如果我们使用类型的构造函数Maybe
如:
Just 200
我们可以使用fmap (*2) (Just 200)
将其乘以2并输出Just 400
。
执行fmap (*2) (ConsInt 200)
不起作用。(我猜是因为 T
不是类型构造函数,而是数据类型(
那么,如何将一个 T
对象插入,而无需手工定义函数:
mult2 :: T -> T
mult2 (ConsInt r) = ConsInt ((*2) r)
或如何使用更简单的匿名函数:
((ConsInt r) -> ConsInt ((*2) r)) (ConsInt 200)
可运行的示例
执行
fmap (*2) (ConsInt 200)
不起作用。(我猜是因为T
不是 type构造函数,而是数据类型(
这是原因的一部分。fmap (*2) (Just 200)
之所以可以使用,是因为Maybe
是一种类型的构造函数,是Functor
的实例:
GHCi> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
GHCi> :set -XTypeApplications
GHCi> :t fmap @Maybe
fmap @Maybe :: (a -> b) -> Maybe a -> Maybe b
Functor
s必须是类型的构造函数,它采用类型并产生另一种类型(即,他们必须具有友好的 * -> *
(:
GHCi> :k Functor
Functor :: (* -> *) -> Constraint
是这样,因为大致说, fmap
应该能够将类型参数更改为类型构造函数 - 请注意a
和b
在fmap
的类型签名中如何完全任意。如果有...
data T
= ConsInt Int
| ConsString String
| ConsChar Char
...然后,T
值不可能包含Int
,String
或Char
以外的任何内容,因此无法满足其为Functor
的要求。如果您真的想坚持将其制作为Functor
,那么一个行动将在Int
中戳一个孔:
-- You can actually write a Functor instance for this:
data T a
= ConsStuff a
| ConsString String
| ConsChar Char
尽管在其他情况下这可能是有用的重构,但我很确定您不想在这里这样做。
那么,如何将
T
对象插入,而无需手工定义函数[...]或如何使用简单的匿名函数,而不是[...] <...]
请注意,如果您要给T
一个Functor
实例,则fmap
的实现与您在此处思考的功能大致复杂。(我忽略了这样一个事实,即,由于DeriveFunctor
扩展,您实际上永远不需要编写Functor
实例,但这除了这一点之外。(为了进行比较,这就是Functor
的Maybe
看起来像CC_33:
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
如果您想要比mult2 :: T -> T
更通用的东西,则可以定义mapConsInt :: (Int -> Int) -> T -> T
。顺便说一句,请注意,您对mult2
的定义不能处理ConsString
和ConsChar
案例,而忽略这几乎总是一个坏主意。修复方法的明显方法是为其他构造函数添加无需案例(就像在fmap
中为Maybe
完成的一样(;但是,根据您想做的事情,您可能更喜欢使函数返回Maybe T
而不是T
(在这种情况下,请注意,名称以" MAP"开头的名称不像以前那样适当(。
P.S。:正确实现的mapConsInt :: (Int -> Int) -> T -> T
非常像fmap
,只是仅限于特定功能类型。实际上,只要我们有一个不是 Functor
的函数,实际上说我们正在与函数打交道并不是错误的。(由于上述原因,它不是Functor
参见在Haskell中构建函数的惯用方法是什么?newtypes不是必需的(。