Lan*_*dei 11 haskell typeclass functional-dependencies
玩弄类型类我想出了看似无辜的人
class Pair p a | p -> a where
one :: p -> a
two :: p -> a
Run Code Online (Sandbox Code Playgroud)
这似乎工作得很好,例如
instance Pair [a] a where
one [x,_] = x
two [_,y] = y
Run Code Online (Sandbox Code Playgroud)
但是我为元组遇到了麻烦.即使以下定义编译......
instance Pair (a,a) a where
one p = fst p
two p = snd p
Run Code Online (Sandbox Code Playgroud)
......我无法按预期使用它:
main = print $ two (3, 4)
No instance for (Pair (t, t1) a)
arising from a use of `two' at src\Main.hs:593:15-23
Possible fix: add an instance declaration for (Pair (t, t1) a)
In the second argument of `($)', namely `two (3, 4)'
In the expression: print $ two (3, 4)
In the definition of `main': main = print $ two (3, 4)
Run Code Online (Sandbox Code Playgroud)
有没有办法正确定义实例?或者我必须诉诸newtype包装?
C. *_*ann 18
实际上你的实例工作得很好.注意:
main = print $ two (3 :: Int, 4 :: Int)
Run Code Online (Sandbox Code Playgroud)
这按预期工作.那么为什么没有类型注释就行不通呢?好吧,考虑一下元组的类型:(3, 4) :: (Num t, Num t1) => (t, t1).因为数字文字是多态的,所以没有什么要求它们是相同的类型.实例的定义是(a, a),但该实例的存在不会告诉GHC统一类型(出于各种好的理由).除非GHC可以通过其他方式推断出这两种类型是相同的,否则它不会选择你想要的实例,即使这两种类型可以相等.
要解决您的问题,您可以添加类型注释,就像我上面所做的那样.如果参数来自其他地方,它通常是不必要的,因为它们已经知道它们是相同的类型,但如果你想使用数字文字,它会很快变得笨拙.
另一种解决方案是要注意,由于实例选择的工作原理,拥有一个实例(a, a)意味着(a, b)即使你想要也不能编写类似的实例.所以我们可以作弊,使用类型强制统一,如下所示:
instance (a ~ b) => Pair (a,b) a where
Run Code Online (Sandbox Code Playgroud)
我认为这需要TypeFamilies扩展~上下文.这样做允许实例首先匹配任何元组,因为实例选择忽略了上下文.然而,在选择实例之后,a ~ b上下文断言类型相等,如果它们不同则会产生错误但是 - 更重要的是 - 如果可能的话将统一类型变量.使用这个,你的定义按main原样工作,没有注释.
问题是文字数字具有多态类型.对于类型检查者来说,两个文字都应该具有相同的类型(Int)并不明显.如果您使用的元组不是多态的,那么您的代码应该可行.考虑这些例子:
*Main> two (3,4)
<interactive>:1:1:
No instance for (Pair (t0, t1) a0)
arising from a use of `two'
Possible fix: add an instance declaration for (Pair (t0, t1) a0)
In the expression: two (3, 4)
In an equation for `it': it = two (3, 4)
*Main> let f = id :: Int -> Int -- Force a monomorphic type
*Main> two (f 3,f 4)
4
*Main> two ('a','b')
'b'
*Main> two ("foo","bar")
"bar"
*Main> two (('a':),('b':)) "cde"
"bcde"
*Main>
Run Code Online (Sandbox Code Playgroud)