Haskell允许在另一个类型周围定义一个新类型,从而创建一个
"type with context"。
因此,在诸如此类的情况下,可以区分(Int, Int)
data Time = Time (Int, Int)
- (h:m)和data Coord = Coord (Int, Int)
- (x,y)。
这些新类型将具有相关函数,知道包装的“基础”类型实际上是Int
。
更进一步,是通过在data
子句中使用“类型参数”来创建“通用”类型,就像在着名的monad:data Maybe t = Nothing | Just t
中一样。
这些泛型类型将被广泛的功能使用,并且用于
寻址many different needs,即:异常:可能,全局状态:状态,输入/输出:IO,非确定性:[],环境:读者,记录器:写入者。
这里出现了两个函数的便利:return :: a -> m a
用于构建类型a
周围的上下文,(>>=) :: m a -> (a -> m b) -> m b
用于基于先前{{1}自动拥有函数m a -> m b
}。这由monad类概括:
a -> m b
以便新的通用类型必须定义class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
和return
的含义,以使它们成为>>=
的实例。
在我看来,每种通用类型都会发生这种情况,所以具体问题是:
Monad
必须是Monad的实例吗?data MyThing t = ...
是Monad的实例是否方便?data MyThing t = ...
不能是Monad的实例?
(除了琐碎的案例)。为什么?data MyThing t = ...
,也是Monad?欢迎并期望以上解释的示例,更正和编辑。
感谢。
答案 0 :(得分:4)
举一个完全不是monad的例子,考虑
data Shower a = Shower (a -> String)
这是一个仿真器的相反(实际上是对偶)的一点:contravariant functor。
contramap :: Contravariant f => (a -> b) -> f b -> f a
contramap f (Shower q) = Shower (q . f)
将此与
进行比较fmap f (Identity x) = Identity (f $ x)
逆变仿函数不能是(非平凡的)monad,也不能是类似的东西。要了解原因,您需要考虑monad is, actually (i.e. in category theory):它是一种 endofunctors 的幺半群。它必须是 endo ,因为关键操作join :: m (m a) -> m a
意味着应用m
两次会使您处于同一类别。因为,如果你fmap
的任何一方A -> B
函数fmap f :: m A -> m B
fmap (fmap f) :: m (m A) -> m (m B)
,你会朝着同一方向前进:
duplicate :: w a -> w (w a)
(同样适用于comonads,它们也是(协变,而不是逆变)仿函数。这里,操作是contramap f :: q B -> q A
contramap (contramap f) :: q (q A) -> q (q B)
,反之亦然仍然必须保持相同的类别。)
对于逆变仿函数,这不起作用!原因是,
{{1}}
即。如果你迭代算子,它会在逆变和协变之间翻转。因此它不能形成任何诸如幺半群结构之类的东西。
答案 1 :(得分:3)
这是不 Monad的东西 - 它甚至不是Functor
。
-- from Data.Monoid
newtype Endo a = Endo { appEndo :: a -> a }
特别是Endo
的类型参数a
出现在逆变和协变位置 - 这种类型不能是Functor
或Contravariant
仿函数 - - 它必须是两者。
当然,如果你只是稍微概括一下,你会获得Reader
monad
newtype Reader r a = Reader { runReader :: r -> a }
因为我们现在已经将type参数的使用分开,使得一个是协变的,一个是逆变的。
显示Endo
不能是Functor
或Contravariant
,因为它必须是,是否有任何数据类型都是?有一个简单的技巧论证表明(1)有和(2)他们总是使用幻像参数。
我们将使用一个nullary数据类型(void
包提供一个,但它很容易重新实现)。关于nullary数据类型的有趣之处在于你可以使用一个来生成absurd
,这个函数接受一个不可能的参数并返回任何内容。
data Void = Void Void -- there are no values of Void
-- ... unless there are values of Void
absurd :: Void -> a
absurd (Void v) = absurd v
然后,结合Functor
和Contravariant
为我们提供了一个非常有趣的功能
contramap absurd :: Contravariant f => f a -> f Void
fmap absurd :: Functor f => f Void -> f a
fmap absurd . contramap absurd
:: (Contravariant f, Functor f) => f a -> f b
换句话说,它允许我们编写一种基于仿函数的coerce
,如果f
实际包含a
类型的任何值,则这是不可能的,因此我们知道这样的f
必须使用a
作为幻像参数。
data Constant b a = Constant { runConstant :: b }
coerceConstant :: Constant a -> Constant b
coerceConstant = Constant . runConstant
instance Functor (Constant b) where
fmap _ = coerceConstant
instance Contravariant (Constant b) where
contramap _ = coerceConstant
甚至为我们提供了一种实现非常无聊的Monad
instance Monoid b => Monad (Constant b) where
return _ = Constant mempty
c >>= _ = coerceConstant c
答案 2 :(得分:1)
参数化类型的概念比只有一个参数类型的特殊情况更简单。
所以,不,不是每种类型(*->*)
的类型都必须是monad而且也不方便。
一个例子:
data HomogenousTriple a = T a a a
为什么以及如何成为Monad?