(对于以下,简化Show
和Read
class Show a where show :: a -> String
class Read a where read :: String -> a
并假设read
永远不会失败。)
众所周知,可以制作一种存在类型的形式
data ShowVal where
ShowVal :: forall a. Show a => a -> ShowVal
然后构造一个“异构列表” :: [ShowVal]
,比如
l = [ShowVal 4, ShowVal 'Q', ShowVal True]
这也是众所周知的,这是相对无用的,因为相反,可以只构造一个 list :: [String]
,例如
l = [show 4, show 'Q', show True]
这完全是同构的(毕竟,唯一可以用 a 做的
ShowVal
就是show
它)。
懒惰使这特别好,因为对于列表中的每个值,结果show
都会自动记忆,因此 noString
被计算多次(并且String
根本不计算未使用的 s )。
AShowVal
等价于存在元组exists a. (a -> String, a)
,其中函数是Show
字典。
可以为 进行类似的构造Read
:
data ReadVal where
ReadVal :: (forall a. Read a => a) -> ReadVal
请注意,因为read
它的返回值是多态的,所以ReadVal
是通用的而不是存在的(这意味着我们根本不需要它,因为 Haskell 具有一流的通用性;但我们将在这里使用它来强调与Show
)。
我们也可以列一个清单:: [ReadVal]
:
l = [ReadVal (read "4"), ReadVal (read "'Q'"), ReadVal (read "True")]
与 一样Show
,列表与列表:: [ReadVal]
同构:: [String]
,例如
l = ["4", "'Q'", "True"]
(我们总是可以String
用
newtype Foo = Foo String
instance Read Foo where read = Foo
因为Read
类型类是开放的。)
AReadVal
等价于通用函数forall a. (String -> a) -> a
(CPS 风格的表示)。这里Read
字典是由用户ReadVal
而不是生产者提供的,因为返回值是多态的而不是参数。
String
然而,在这些表示中,我们都没有得到我们在表示中得到的自动记忆Show
。假设
read
我们的类型是一个昂贵的操作,所以我们不想对同String
一个类型多次计算它。
如果我们有一个封闭的类型,我们可以这样做:
data ReadVal = ReadVal { asInt :: Int, asChar :: Char, asBool :: Bool }
然后使用一个值
ReadVal { asInt = read s, asChar = read s, asBool = read s }
或类似的规定。
但在这种情况下——即使我们只使用ReadVal
作为一种类型——
String
每次使用值时都会解析 。有没有一种简单的方法可以在保持ReadVal
多态性的同时获得记忆?
(如果可能的话,让 GHC 自动完成它,类似于这种Show
情况,将是理想的。更明确的记忆方法 - 也许通过添加Typeable
约束? - 也可以。)