泛型类型不是monad?

时间:2013-12-10 11:21:03

标签: haskell monads

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的含义,以使它们成为>>=的实例。

在我看来,每种通用类型都会发生这种情况,所以具体问题是:

  1. 每个通用类型Monad 必须是Monad的实例吗?
  2. 至少在一般情况下,每个通用类型data MyThing t = ...是Monad的实例是否方便?
  3. 是否通用类型data MyThing t = ...不能是Monad的实例? (除了琐碎的案例)。为什么?
  4. 有趣的是,即使是具体的上下文类型data MyThing t = ...,也是Monad?
  5. 欢迎并期望以上解释的示例,更正和编辑。

    感谢。

3 个答案:

答案 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出现在逆变和协变位置 - 这种类型不能是FunctorContravariant仿函数 - - 它必须是两者。

当然,如果你只是稍微概括一下,你会获得Reader monad

newtype Reader r a = Reader { runReader :: r -> a }

因为我们现在已经将type参数的使用分开,使得一个是协变的,一个是逆变的。


显示Endo不能是FunctorContravariant,因为它必须是,是否有任何数据类型都是?有一个简单的技巧论证表明(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

然后,结合FunctorContravariant为我们提供了一个非常有趣的功能

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?