我一直在浏览打字教程来学习打字类。我被困在理解替代
(和MonadPlus
,就此而言)。
我遇到的问题是:
>
《pedia》说,“可选类型类是为同样具有幺半群结构的应用程序函子设计的。”我不明白,替代品不是意味着完全不同于单倍体吗?i、 e.我理解另类类的意义在于在两个事物之间进行选择,而我理解幺半群是关于组合事物的。
为什么Alternative需要一个空的
方法/成员?我可能错了,但它似乎根本没有被使用。。。至少在我能找到的代码中。而且它似乎不符合课程的主题——如果我有两样东西,需要挑一样,我需要一个“空的”做什么?
为什么替代类型类需要一个应用程序约束,为什么它需要一种
*-
MonadPlus
type类有什么意义?难道我不能通过同时使用Monad
和Alternative
来释放它所有的优点吗?为什么不干脆放弃呢?(我肯定我错了,但我没有任何反例)
希望所有这些问题都是连贯的...!
赏金更新:@Antal的答案是一个很好的开始,但第三季度仍然开放:替代方案提供了什么,而Monoid没有?我觉得这个答案并不令人满意,因为它缺乏具体的例子,也没有具体讨论替代品的更高善良性如何将其与幺半群区分开来。
如果要将applicative的效果与Monoid的行为结合起来,为什么不:
liftA2 mappend
这对我来说更令人困惑,因为许多幺半群实例与其他实例完全相同。
这就是为什么我要寻找一些具体的例子来说明为什么选择是必要的,以及它与幺半群有什么不同,或者说有什么不同。
>
我们需要为一些应用函子定义(提供与之相同的操作的实例)幺半群实例,它们在应用函子级别真正结合,而不仅仅是提升较低级别的幺半群。下面的示例错误来自litvar=liftA2 mappend literal变量
如果我们直接使用Monid,我们需要语言扩展来定义实例。
替代
是更高类型的,所以您可以在不需要语言扩展的情况下制作这些实例。
让我们想象一下,我们正在解析一些声明,所以我们导入了所有我们需要的东西
import Text.Parsec
import Text.Parsec.String
import Control.Applicative ((<$>),(<*>),liftA2,empty)
import Data.Monoid
import Data.Char
想想我们将如何解析类型。我们选择简单化:
data Type = Literal String | Variable String deriving Show
examples = [Literal "Int",Variable "a"]
现在让我们为文字类型编写一个解析器:
literal :: Parser Type
literal = fmap Literal $ (:) <$> upper <*> many alphaNum
含义:解析一个
大写
大小写字符,然后许多alphaNum
eric字符,将结果与纯函数(:)
组合成一个字符串。然后,应用纯函数Literal
将那些String
s转换为Type
s。我们将以完全相同的方式解析变量类型,除了以小写字母开头:
variable :: Parser Type
variable = fmap Variable $ (:) <$> lower <*> many alphaNum
这很好,
parseTest literal“Bool”==literal“Bool”
正是我们所希望的。
编辑:哎呀-忘记实际使用
types :: Parser Type
types = literal <|> variable
这可以解析任何类型:
parseTest类型“Int”==Literal“Bool”
和parseTest类型“a”==Variable“a”
。这将组合两个解析器,而不是两个值。这就是它在应用程序函子级别而不是数据级别工作的意义。
然而,如果我们尝试:
litvar = liftA2 mappend literal variable
这将要求编译器在数据级别组合它们生成的两个值。我们得到
No instance for (Monoid Type)
arising from a use of `mappend'
Possible fix: add an instance declaration for (Monoid Type)
In the first argument of `liftA2', namely `mappend'
In the expression: liftA2 mappend literal variable
In an equation for `litvar':
litvar = liftA2 mappend literal variable
所以我们发现了第一件事;Alternative类与
liftA2 mappend
做了一些真正不同的事情,因为它在不同的级别上组合了对象——它组合了解析器,而不是解析的数据。如果你喜欢这样想,这是真正更高层次的结合,而不仅仅是提升。我不喜欢这样说,因为解析器类型
有kind*
,但确实可以说我们是在组合解析器
s,而不是类型
s。
(即使对于具有Monoid实例的类型,
liftA2-mappend
也不会提供与
首先,您可以正确地注意到,它并没有在单片实例上提供新功能。
然而,第二,直接使用幺半群有一个问题:让我们试着在解析器上使用
mappend
,同时显示它与Alternative
的结构相同:
instance Monoid (Parser a) where
mempty = empty
mappend = (<|>)
哎呀!我们得到
Illegal instance declaration for `Monoid (Parser a)'
(All instance types must be of the form (T t1 ... tn)
where T is not a synonym.
Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Monoid (Parser a)'
因此,如果您有一个应用仿函数
f
,替代
实例显示f a
是一个单数,但您只能声明为带有语言扩展名的单数
。
一旦我们在文件顶部添加了
{-#LANGUAGE TypeSynonym实例#-}
,我们就可以定义
typeParser = literal `mappend` variable
让我们高兴的是,它可以工作:
parseTest typeParser“Yes”==Literal“Yes”
和parseTest typeParser“a”==Literal“a”
。
即使您没有任何同义词(
Parser
和String
是同义词,所以它们已经过时了),您仍然需要{-#LANGUAGE Flexible实例#-}
来定义这样的实例:
data MyMaybe a = MyJust a | MyNothing deriving Show
instance Monoid (MyMaybe Int) where
mempty = MyNothing
mappend MyNothing x = x
mappend x MyNothing = x
mappend (MyJust a) (MyJust b) = MyJust (a + b)
(的幺半群实例可能通过提升基础幺半群来解决这个问题。)
使标准库不必要地依赖于语言扩展显然是不可取的。
所以你有了。另类只是应用函数的单元体(不仅仅是单元体的提升)。它需要高级类型
f a-
>
MonadPlus类型类有什么意义?难道我不能通过使用单子和替代品来释放它的所有优点吗?不,我让你回到你链接的问题:
此外,即使Applicative是Monad的一个超类,您最终还是需要MonadPlus类,因为遵循
empty
....我举了一个例子:也许吧。我在回答安塔尔的问题时做了详细的解释,并提供了证据。为了回答这个问题,值得注意的是,我能够使用
幺半群结构很有用。Alternative是为应用函子提供它的最佳方式。
import Data.Monoid
import Control.Applicative
让我们通过一个例子来追溯一下幺半群和另类是如何与Maybe
函子和ZipList
函子相互作用的,但是让我们从头开始,一部分是为了让所有的定义都在我们的脑海中保持新鲜,一部分是为了停止一直把标签切换到一些黑客行为,但主要是为了让我能通过ghci来纠正我的打字错误!
(<>) :: Monoid a => a -> a -> a
(<>) = mappend -- I'll be using <> freely instead of `mappend`.
以下是克隆人:
data Perhaps a = Yes a | No deriving (Eq, Show)
instance Functor Perhaps where
fmap f (Yes a) = Yes (f a)
fmap f No = No
instance Applicative Perhaps where
pure a = Yes a
No <*> _ = No
_ <*> No = No
Yes f <*> Yes x = Yes (f x)
现在是ZipList:
data Zip a = Zip [a] deriving (Eq,Show)
instance Functor Zip where
fmap f (Zip xs) = Zip (map f xs)
instance Applicative Zip where
Zip fs <*> Zip xs = Zip (zipWith id fs xs) -- zip them up, applying the fs to the xs
pure a = Zip (repeat a) -- infinite so that when you zip with something, lengths don't change
也许克隆
首先让我们看看也许是String
。有两种方法可以将它们结合起来。一级串联
(<++>) :: Perhaps String -> Perhaps String -> Perhaps String
Yes xs <++> Yes ys = Yes (xs ++ ys)
Yes xs <++> No = Yes xs
No <++> Yes ys = Yes ys
No <++> No = No
通过将No
视为Yes[]
,连接本质上在String级别工作,而不是真正的也许级别。它等于liftA2()
。这是明智和有用的,但是也许我们可以从仅仅使用概括到使用任何组合的方式——那么是任何单体型!
(<++>) :: Monoid a => Perhaps a -> Perhaps a -> Perhaps a
Yes xs <++> Yes ys = Yes (xs `mappend` ys)
Yes xs <++> No = Yes xs
No <++> Yes ys = Yes ys
No <++> No = No
这个用于也许
的单值结构试图在a
级别尽可能多地工作。注意Monodea
约束,告诉我们我们使用的是a
级别的结构。这不是一个替代结构,它是一个派生的(提升的)整体结构。
instance Monoid a => Monoid (Perhaps a) where
mappend = (<++>)
mempty = No
在这里,我使用了数据a的结构来为整个过程添加结构。如果我将Set
s组合起来,我就可以添加Ord a
上下文。
下载克隆
那么,我们应该如何将元素与zipList结合起来呢?如果我们把它们结合起来,它们应该拉链到什么地方?
Zip ["HELLO","MUM","HOW","ARE","YOU?"]
<> Zip ["this", "is", "fun"]
= Zip ["HELLO" ? "this", "MUM" ? "is", "HOW" ? "fun"]
mempty = ["","","","",..] -- sensible zero element for zipping with ?
但是应该使用什么呢
。我说这里唯一明智的选择是
。实际上,对于列表,
(
Zip [Just 1, Nothing, Just 3, Just 4]
<> Zip [Just 40, Just 70, Nothing]
= Zip [Just 1 ? Just 40, Nothing ? Just 70, Just 3 ? Nothing]
mempty = [Nothing, Nothing, Nothing, .....] -- sensible zero element
但是我们能为
使用什么呢
我说我们应该组合元素,所以我们应该再次使用Monoid中的元素组合操作符:
instance Monoid a => Monoid (Zip a) where
Zip as `mappend` Zip bs = Zip (zipWith (<>) as bs) -- zipWith the internal mappend
mempty = Zip (repeat mempty) -- repeat the internal mempty
这是使用zip组合元素的唯一合理方法,因此它是唯一合理的monoid实例。
有趣的是,这不适用于上面的示例,因为Haskell不知道如何组合
Int
s——它应该使用还是
*
?要获得数值数据上的幺半群实例,可以将它们包装在Sum
或Product
中,告诉它使用哪个幺半群。
Zip [Just (Sum 1), Nothing, Just (Sum 3), Just (Sum 4)] <>
Zip [Just (Sum 40), Just (Sum 70), Nothing]
= Zip [Just (Sum 41),Just (Sum 70), Just (Sum 3)]
Zip [Product 5,Product 10,Product 15]
<> Zip [Product 3, Product 4]
= Zip [Product 15,Product 40]
重点
请注意,单片中的类型具有类型
*
,这正是允许我们将单片上下文放在这里的原因——我们还可以添加
Eq a
或Ord a
。在单片中,原始元素很重要。Monode实例旨在让您操作和组合结构中的数据。
choice运算符类似,但也不同。
也许克隆
(<||>) :: Perhaps String -> Perhaps String -> Perhaps String
Yes xs <||> Yes ys = Yes xs -- if we can have both, choose the left one
Yes xs <||> No = Yes xs
No <||> Yes ys = Yes ys
No <||> No = No
这里没有串联-我们根本没有使用
-这个组合纯粹在
也许
级别工作,所以让我们将类型签名更改为
(<||>) :: Perhaps a -> Perhaps a -> Perhaps a
Yes xs <||> Yes ys = Yes xs -- if we can have both, choose the left one
Yes xs <||> No = Yes xs
No <||> Yes ys = Yes ys
No <||> No = No
请注意,这里没有约束——我们没有使用
a
级别的结构,只使用级别的结构。这是另一种结构。
instance Alternative Perhaps where
(<|>) = (<||>)
empty = No
下载克隆
我们应该如何在两个拉链之间进行选择?
Zip [1,3,4] <|> Zip [10,20,30,40] = ????
这将是非常诱人的使用
Zip [] <|> Zip ys = Zip ys
Zip xs <|> Zip [] = Zip xs
Zip[1,3,4]有两个明智的选择
Zip[1,3,4]
因为它是第一个-可能与
Zip[10,20,30,40]
因为它最长——与被丢弃的Zip[]
一致
这很容易决定:因为
pure x=Zip(repeat x)
,两个列表可能是无限的,所以比较它们的长度可能永远不会终止,所以必须选择第一个。因此,唯一合理的替代实例是:
instance Alternative Zip where
empty = Zip []
Zip [] <|> x = x
Zip xs <|> _ = Zip xs
这是我们可以定义的唯一明智的替代方案。请注意,它与单片实例有多么不同,因为我们不能乱动元素,甚至不能查看它们。
重点
请注意,因为
Alternative
采用了*-
不是很多——它们都是幺半群,但总结一下最后两部分:
Monoid*
实例使组合内部数据成为可能<代码>备选方案(*-
这是正确的事情,我们的两种口味都很合适。
也许String
的单数实例表示将所有字符组合在一起,替代实例表示在Strings之间进行选择。
比如,幺半群实例并没有什么错——它正在做自己的工作,合并数据<另一个例子也许没什么错——它在做自己的工作,在事情之间做出选择。
Zip的Monoid实例结合了它的元素。Zip的另一个实例被迫选择其中一个列表——第一个非空列表。
两者兼得很好。
选择和应用之间有一些互动。见安塔尔S Z定律,在他的问题或在回答的中间。
从实用的角度来看,它很有用,因为Alternative是一些应用程序函子可以选择的东西。该功能被用于应用程序,因此发明了一个通用接口类。应用函子很适合表示产生值的计算(IO、解析器、输入UI元素等)他们中的一些人必须应对失败——需要其他选择。
为什么Alternative需要一个空的方法/成员?我可能错了,但它似乎根本没有被使用。。。至少在我能找到的代码中。而且它似乎不符合课程的主题——如果我有两样东西,需要挑一样,我需要一个“空的”做什么?
这就好比问为什么加法需要0——如果你想添加东西,那么拥有一个不添加任何东西的东西又有什么意义呢?答案是0是一个关键的数字,所有的东西都围绕着它旋转,就像1对于乘法是至关重要的,
[]
对于列表是至关重要的(而y=e^x
对于微积分是至关重要的)。实际上,你可以使用这些不做任何事情的元素来开始你的建筑:
sum = foldr (+) 0
concat = foldr (++) []
msum = foldr (`mappend`) mempty -- any Monoid
whichEverWorksFirst = foldr (<|>) empty -- any Alternative
MonadPlus类型类有什么意义?难道我不能通过使用单子和替代品来释放它的所有优点吗?为什么不干脆放弃呢?(我肯定我错了,但我没有任何反例)
你没看错,没有反例!
你有趣的问题让安塔尔·S-Z、彼得·普德拉克和我深入研究了MonadPlus和Applicationive之间的关系。这里和这里的答案是,任何属于
MonadPlus
(在左边的发行意义上,请关注链接了解详细信息)的东西都是替代品,但不是相反。
这意味着,如果您创建Monad和MonadPlus的实例,它无论如何都满足应用程序和替代程序的条件。这意味着如果你遵循MonadPlus的规则(带左dist),你也可以让你的Monad成为一个应用程序并使用替代程序。
但是,如果我们删除MonadPlus类,我们就删除了一个合理的地方来记录规则,那么您就失去了在不使用MonadPlus的情况下指定某个对象的替代方案的能力(从技术上讲,我们应该这样做)。这些都是理论上的原因。实际原因是它会破坏现有的代码。(这也是为什么Applicative和Functor都不是Monad的超类。)
'pedia说"替代类型类适用于同样具有单数结构的应用函数。"我不明白——另类是不是意味着和蒙诺完全不同的东西?即。我理解另类类型类的重点是在两件事情之间进行选择,而我理解单元体是关于组合事物。
Monoid和Alternative是以合理的方式从两个对象中获取一个对象的两种方法。数学不在乎你是在选择、组合、混合还是放大你的数据,这就是为什么Alternative被称为Applicative的幺半群。你现在似乎对这个概念很熟悉,但你现在说
对于同时具有替代实例和单片实例的类型,实例旨在是相同的
我不同意这一点,我认为我的也许和ZipList例子已经被仔细解释了为什么它们不同。如果有什么不同的话,我认为它们是一样的应该是罕见的。我只能想到一个合适的例子,简单的列表。这是因为列表是具有
的单元体的基本示例,但是列表在某些上下文中也被用作元素的不确定选择,所以
首先,让我对每一个问题给出简短的回答。然后,我将把每一个问题扩展成一个更详细的答案,但这些简短的答案有望帮助我们更好地理解这些问题。
>
不,另类
和单数
的意思不一样;另类
适用于同时具有应用
和单数
结构的类型。“采摘”和“组合”是对同一个更广泛概念的两种不同直觉。
备选方案
包含空
以及
我们需要
替代
和幺半群
,因为前者比后者遵守(或应该遵守)更多的法律;这些定律与类型构造函数的幺半群结构和应用结构有关。另外,Alternative
不能依赖于内部类型,而Monoid
可以。
MonadPlus
比Alternative
略强,因为它必须遵守更多的法律;除了应用结构之外,这些定律还将单倍体结构与一元结构联系起来。如果两种情况都有,它们应该是一致的。
难道
替代
的意思与单元体
完全不同吗?
不是真的!造成您困惑的部分原因是Haskell
Monoid
类使用了一些非常糟糕(嗯,不够笼统)的名称。这就是数学家定义幺半群的方式(非常明确):
释义幺半群是带有可分辨元素ε的集合M∈ M和一个二元算子·:M×M→ M、 通过并置来表示,使得以下两个条件成立:
ε是恒等式:对于所有m∈ M、 Mε=εM=M.
就这样了。在Haskell中,ε被拼写为
mair
,·被拼写为mappend
(或者,现在,
看看这个定义,我们发现它没有提到“组合”(或者“挑选”)。它说的是关于·和ε,但仅此而已。现在,将事物与这个结构结合起来肯定是正确的:ε对应于没有事物,而m₁M₂ 说如果我幸灾乐祸₁ 还有m₂’把他们的东西放在一起,我就能得到一个包含他们所有东西的新东西。但这里有另一种直觉:ε表示根本没有选择,而m₁M₂ 对应于m和m之间的选择₁ 还有m₂. 这就是“挑选”的直觉。请注意,两者都遵守幺半群定律:
一无所有和别无选择都是身份。
- 如果我没有东西,把它和一些东西混在一起,我最终会再次得到同样的东西。
- 如果我在根本没有选择(不可能的事情)和其他选择之间有选择,我必须选择另一个(可能的)选择。
- 如果我有三套东西,那么前两套再加上第三套,或者后两套再加上第一套都无关紧要;不管怎样,我最终还是得到了同样的全套格洛姆收藏
- 如果我可以在三件事中做出选择,那么我(a)首先选择第一件、第二件和第三件,然后,如果我需要,选择第一件和第二件,或者(b)首先选择第一件和第二件或第三件,然后,如果我需要,选择第二件和第三件,都无关紧要。不管怎样,我都可以选择我想要的
(注意:我在这里玩的是快速和宽松的游戏,这就是为什么它是直觉。例如,重要的是要记住·不需要是可交换的,这上面掩盖了:完全可能₁M₂ ≠ M₂M₁.)
看,这两种情况(还有许多其他情况是将数字相乘,真的是“组合”或“挑选”?)遵守同样的规则。直觉对于理解能力的发展很重要,但是规则和定义决定了实际发生的事情。
最棒的是,这两种直觉可以由同一个载体来解释!设M是一些集合(不是所有集合的集合!)包含空集,设ε为空集∅, 让我们团结起来∪. 很容易看出这一点∅ 是你的身份∪, 而且∪ 是关联的,所以我们可以得出结论(M,∅,∪) 是一个幺半群。现在:
- 如果我们认为集合是事物的集合∪ 对应于将它们拼凑在一起以获得更多的东西,即“组合”直觉
- 如果我们认为集合代表可能的动作∪ 对应于从“挑选”直觉中挑选更多可能的行动
这正是Haskell中的[]
所发生的:[a]
是所有a
的单元体
,并且[]
作为应用仿(和单元体)使用代表不确定性。组合和选择直觉在相同的类型上一致:mair=空 = []
和mappend=(
因此,Alternative
类只是用来表示对象,这些对象(a)是应用函数,以及(b)当在类型上实例化时,它们上面有一个值和一个遵循一些规则的二进制函数。什么规则?幺半群规则。为什么?因为它被证明是有用的:-)
为什么替代方法需要空方法/成员?
好吧,尖刻的答案是“因为Alternative
代表一个幺半群结构。”但真正的问题是:为什么是幺半群结构?为什么不只是一个半群,一个没有ε的幺半群?一个答案是声称幺半群更有用。我想很多人(但也许不是爱德华·科米特)会同意这一点;如果你有一个合理的(
你还想知道它是如何与“拾取”相匹配的
所有这些都说了,请记住,完全有可能有一个类几乎像替代
,但缺少空
。这是完全有效的——它甚至可能是一个超类——但碰巧不是哈斯克尔所做的。大概这是出于对什么有用的猜测。
为什么替代类型类需要一个应用程序
约束,为什么它需要一种*-
那么,让我们来考虑这三个建议的变化:摆脱<代码>应用程序< /代码>约束<代码>备选< /代码>;改变
备选方案
的参数类型;并使用liftA2 mappend
代替
fempty :: (Applicative f, Monoid a) => f a
fempty = pure mempty
(>|<) :: (Applicative f, Monoid a) => f a -> f a -> f a
(>|<) = liftA2 mappend
我们甚至可以保留some
和many
的定义。这确实给了我们一个幺半群结构,这是真的。但它似乎给了我们错误的答案。应该只是fst吗
既然我们想把Alternative
作为某种类型的类,那么让我们来看看两种修改它的方法。如果我们改变类型,我们必须摆脱应用性的约束<代码>应用程序
只谈论同类事物*-
class Alternative' f where
empty' :: f a
(<||>) :: f a -> f a -> f a
另一个更大的变化是摆脱Applicative
约束,改变类型:
class Alternative'' a where
empty'' :: a
(<|||>) :: a -> a -> a
在这两种情况下,我们都必须摆脱一些
/许多
,但没关系;我们可以将它们定义为类型为的独立函数(应用程序f,替代程序f)=
现在,在第二种情况下,我们改变了类型变量的种类,我们看到我们的类与幺半群
完全相同(或者,如果您仍然想删除空的“
,半群
),所以使用单独的类没有好处。事实上,即使我们不使用kind变量,而是删除了Applicative
约束,Alternative
对于所有a.Monoid(fa)
,也只是变成了,尽管我们无法在Haskell中编写这些量化的约束,即使使用所有花哨的GHC扩展也是如此。(请注意,这表达了上述不可知论的内在类型。)因此,如果我们可以做出这些改变中的任何一个,那么我们就没有理由保留备选方案
(除了能够表达量化的约束,但这似乎很难令人信服)。
因此,问题可以归结为“在可选
部分和应用
部分之间是否存在关系,这是f
的一个实例?”虽然文件中没有任何内容,但我会站出来说是的,或者至少应该有。我认为,备选方案
应该遵守一些与应用
相关的法律(除了幺半群法律);特别是,我认为这些法律
权利分配(的<代码>
这些定律似乎适用于[]
和可能
,并且(假装它的MonadPlus
实例是替代
实例)IO
,但我还没有做任何证明或详尽的测试。(例如,我最初认为左分配性适用于
左吸收(用于
然而,尽管我相信[]
和可能遵守这条法律,IO
不遵守这条法律,我认为(在接下来的几段中,原因将变得显而易见)最好不要要求它。
事实上,爱德华·科米特(Edward Kmett)在一些幻灯片中似乎也支持类似的观点;为了深入了解这一点,我们需要做一些简短的离题,涉及一些数学术语。最后一张幻灯片《我想要更多的结构》说,“一个幺半群对一个应用程序来说就像一个右半群对另一个选择一样,”并且“如果你扔掉一个应用程序的参数,你得到一个幺半群,如果你扔掉另一个选择的参数,你得到一个右半群。”
对吧,耳环?“正确的耳环是怎么进去的?”我听见你哭了。好
释义右近半环(也叫右半环,但谷歌上似乎更多地使用前者)是四元(R,·,0),其中(R,0)是幺半群,(R,·)是半群,以下两个条件成立:
·是右分配的:对于所有r,s,t 2 R,(s t)r=sr tr。
- 0对于·是右吸收的:对于所有r 2 R,0r=0。
类似地定义了一个左近半环。
现在,这不太起作用,因为
无论如何,这些法则比单数法则更强,意味着完全有效的单数
实例将变成无效的替代
实例。标准库中有(至少)两个这样的例子:Monodea=
给定任意两个单元体,它们的乘积是单元体;因此,元组可以通过明显的方式(重新格式化基本包的源)成为Monid
的实例:
instance (Monoid a, Monoid b) => Monoid (a,b) where
mempty = (mempty, mempty)
(a1,b1) `mappend` (a2,b2) = (a1 `mappend` a2, b1 `mappend` b2)
类似地,我们可以通过累加monoid元素(重新格式化基本包的源代码),将第一个组件是monoid元素的元组变成Applicative
的实例:
instance Monoid a => Applicative ((,) a) where
pure x = (mempty, x)
(u, f) <*> (v, x) = (u `mappend` v, f x)
然而,元组不是Alternative
的实例,因为它们不能是幺半群a上的幺半群结构=
权利分配:
(ssf 1
mempty
(
(
接下来,考虑<代码>可能。目前来看,可能
的幺半群
和替代
实例不一致。(虽然我在本节开头提到的haskell cafe讨论建议对此进行更改,但半群软件包中有一个选项
newtype,将产生相同的效果。)作为一个幺半群
,可能
通过使用无
作为身份将半群提升为幺半群;因为基本包没有半群类,它只提升幺半群,所以我们得到(重新格式化基本包的源代码):
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
Nothing `mappend` m = m
m `mappend` Nothing = m
Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
另一方面,作为一个替代
,也许
代表了失败的优先选择,所以我们得到(再次重新格式化基本包的源):
instance Alternative Maybe where
empty = Nothing
Nothing <|> r = r
l <|> _ = l
事实证明,只有后者满足替代
定律。Monode
实例的失败不如(,)
的失败那么严重;它确实遵守有关的法律。
f <$> (Nothing <|> b)
= f <$> b by the definition of (<|>)
= Nothing <|> (f <$> b) by the definition of (<|>)
= (f <$> Nothing) <|> (f <$> b) by the definition of (<$>)
f <$> (Just a <|> b)
= f <$> Just a by the definition of (<|>)
= Just (f a) by the definition of (<$>)
= Just (f a) <|> (f <$> b) by the definition of (<|>)
= (f <$> Just a) <|> (f <$> b) by the definition of (<$>)
然而,对于幺半群
实例,它失败了;写(
(
现在,这个例子有一个警告。如果您只要求备选方案
s与
因此,我们可以得出结论,作为替代
比作为幺半群
有更高的要求,因此它需要一个不同的类。最纯粹的例子是一个内部类型不可知的幺半群
实例和一个彼此不兼容的应用
实例的类型;然而,基本包中没有这样的类型,我想不出任何类型。(可能根本不存在,尽管我会感到惊讶。)然而,这些内型诺斯替派的例子说明了为什么这两种类型的类必须是不同的。
MonadPlus
type类有什么意义?
MonadPlus
,与Alternative
一样,是对Monoid
的一种强化,但是针对Monad
而不是Applicative
。Edward Kmett在回答“类型类MonadPlus
,Alternative
,以及Monoid
?”问题时说MonadPlus
也比Alternative
强:法则为空
然而,因为任何类型的MonadPlus都应该有它的实例与它的替代实例相一致(我相信这是必需的,就像要求ap
和(
class (Monad m, Alternative m) => MonadPlus' m
该类不需要声明新函数;这只是关于empty
和(
您不能对替代
执行相同操作的原因是,即使您想保证替代
和单元体
始终一致,也是因为这种不匹配。所需的类声明将具有以下形式
class (Applicative f, forall a. Monoid (f a)) => Alternative''' f
但是(如上所述),就连GHC Haskell也不支持量化约束。
另外,请注意,Alternative
作为MonadPlus
的超类需要Applicative
作为Monad
的超类,祝你好运。如果你遇到了这个问题,总会有WrappedMonad
newtype,它会以一种显而易见的方式将任何Monad
转换成一个Applicative
;有一个实例MonadPlus m=
2.3.4 字符串类型与其他类型的转换 应用程序中有时需要将字符串类型的数据转换成其他数据类型,或者相反。下面介绍Python 中如何实现这些功能。 首先看函数 eval()。eval 函数接收一个字符串,并将该字符串解释成 Python 表达式 进行求值,最终得到特定类型的结果值;如果字符串无法解释成合法的 Python 表达式则报 错(如语法错误、未定义变量错误等)。例如: >>> eval(
假设有一个具有属性a的抽象类A和三个非抽象子类B、C和D。B没有附加属性,C包含属性c,D包含属性c和d。 我想为抽象类A的子类StdDeserializer能够根据要反序列化的属性的存在来决定选择哪个子类。 我以前用Codehaus的一些Jackson版本做到了这一点,它使用以下实现运行良好: 这很好,但从FasterXML ObjectMapper迁移到Jackson 2.4后,不允许Obje
问题内容: 目前,我在Java中遇到通用类问题。 我有这样的事情: 现在,我实例化了一个没有类型参数的类的对象,因为我对此没有兴趣或不知道。 问题是,尽管编译器不依赖于参数,但它不仅丢弃有关类型参数的信息,而且还丢弃有关集合(字符串)的类型的信息。 我是在做错什么,还是Java的限制?如果是这样,那为什么会有限制? 问题答案: 你做错了 如果您不知道T类型,则只需使用通配符: GenericCla
我遇到了一个奇怪的例外 '_InternalLinkedHashMap 我不知道怎么画地图 第一次运行代码时,它可以正常工作,因为它可以访问else部分。当它第二次应该访问第一个部件时,它会产生上述异常。 } 输出 FoodLog输出: {2021年8月3日:{早餐:{鱼片:{碳水化合物:0.0,脂肪:1.7,蛋白质:20.08,钙:95}},晚餐:{},零食:{},午餐:{} E/flatter
Interfaces and other types 接口与其它类型 接口 Interfaces in Go provide a way to specify the behavior of an object: if something can do this, then it can be used here. We’ve seen a couple of simple examples al
虽然 Web 应用最终都是处理字符串,但也需要其他的数据类型来生成字符串。本节介绍一些对开发 Rails 应用很重要的其他 Ruby 数据类型。 4.3.1 数组和值域 数组是一组具有特定顺序的元素。前面还没用过数组,不过理解数组对理解哈希有很大帮助(4.3.3 节),也有助于理解 Rails 中的数据模型(例如 2.3.3 节用到的 has_many 关联,11.1.3 节会做详细介绍)。 目前