不是Functor / Functor / Applicative / Monad的好例子?

时间:2011-08-28 10:42:53

标签: haskell monads functor applicative

在向某人解释什么是类型类X时,我很难找到正好是X的数据结构的好例子。

所以,我请求示例:

  • 不是Functor的类型构造函数。
  • 一个类型构造函数,它是一个Functor,但不是Applicative。
  • 一个类型构造函数,它是一个Applicative,但不是Monad。
  • 一个Monad的类型构造函数。

我认为Monad到处都有很多例子,但Monad的一个很好的例子与之前的例子有一些关系可以完成图片。

我查找的示例彼此相似,仅在属于特定类型类的重要方面有所不同。

如果有人能够设法在这个层次结构的某个地方隐藏一个Arrow的例子(它是在Applicative和Monad之间吗?),那也会很棒!

5 个答案:

答案 0 :(得分:93)

不是Functor的类型构造函数:

newtype T a = T (a -> Int)

你可以用它来制作逆变函子,但不能用(协变)函子。尝试写fmap,你就会失败。请注意,逆变仿函数版本是相反的:

fmap      :: Functor f       => (a -> b) -> f a -> f b
contramap :: Contravariant f => (a -> b) -> f b -> f a

作为仿函数的类型构造函数,但不是Applicative:

我没有一个很好的例子。有Const,但理想情况下我想要一个具体的非Monoid,我想不出任何。当您开始使用时,所有类型基本上都是数字,枚举,产品,总和或函数。你可以看到下面的pigworker,我不同意Data.VoidMonoid;

instance Monoid Data.Void where
    mempty = undefined
    mappend _ _ = undefined
    mconcat _ = undefined

由于_|_是Haskell中的合法值,并且实际上是Data.Void的唯一合法值,因此符合Monoid规则。我不确定unsafeCoerce与它有什么关系,因为一旦你使用任何unsafe函数,你的程序就不再保证不会违反Haskell语义。

有关底部(link)或不安全函数(link)的文章,请参阅Haskell Wiki。

我想知道是否可以使用更丰富的类型系统创建这样的类型构造函数,例如具有各种扩展的Agda或Haskell。

一个类型构造函数,它是一个Applicative,但不是Monad:

newtype T a = T {multidimensional array of a}

您可以使用以下内容制作一个Applicative:

mkarray [(+10), (+100), id] <*> mkarray [1, 2]
  == mkarray [[11, 101, 1], [12, 102, 2]]

但如果你把它作为monad,你可能会得到尺寸不匹配。我怀疑这样的例子在实践中很少见。

Monad的类型构造函数:

[]

关于箭头:

询问箭头在这个层次结构上的位置就像问“红色”是什么样的形状。注意那种不匹配:

Functor :: * -> *
Applicative :: * -> *
Monad :: * -> *

但是,

Arrow :: * -> * -> *

答案 1 :(得分:81)

我的手机可能会让我的风格变得狭窄,但这里也是如此。

newtype Not x = Kill {kill :: x -> Void}

不能是Functor。如果是的话,我们就有了

kill (fmap (const ()) (Kill id)) () :: Void

月亮将由绿色奶酪制成。

同时

newtype Dead x = Oops {oops :: Void}

是一个仿函数

instance Functor Dead where
  fmap f (Oops corpse) = Oops corpse

但不能申请,或者我们有

oops (pure ()) :: Void

和格林将由月亮奶酪制成(实际上可以发生,但只能在晚上播出)。

(额外注意:Void,如Data.Void中的数据类型为空。如果您尝试使用undefined来证明它是一个Monoid,我会使用unsafeCoerce证明它不是。)

愉快地,

newtype Boo x = Boo {boo :: Bool}

在许多方面都是适用的,例如Dijkstra会拥有它,

instance Applicative Boo where
  pure _ = Boo True
  Boo b1 <*> Boo b2 = Boo (b1 == b2)

但它不能是Monad。要了解原因,请注意返回必须经常Boo TrueBoo False,因此

join . return == id

不可能举行。

哦,是的,我差点忘了

newtype Thud x = The {only :: ()}

是Monad。滚动你自己。

飞机赶上......

答案 2 :(得分:67)

我相信其他答案错过了一些简单而常见的例子:

一个类型构造函数,它是一个Functor但不是Applicative。一个简单的例子是一对:

instance Functor ((,) r) where
    fmap f (x,y) = (x, f y)

但如果不对Applicative施加额外限制,则无法定义其r实例。特别是,如何为任意pure :: a -> (r, a)定义r

一个类型构造函数,它是一个Applicative,但不是Monad。一个众所周知的例子是ZipList。 (它是一个newtype包装列表并为它们提供不同的Applicative实例。)

fmap以通常的方式定义。但pure<*>定义为

pure x                    = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith id fs xs)

所以pure通过重复给定值来创建无限列表,<*>使用值列表压缩函数列表 - 将 i -th函数应用于< em> i -th元素。 (<*>上的标准[]生成了将 i -th函数应用于 j -th元素的所有可能组合。)但是没有理智的如何定义monad的方法(参见this post)。


箭头如何适应functor / applicative / monad层次结构? 见Sam Lindley的Idioms are oblivious, arrows are meticulous, monads are promiscuous,Philip Wadler,Jeremy Yallop。 MSFP 2008.(他们称应用函子成语。)摘要:

  

我们重新审视了三个计算概念之间的联系:Moggi的monad,Hughes的箭头和McBride和Paterson的习语(也称为applicative functors)。我们证明成语等同于满足类型同构A~>的箭头。 B = 1~> (A - > B)并且monad相当于满足类型同构A~>的箭头。 B = A - >; (1→B)。此外,成语嵌入箭头和箭头嵌入monad。

答案 3 :(得分:20)

不是仿函数的类型构造函数的一个很好的示例是Set:您无法实现fmap :: (a -> b) -> f a -> f b,因为没有额外的约束Ord b您无法构造f b 1}}。

答案 4 :(得分:7)

我想提出一个更系统的方法来回答这个问题,并且还要展示不使用任何特殊技巧的示例,例如&#34; bottom&#34;值或无限数据类型或类似的东西。

何时类型构造函数无法具有类型类实例?

通常,类型构造函数可能无法拥有某个类型类的实例有两个原因:

  1. 无法从类型类实现所需方法的类型签名。
  2. 可以实施类型签名,但不能满足要求的法律。
  3. 第一种示例比第二种示例更容易,因为对于第一种示例,我们只需要检查是否可以实现具有给定类型签名的函数,而对于第二种,我们需要证明没有任何实施可能会满足法律。

    具体例子

    • 无法使用仿函数实例的类型构造函数,因为无法实现该类型:

      data F a = F (a -> Int)
      

    这是一个反向函数,而不是一个仿函数,因为它在逆变位置使用类型参数a。实现类型签名为(a -> b) -> F a -> F b的函数是不可能的。

    • 一个非合法仿函数的类型构造函数,即使可以实现fmap的类型签名:

      data Q a = Q(a -> Int, a)
      fmap :: (a -> b) -> Q a -> Q b
      fmap f (Q(g, x)) = Q(\_ -> g x, f x)  -- this fails the functor laws!
      

    此示例的一个奇怪方面是我们可以实现正确类型的fmap,即使F不可能是仿函数,因为它使用a处于逆境中。因此,上面显示的fmap的这种实现具有误导性 - 即使它具有正确的类型签名(我相信这是该类型签名的唯一可能实现),但算子法则不满足(这需要一些简单的计算检查)。

    事实上,F只是一个变形金刚,它既不是算子也不是反向函数。

    • 一个不适用的合法仿函数,因为pure的类型签名无法实现:请使用Writer monad (a, w)并删除w的约束1}}应该是一个幺半群。然后,无法从(a, w)构建a类型的值。

    • 不适用的仿函数,因为<*>的类型签名无法实现:data F a = Either (Int -> a) (String -> a)

    • 即使可以实现类型类方法,也不是合法适用的仿函数

      data P a = P ((a -> Int) -> Maybe a)
      

    类型构造函数P是一个仿函数,因为它仅在协变位置使用a

    instance Functor P where
       fmap :: (a -> b) -> P a -> P b
       fmap fab (P pa) = P (\q -> fmap fab $ pa (q . fab))
    

    <*>类型签名的唯一可能实现是始终返回Nothing的函数:

     (<*>) :: P (a -> b) -> P a -> P b
     (P pfab) <*> (P pa) = \_ -> Nothing  -- fails the laws!
    

    但是这种实现方式不符合应用函子的身份定律。

    • Applicative但不是Monad 的仿函数,因为bind的类型签名无法实现。

    我不知道任何这样的例子!

    • 一个Applicative但不是Monad 的仿函数,因为即使可以实现bind的类型签名,也无法满足法律。

    这个例子已经产生了相当多的讨论,因此可以肯定地说,证明这个例子是正确的并不容易。但有几个人通过不同的方法独立验证了这一点。有关其他讨论,请参阅Is `data PoE a = Empty | Pair a a` a monad?

     data B a = Maybe (a, a)
       deriving Functor
    
     instance Applicative B where
       pure x = Just (x, x)
       b1 <*> b2 = case (b1, b2) of
         (Just (x1, y1), Just (x2, y2)) -> Just((x1, x2), (y1, y2))
         _ -> Nothing
    

    证明没有合法的Monad实例有点麻烦。非monadic行为的原因是,当函数bind可以为{{1}的不同值返回f :: a -> B bNothing时,没有自然的方法来实现Just }}

    考虑a(也不是monad)并试图为此实现Maybe (a, a, a)可能更清楚。人们会发现没有直观合理的方式来实施join

    join

    join :: Maybe (Maybe (a, a, a), Maybe (a, a, a), Maybe (a, a, a)) -> Maybe (a, a, a) join Nothing = Nothing join Just (Nothing, Just (x1,x2,x3), Just (y1,y2,y3)) = ??? join Just (Just (x1,x2,x3), Nothing, Just (y1,y2,y3)) = ??? -- etc. 指示的情况下,我们似乎无法以六种不同的???类型的值以任何合理和对称的方式生成Just (z1, z2, z3)。我们当然可以选择这六个值中的一些任意子集 - 例如,总是采用第一个非空a - 但这不符合monad的定律。返回Maybe也不符合法律规定。

    • 树状数据结构不是monad ,即使它与Nothing具有关联性 - 但未通过身份法律。

    通常的树状monad(或#34;带有仿函数形状的树枝的树#34;)定义为

    bind

    这是一个关于仿函数 data Tr f a = Leaf a | Branch (f (Tr f a)) 的免费monad。数据的形状是一棵树,每个分支点都是一个&#34; functor-ful&#34;子树。标准二叉树将使用f获得。

    如果我们通过创建仿函数type f a = (a, a)形状的叶子来修改这个数据结构,我们就可以得到我称之为&#34; semimonad&#34; - 它f满足自然性和相关性定律,但其bind方法未通过其中一个身份定律。 &#34; Semimonads是endofunctors类别中的半群,问题是什么?&#34;这是类型类pure

    为简单起见,我定义Bind方法而不是join

    bind

    分枝嫁接是标准的,但叶嫁接是非标准的,产生 data Trs f a = Leaf (f a) | Branch (f (Trs f a)) join :: Trs f (Trs f a) -> Trs f a join (Leaf ftrs) = Branch ftrs join (Branch ftrstrs) = Branch (fmap @f join ftrstrs) 。这不是关联法律的问题,而是打破了身份法之一。

    多项式类型何时具有monad实例?

    仿函数BranchMaybe (a, a)都不能被赋予合法的Maybe (a, a, a)个实例,尽管它们显然是Monad

    这些仿函数没有任何技巧 - 任何地方都没有ApplicativeVoid,没有棘手的懒惰/严格,没有无限的结构,也没有类型类约束。 bottom实例完全是标准的。函数Applicativereturn可以为这些仿函数实现,但不符合monad的规律。换句话说,这些仿函数不是monad,因为缺少特定的结构(但不容易理解究竟缺少什么)。例如,函子的一个小变化可以使它成为一个monad:bind是一个monad。另一个类似的仿函数data Maybe a = Nothing | Just a也是一个monad。

    多项式monad的构造

    一般来说,这里有一些结构可以用多项式类型产生合法的data P12 a = Either a (a, a)。在所有这些结构中,Monad是一个monad:

    1. M其中type M a = Either c (w, a)是任何幺半群
    2. w其中type M a = m (Either c (w, a))是任何monad,m是任何monoid
    3. w其中type M a = (m1 a, m2 a)m1是任何monad
    4. m2其中type M a = Either a (m a)是任何monad
    5. 第一个构造是m,第二个构造是WriterT w (Either c)。第三种结构是monads的组件产品:WriterT w (EitherT c m)被定义为pure @Mpure @m1的组件明智产品,pure @m2通过省略交叉定义产品数据(例如join @M通过省略元组的第二部分映射到m1 (m1 a, m2 a)

      m1 (m1 a)

      第四种结构定义为

       join :: (m1 (m1 a, m2 a), m2 (m1 a, m2 a)) -> (m1 a, m2 a)
       join (m1x, m2x) = (join @m1 (fmap fst m1x), join @m2 (fmap snd m2x))
      

      我已经检查过所有四种结构都会生成合法的monad。

      猜想多项式monad没有其他构造。例如,仿函数 data M m a = Either a (m a) instance Monad m => Monad M m where pure x = Left x join :: Either (M m a) (m (M m a)) -> M m a join (Left mma) = mma join (Right me) = Right $ join @m $ fmap @m squash me where squash :: M m a -> m a squash (Left x) = pure @m x squash (Right ma) = ma 不是通过任何这些结构获得的,因此不是monadic。但是,Maybe (Either (a, a) (a, a, a, a))是monadic,因为它与三个monad Either (a, a) (a, a, a)aa的乘积同构。此外,Maybe a是monadic,因为它与Either (a,a) (a,a,a,a)a的乘积同构。

      上面显示的四种结构将允许我们获得任意数量Either a (a, a, a)的任意数量的产品的总和,例如a,等等。所有这些类型构造函数都将具有(至少一个)Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))实例。

      当然,还有待观察,这些monad可能存在哪些用例。另一个问题是通过结构1-4导出的Monad实例通常不是唯一的。例如,可以通过两种方式为类型构造函数Monad提供type F a = Either a (a, a)实例:使用monad Monad构造4,使用类型isomorphism (a, a)构造3 。同样,找到这些实现的用例并不是很明显。

      问题仍然存在 - 给定任意多项式数据类型,如何识别它是否具有Either a (a, a) = (a, Maybe a)实例。我不知道如何证明多项式monad没有其他结构。到目前为止,我还没有想到任何理论可以回答这个问题。