我打算在明天向Haskell提供的小型介绍中展示一个类型类的强大功能的例子。
我以为我会仔细检查我要呈现的内容,因为我并非百分之百的自信。
所以,我们说我有两个功能:
firstSort :: Int -> Int -> Ordering
firstSort a b = compare (even a) (even b)
secondSort :: Int -> Int -> Ordering
secondSort = compare
现在,由于他们的Monoid实例,我可以将这两种结合起来:
sort :: Int -> Int -> Ordering
sort = firstSort <> secondSort
现在我要确认的是两种情况mappend
期间使用的过程。
据我所知,它的工作原理如下:
首先是实例:
instance Monoid b => Monoid (a -> b) where
mempty _ = mempty
mappend f g x = f x `mappend` g x
被使用。这是通过使用a
实例化此实例的(Int -> Int)
来完成的。此外,mappend
的声明让我困惑了一段时间,因为它需要3个参数而不是2个但后来我记得(a -> b) -> (a -> b) -> (a -> b)
实际上与(a -> b) -> (a -> b) -> a -> b
相同。
感谢这个实例,我们获得了firstSort <> secondSort
:
\a b -> (firstSort a b) <> (secondSort a b)
然后是实例:
instance Monoid Ordering where
mempty = EQ
LT `mappend` _ = LT
EQ `mappend` y = y
GT `mappend` _ = GT
与mappend
和firstSort
secondSort
因此,总结一下,它会将Monoid b => Monoid (a -> b)
个实例与b = Ordering
和a = (Int -> Int)
以及之后的Monoid Ordering
个实例相匹配。
这是对的吗?
答案 0 :(得分:7)
是。你已经正确地结束了。
由于Int -> Int -> Ordering
是curry,它可以看作是一个参数的函数,它返回另一个函数。即:
Int -> (Int -> Ordering)
因此,编译器分三步解析Monoid
个实例:
Value Type | Monoid Instance
--------------------------|---------------------------
Int -> (Int -> Ordering) | instance Monoid b => Monoid (a -> b)
Int -> Ordering | instance Monoid b => Monoid (a -> b)
Ordering | instance Monoid Ordering
答案 1 :(得分:7)
我们来看看。我不认为你想要的a
是(Int -> Int)
因为当没有parantheses时函数类型与右边相关联。在您的情况下,类型是
firstSort :: Int -> (Int -> Bool)
secondSort :: Int -> (Int -> Bool)
所以,这里有一些有趣的事情。当你连接这两个函数时,它实际上是两次!首先,a
只是第一个Int
而b
是Int -> Bool
,所以我们必须使用Int -> Bool
的Monoid结构。但要做到这一点,我们必须再做同样的事情,因此a
为Int
而b
为Bool
。
firstSort <> secondSort = \a -> (firstSort a) <> (secondSort a)
= \a -> (\b -> (firstSort a b) <> (secondSort a b))
但最终结果仍然与你得出的结果相同:
= \a b -> (firstSort a b) <> (secondSort a b)