繁体   English   中英

Not a Functor/Functor/Applicative/Monad 的好例子?

[英]Good examples of Not a Functor/Functor/Applicative/Monad?

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

因此,我请求以下示例:

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

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

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

如果有人可以设法在这个层次结构中的某个地方偷偷摸摸一个 Arrow 的例子(它是在 Applicative 和 Monad 之间吗?),那也太棒了!

不是 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.Void是否是Monoid

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

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

有关底部(链接)或不安全函数(链接)的文章,请参阅 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 :: * -> * -> *

我的风格可能会被我的手机局促,但在这里。

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

不能是函子。 如果是这样,我们会有

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

而 Green 将由 Moon cheese 制成(这实际上可能发生,但只能在晚上晚些时候)。

(额外注意: 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。 要知道为什么不,请观察 return 必须始终为Boo TrueBoo False ,因此

join . return == id

不可能持有。

哦对了,差点忘了

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

是一个单子。 自己卷。

赶飞机...

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

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

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

但是没有办法如何定义它的Applicative实例而不对r施加额外的限制。 特别是,无法为任意r定义pure :: a -> (r, a)

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

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

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

so pure通过重复给定的值创建一个无限列表,并且<*>使用值列表压缩函数列表 - 将第i个函数应用于第i个元素。 []上的标准<*>产生将第i个函数应用于第j个元素的所有可能组合。)但是没有明智的方法来定义 monad(请参阅这篇文章)。


箭头如何适应 functor/applicative/monad 层次结构? 参见 Sam Lindley、Philip Wadler、Jeremy Yallop 的习语是无知的,箭头是细致的,单子是混杂的。 MSFP 2008。(他们称应用函子成语。)摘要:

我们重新审视计算的三个概念之间的联系:Moggi 的单子、Hughes 的箭头以及 McBride 和 Paterson 的习语(也称为应用函子)。 我们证明了习语等价于满足类型同构 A ~> B = 1 ~> (A -> B) 的箭头,并且单子等价于满足类型同构 A ~> B = A -> (1 ~ > B)。 此外,习语嵌入到箭头中,而箭头嵌入到单子中。

一个不是函子的类型构造函数的一个很好的例子是Set :你不能实现fmap :: (a -> b) -> fa -> fb ,因为没有额外的约束Ord b你不能构造fb

我想提出一种更系统的方法来回答这个问题,并展示一些不使用任何特殊技巧的例子,比如“底部”值或无限数据类型或类似的东西。

什么时候类型构造函数没有类型类实例?

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

  1. 无法从类型类实现所需方法的类型签名。
  2. 可以实现类型签名但不能满足所需的法律。

第一种的例子比第二种容易,因为对于第一种,我们只需要检查一个给定类型签名的函数是否可以实现,而对于第二种,我们需要证明没有实现可能满足法律。

具体例子

  • 由于无法实现类型而不能具有函子实例的类型构造函数

     data F za = F (a -> z)

对于类型参数a ,这是一个反函子,而不是函子,因为a处于逆变位置。 不可能实现具有类型签名(a -> b) -> F za -> F zb的函数。

  • 即使可以实现fmap的类型签名,也不是合法函子的类型构造函数

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

这个例子的奇怪之处在于我们可以实现正确类型的fmap ,即使F不可能是一个函子,因为它在逆变位置使用a a。 因此, fmap显示的fmap实现具有误导性 - 即使它具有正确的类型签名(我相信这是该类型签名的唯一可能实现),但不满足函子定律。 例如fmap idid ,因为let (Q(f,_)) = fmap id (Q(read,"123")) in f "456"123 ,但是let (Q(f,_)) = id (Q(read,"123")) in f "456"456

事实上, F只是一个 profunctor,——它既不是一个函子也不是一个反函子。

  • 一个不适用的合法函子,因为不能实现pure的类型签名:取 Writer monad (a, w)并删除w应该是幺半群的约束。 它是那么不可能构造类型的值(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的类型签名,也无法满足法律。

这个例子引起了很多讨论,所以可以肯定地说,证明这个例子是正确的并不容易。 但是有几个人已经通过不同的方法独立验证了这一点。 请参阅是`数据PoE a = 空| 一对aa`一个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 行为的原因是,当函数f :: a -> B b可以为不同的a值返回NothingJustNothing实现bind自然方式。

考虑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.

在用???表示的情况下 ,很明显,我们不能从类型a的六个不同值中以任何合理和对称的方式产生Just (z1, z2, z3) 我们当然可以选择这六个值的任意子集,例如,总是取第一个非空的Maybe - 但这将不满足单子定律。 返回Nothing也不会满足的法律。

  • 一个树状数据结构,它不是 monad,即使它具有bind关联性 - 但不符合恒等律。

通常的树状单子(或“具有函子形分支的树”)被定义为

 data Tr f a = Leaf a | Branch (f (Tr f a))

这是一个基于函子f的自由单子。 数据的形状是一棵树,其中每个分支点都是子树的“函子”。 标准二叉树将通过type fa = (a, a)

如果我们通过将叶子也做成函子f的形状来修改这个数据结构,我们就会得到我所说的“semimonad”——它具有满足自然性和结合律的bind ,但它的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)

接枝是标准的,但接叶是非标准的,产生一个Branch 这对于结合律来说不是问题,而是违反了恒等律之一。

多项式类型什么时候有 monad 实例?

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

这些函子没有技巧——任何地方都没有Voidbottom ,没有棘手的懒惰/严格,没有无限结构,也没有类型类约束。 Applicative实例是完全标准的。 函数returnbind可以为这些函子实现,但不满足 monad 的定律。 换句话说,这些函子不是 monad,因为缺少特定的结构(但并不容易理解到底缺少什么)。 举个例子,函子的一个小改动就可以使它变成一个 monad: data Maybe a = Nothing | Just a data Maybe a = Nothing | Just a是一个单子。 另一个类似的函子data P12 a = Either a (a, a)也是一个 monad。

多项式单子的构造

一般来说,这里有一些构造可以从多项式类型中产生合法的Monad 在所有这些结构中, M是一个 monad:

  1. type M a = Either c (w, a)其中w是任何幺半群
  2. type M a = m (Either c (w, a))其中m是任何单子, w是任何幺半群
  3. type M a = (m1 a, m2 a)其中m1m2是任何单子
  4. type M a = Either a (ma)其中m是任何 monad

第一个结构是WriterT w (Either c) ,第二个结构是WriterT w (EitherT cm) 第三种构造是 monad 的组件方式乘积: pure @M定义为pure @m1pure @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))

第四个结构定义为

 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

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

猜想多项式单子没有其他构造。 例如,函子Maybe (Either (a, a) (a, a, a, a))不是通过任何这些构造获得的,因此不是一元的。 然而, Either (a, a) (a, a, a)是单子的,因为它同构于三个单子aaMaybe a的乘积。 此外, Either (a,a) (a,a,a,a)是一元的,因为它同构于aEither a (a, a, a)的乘积。

以上所示的四个构造将允许我们获得任何数量的任何数量的产品的任何总和a的,例如Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))等等。 所有这样的类型构造函数都会有(至少一个) Monad实例。

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

一个问题仍然存在 - 给定任意多项式数据类型,如何识别它是否具有Monad实例。 我不知道如何证明多项式单子没有其他构造。 我认为到目前为止还没有任何理论可以回答这个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM