阅读 Real World Haskell 和Typeclassopedia我得到的印象是2元组(a,b)
在Haskell中可以扮演非常特殊的角色。
我遇到的第一个用途是使用lookup
,我们使用2元组列表作为字典。
然后我也发现((,) e)
是一个仿函数的实例(但没有其他n元组),这在(key,value)
的上例中是有意义的。
现在最近的案例 - 我实际想要问的一个案例 - 在Typeclassopedia的第4.3章中。它表示((,) a)
是Applicative
的实例,如果 a
是幺半群。你什么时候真正利用它?您使用Applicative
(a,b)
实例的应用程序是什么?
答案 0 :(得分:5)
没有什么能阻止我们编写三元组或任意n元组的实例:
instance Functor ((,,) a b) where
fmap f (x,y,z) = (x,y,f z)
instance (Monoid a, Monoid b) => Applicative ((,,) a b) where
pure z = (mempty, mempty, z)
(a,b,f) <*> (x,y,z) = (a `mappend` x, b `mappend` y, f z)
因此,对于任何n元组都可以实现实例,因此它们并不特殊。但由于这些实例必须写在某处,所以我们应该走多远。例如,Monoid
个实例最多定义为5元组。当然可以将它们写成10元组,但我们只是在那时复制样板代码。
话虽如此,对是特殊的,因为它们的集合提供了描述关系的自然方式。一个例子是字典,它涉及一个术语及其定义:
dictionary :: [(String, String)]
dictionary =
[("cat", "animal that likes strings; not Strings, though")
,("dog", "animal that likes you; yes you")
,("foo", "a strange word used by programmers in examples")
]
显示该关系的另一种方式(如果所有对的第一部分都是唯一的)将是
partialDictionaryEntry :: String -> String
如果我们将输入域限制为"cat"
,"dog"
和"foo"
,或
dictionaryEntry :: String -> Maybe String
这正是\s -> lookup s dictionary
的内容。使用对,你可以建模任何其他n元组:
(a,b,z) = ((a,b),z)
(a,b,c,z) = ((a,b,c),z) = (((a,b),c),z)
从这个意义上讲,它们是提供此功能的最小容器。我们可以从对中构建所有其他元组类型。哎呀,理论上我们甚至不需要编写我们的Applicative ((,,) a b)
实例,因为(,) (a,b)
实例已经为Monoid
提供了实例。
话虽如此,为什么它甚至有Applicative
个实例?它是最简单的Writer
实现:
log :: (Show a) => a -> (String, a)
log x = (show x ++ "\n", x)
fivePlusThree = (+) <$> log 3 <*> log 5
main = do
let (logs, result) = fivePlusThree
putStrLn logs
print result
3
5
8
这提供了一种向函数或值添加其他信息的简便方法,尽管您可能会使用Writer
及其writer
方法,因为它们使用起来更愉快并且提供了严格的变体。< / p>