在向某人解释什么是类型类X时,我很难找到正好是X的数据结构的好例子。
所以,我请求示例:
我认为Monad到处都有很多例子,但Monad的一个很好的例子与之前的例子有一些关系可以完成图片。
我查找的示例彼此相似,仅在属于特定类型类的重要方面有所不同。
如果有人能够设法在这个层次结构的某个地方隐藏一个Arrow的例子(它是在Applicative和Monad之间吗?),那也会很棒!
答案 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.Void
是Monoid
;
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 True
或Boo 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;值或无限数据类型或类似的东西。
通常,类型构造函数可能无法拥有某个类型类的实例有两个原因:
第一种示例比第二种示例更容易,因为对于第一种示例,我们只需要检查是否可以实现具有给定类型签名的函数,而对于第二种,我们需要证明没有任何实施可能会满足法律。
无法使用仿函数实例的类型构造函数,因为无法实现该类型:
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 b
或Nothing
时,没有自然的方法来实现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
也不符合法律规定。
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)
。这不是关联法律的问题,而是打破了身份法之一。
仿函数Branch
和Maybe (a, a)
都不能被赋予合法的Maybe (a, a, a)
个实例,尽管它们显然是Monad
。
这些仿函数没有任何技巧 - 任何地方都没有Applicative
或Void
,没有棘手的懒惰/严格,没有无限的结构,也没有类型类约束。 bottom
实例完全是标准的。函数Applicative
和return
可以为这些仿函数实现,但不符合monad的规律。换句话说,这些仿函数不是monad,因为缺少特定的结构(但不容易理解究竟缺少什么)。例如,函子的一个小变化可以使它成为一个monad:bind
是一个monad。另一个类似的仿函数data Maybe a = Nothing | Just a
也是一个monad。
一般来说,这里有一些结构可以用多项式类型产生合法的data P12 a = Either a (a, a)
。在所有这些结构中,Monad
是一个monad:
M
其中type M a = Either c (w, a)
是任何幺半群w
其中type M a = m (Either c (w, a))
是任何monad,m
是任何monoid w
其中type M a = (m1 a, m2 a)
和m1
是任何monad m2
其中type M a = Either a (m a)
是任何monad 第一个构造是m
,第二个构造是WriterT w (Either c)
。第三种结构是monads的组件产品:WriterT w (EitherT c m)
被定义为pure @M
和pure @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)
,a
和a
的乘积同构。此外,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没有其他结构。到目前为止,我还没有想到任何理论可以回答这个问题。