(对于以下内容,将 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]
众所周知,这是相对无用的,因为相反,可以
只需构建一个列表
:: [String]
, 如l = [show 4, show 'Q', show True]
这完全是同构的(毕竟,唯一可以用
ShowVal
是 show
它)。懒惰使这特别好,因为对于列表中的每个值,
show
的结果自动内存,所以没有 String
计算超过一次(并且根本不计算未使用的
String
s)。一个
ShowVal
等价于存在元组 exists a. (a -> String, a)
,其中函数是
Show
字典。可以为
Read
制作类似的构造。 :data ReadVal where
ReadVal :: (forall a. Read a => a) -> ReadVal
请注意,因为
read
它的返回值是多态的,ReadVal
是普遍的而不是存在的(这意味着我们在
全部,因为 Haskell 具有一流的通用性;但我们将在这里使用它
突出显示与
Show
的相似之处)。我们也可以列个 list
:: [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
类型类是开放的。)一个
ReadVal
相当于一个通用函数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
约束? ——也可以。)
最佳答案
Laziness makes this particularly nice, because for each value in the list, the result of show is memoized automatically, so no String is computed more than once (and Strings that aren't used aren't computed at all).
这个前提是不正确的。引擎盖下没有神奇的备忘录表。
懒惰意味着不需要的东西,不计算。这并不意味着所有计算值都是共享的。您仍然必须引入显式共享(通过您自己的表)。
关于haskell - 惰性和多态值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10168939/