在阅读Haskell中存在的this page后,我被迫测试了这种行为的限制,所以我编写了以下代码片段:
{-# LANGUAGE ExistentialQuantification #-}
data Showable = forall a. Show a => MkShowable a
pack :: Show a => a -> Showable
pack = MkShowable
instance Show Showable where
show (MkShowable x) = show x
Showable
类型与上述链接中创建的ShowBox
类型非常相似。然后我创建了这个人为的例子来说明我的问题。
showSomething :: Bool -> Showable
showSomething True = pack 1
showSomething False = pack "ABC"
main :: IO ()
main = do
x <- getLine
let y = read x
print $ showSomething y
此代码(工作正常)要求用户输入(应为“True”或“False”),然后打印出1
(如果为True)或"ABC"
(如果是)假
但我没能完全理解系统是如何做到这一点的。从数学上讲,它非常有意义。但我不知道计算机是如何解决它的。对我来说,系统似乎在运行时决定是否调用Int
的{{1}}实例或show
的{{1}}函数,但这意味着存在类似C ++的vtable,我不相信Haskell有一个概念。
我的问题是:它如何解决这个问题?系统无法事先知道我是要输入true还是false,因此它无法知道在编译时调用哪个String
,但它在两种情况下都清楚有效。
答案 0 :(得分:6)
如何实现类型类的一种方法是传递一个实现类型类的函数字典。例如带签名的函数
f :: Show a => T
将被翻译成
f' :: (a -> String) -> T
由编译器在show
内使用f
时,它被附加参数替换(实际上会有更多函数,所有这些都在{{1}中声明})。
类似于数据类型
Show
将被翻译成
forall a . Show a => MkShowable a
因此转换后的代码可能如下所示:
forall a . MkShowable' (a -> String) a
调用{-# LANGUAGE ExistentialQuantification #-}
data Showable' = forall a . MkShowable' (a -> String) a
pack' :: Show a => a -> Showable'
pack' = MkShowable' show
instance Show Showable' where
show (MkShowable' f x) = f x
showSomething :: Bool -> Showable'
showSomething True = pack' 1
showSomething False = pack' "ABC"
时,pack
实现也会传递给构造函数,以便在需要时可用。
答案 1 :(得分:2)
是的。 Haskell中的一堆语言特性(大多数是扩展)可以被视为基本上实现了许多概念,这些概念捆绑在&#34;类&#34;的复杂整体中。在OO编程中,但单独作为单独的功能。基本上的类型类约束的存在类型是 vtables!
为了使show (MkShowable x)
能够在没有任何其他限制的情况下工作,MkShowable x
必须包含足够的信息以了解如何show x
。这就是它的作用;即使它只有一个字段,它实际上也包含其他信息。
这与函数foo :: Show a => a; foo x = show x
似乎只有一个参数的方式基本相同,但实际上必须获得足够的附加信息以了解show x
;它不能“硬连线”#34; in因为foo
可能用于几种不同的类型。但是foo
要求其调用者从外部传递该信息(并且类型系统强制执行此操作),Showable
值包含完全相同的信息,以便能够提取它以传递给foo
等函数。
所以现在使用这个工具,与没有ExistentialQuantification
不同的是,知道要调用哪个show
实例的问题只是在两个相同类型的值之间进行选择(其中包含一个Show
实例一些未知的类型以及相同类型的值),这很容易做到;没有编译时知道它是哪一个。