我试图理解为什么Haskell的show
会对待
与例如列表不同的字符列表整数
即使没有FlexibleInstances
Pragma。
阅读了Show
的文档,我意识到了
我真的不明白Haskell如何选择方法
对于类型类的实例。
请考虑以下代码:
class MyShow a where
myShow :: a -> String
myShowList :: [a] -> String
myShowTuple :: (a, b) -> String
myShowList xs = "Default List Implementation"
myShowTuple t = "Default Tuple Implementation"
instance MyShow Char where
myShow c = "One Char"
myShowList xs = "List of Chars"
myShowTuple t = "Char Tuple"
instance MyShow Int where
myShow n = "One Int"
myShowList xs = "List of Integers"
myShowTuple t = "Int Tuple"
instance MyShow Float where
myShow n = show n
instance (MyShow a) => MyShow [a] where
myShow = myShowList
instance (MyShow a) => MyShow (a, b) where
myShowTuple t = "foo"
myShow = myShowTuple
现在,如果我打电话,例如
myShow (5::Int,5::Int)
我希望Haskell能够思考
'哦,myShow
有一个元组作为参数。我们来看看哪个
执行我必须打电话。'
并选择最后一个会产生的结果
在"foo"
。
显然,事实并非如此。
Haskell似乎在研究元组的内容(即
a
)的类型并决定调用相应的方法,
结果为"Int Tuple"
。
为什么会这样?
答案 0 :(得分:9)
当你写myShow (5::Int, 5::Int)
时,Haskell 会说“哦,myShow有一个元组作为参数。让我们看看我必须调用哪个实现。”并且它 选择最后一个,即myShow = myShowTuple
。但这并不意味着结果将是“foo”。这意味着调用myShow (5::Int, 5::Int)
的结果与调用myShowTuple (5 :: Int, 5 :: Int)
的结果相同。
所以现在Haskell必须决定它必须调用哪个版本的myShowTuple
。由于myShowTuple
的类型为MyShow a => (a, b) -> String
,因此在倒数第二行上定义的myShowTuple
版本的类型为MyShow a => ((a, c), b) -> String
,因此不适合。第17行定义的那个类型为(Int, b) -> String
,因此一个适合。所以这就是被选中的那个。
答案 1 :(得分:4)
Haskell的思维过程是这样的:
MyShow
或(5 :: Int, 5 :: Int)
(Int, Int)
实例
MyShow a => MyShow (a, b)
a
=> a
和b
=> a
),它会选择此实例。a
,在这种情况下Int
,也是MyShow
的实例。注意:在选择实例后,此检查会发生。myShow (5 :: Int, 5 :: Int)
调用元组的myShow
并成为myShowTuple (5 :: Int, 5 :: Int)
myShowTuple
,因为它的类型为(a, b)
,而在元组的情况下,a
为(a, b)
所以myShowTuple
的类型为((a, b) ,c)
{1}}显然不匹配。MyShow a => (a, b) -> String)
,因为在这种情况下a
类型为Int
haskell将此解析为Int
MyShow
的实例myShowTuple
"Int Tuple"
只是一个旁注,如果这是Show
的实际实现,那么myShowTuple
myShowTuple :: MyShow b => (a, b) -> String
否则你无法实际格式化b
,毕竟是任何类型。
这会使
instance (MyShow a) => MyShow (a, b) where
...
到
instance (MyShow a, MyShow b) => MyShow (a, b) where
...