当一个泛型不会是单子吗?

Haskell允许围绕另一个定义新类型,从而创建一个“带有上下文的类型”。 因此,在例如data Time = Time (Int, Int) - (h:m)和data Coord = Coord (Int, Int) - (x,y)的情况下,可以区分(Int, Int) )。 这些新类型将具有相关功能,因为知道包装的“基本”类型实际上是一个Int

更进一步的是,通过在data子句中使用“类型参数”来创建“通用”类型,就像在着名的monad中一样: data Maybe t = Nothing | Just t data Maybe t = Nothing | Just t 。 这些通用类型将被广泛的功能所使用,并且用于满足许多不同的需求,即:异常:可能,全局状态:状态,输入/输出:IO,不确定性:[],环境:读取器,记录器:作家。 这里有两个函数: return :: a -> ma用于在类型a周围构建上下文,以及(>>=) :: ma -> (a -> mb) -> mb用于自动具有函数ma -> mb基于前a -> mb 。 monad类汇总了这一点:

class Monad m where
        return :: a -> m a
        (>>=)  :: m a -> (a -> m b) -> m b

所以新的泛型类型必须定义什么意思return>>=他们是Monad一个实例。

在我看来,对于每种泛型都会发生这种情况,所以具体的问题是:

  • 每个泛型类型data MyThing t = ... 必须是Monad的实例?
  • 至少在一般情况下,每个泛型类型data MyThing t = ...是Monad的一个实例是否方便?
  • 任何情况下,泛型类型data MyThing t = ...不能是Monad的实例吗? (除了微不足道的情况)。 为什么?
  • 有趣的是,即使是具体的上下文类型, data Time = Time (Int, Int) ,也是Monad?
  • 以上解释的例子,修正和编辑是受欢迎和期望的。

    谢谢。


    这不是一个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 }
    

    因为我们现在已经分开了类型参数的用法,例如一个是协变的,一个是逆变的。


    在证明Endo不能作为FunctorContravariant因为它必须是两者,是否有任何数据类型都是? 有一个简单的技巧参数表明(1)有和(2)他们总是使用幻影参数。

    我们将使用无数据类型( void包提供了一个,但很容易重新实现)。 关于空数据类型的有趣之处在于你可以使用一个产生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值,那么这是不可能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
    

    举个完全不是monad的例子,考虑一下

    data Shower a = Shower (a -> String)
    

    这是一个与functor相反的(实际上是双重的):一个逆变函子。

    contramap :: Contravariant f => (a -> b) -> f b -> f a
    
    contramap f (Shower q) = Shower (q . f)
    

    比较这一点

    fmap f (Identity x)  = Identity (f $ x)
    

    一个逆变函数不能是一个(平凡的)单子,也不是类似的东西。 要明白为什么,你需要考虑实际上(即在分类理论中)单子是什么:它是一种内生函数的幺半群。 它必须是endo,因为关键操作join :: m (ma) -> ma意味着应用m两次会使您处于同一类别中。 因为,如果你要在任何fmap对函数A -> B进行fmap ,你将会沿着相同的方向前进:

    fmap f        :: m A     -> m B
    fmap (fmap f) :: m (m A) -> m (m B)
    

    (对于comonads也是如此,它也是(协变的,不是逆向的)函数。在这里,操作是duplicate :: wa -> w (wa) ,这反过来却仍然保持在同一类别中。 )

    对于逆变函数,这是行不通的! 原因是,

    contramap f             :: q B     -> q A
    contramap (contramap f) :: q (q A) -> q (q B)
    

    即如果迭代函子,它会在逆变和协变之间不断翻转。 因此它不能形成任何像猿人结构这样的东西。


    参数化类型的概念比只有一个参数行为类似monad的类型的特例更简单。

    所以,不,不是每种类型(*->*)必须是monad,它也不方便。

    一个例子:

    data HomogenousTriple a = T a a a
    

    为什么以及该如何做Monad?

    链接地址: http://www.djcxy.com/p/77365.html

    上一篇: When a generic type would not be a monad?

    下一篇: Anatomy of a monad transformer