如何内联乘以自定义数据类型对象



创建此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应该能够将类型参数更改为类型构造函数 - 请注意abfmap的类型签名中如何完全任意。如果有...

data T
    = ConsInt Int
    | ConsString String
    | ConsChar Char

...然后,T值不可能包含IntStringChar以外的任何内容,因此无法满足其为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实例,但这除了这一点之外。(为了进行比较,这就是FunctorMaybe看起来像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的定义不能处理ConsStringConsChar案例,而忽略这几乎总是一个坏主意。修复方法的明显方法是为其他构造函数添加无需案例(就像在fmap中为Maybe完成的一样(;但是,根据您想做的事情,您可能更喜欢使函数返回Maybe T而不是T(在这种情况下,请注意,名称以" MAP"开头的名称不像以前那样适当(。

P.S。:正确实现的mapConsInt :: (Int -> Int) -> T -> T非常像fmap,只是仅限于特定功能类型。实际上,只要我们有一个不是 Functor的函数,实际上说我们正在与函数打交道并不是错误的。(由于上述原因,它不是Functor参见在Haskell中构建函数的惯用方法是什么?newtypes不是必需的(。

最新更新