(对于以下内容,请将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]
这是完全同构的(毕竟,唯一一个可以用a做的事情
ShowVal
是show
。)
懒惰使这特别好,因为对于列表中的每个值,
show
的结果会自动记忆,因此计算的String
不会超过String
一次(并且未使用的ShowVal
根本不计算。)
exists a. (a -> String, a)
相当于存在元组Show
,
函数是Read
字典。
可以为data ReadVal where
ReadVal :: (forall a. Read a => a) -> ReadVal
:
read
请注意,因为ReadVal
的返回值是多态的,Show
是。{
普遍而不是存在(这意味着我们并不真的需要它
所有,因为Haskell拥有一流的普遍性;但我们会在这里使用它
突出显示与:: [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
相当于通用函数Read
(CPS式表示)。这里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
。有一个简单的方法
在保持Show
多态的同时得到记忆?
(让GHC自动执行此操作,与Typeable
情况类似,即可
理想的,如果它在某种程度上是可能的。更明确的记忆方法 -
也许是通过添加{{1}}约束? - 也没关系。)
答案 0 :(得分:5)
Laziness使这一点特别好,因为对于列表中的每个值,show的结果会自动记忆,因此不会多次计算任何String(并且根本不会计算未使用的字符串)。 / p>
这个前提是不正确的。引擎盖下没有神奇的备忘录表。
懒惰意味着不需要的东西,不计算。这并不意味着所有计算值都是共享的。您仍然需要引入显式共享(通过您自己的表)。
答案 1 :(得分:2)
这是更明确的方法的实现;它需要Typeable
,因为否则没有任何东西可以锁定备忘录表。我基于uglymemo的备忘录代码;可能有一种方法可以使用纯记忆,但我不确定。这很棘手,因为你必须构造任何forall a. (Read a, Typeable a) => ...
创建的隐式函数的 表,否则你最终会在每次调用时构造一个表,这是无用的。
{-# LANGUAGE GADTs, RankNTypes #-}
import Data.Dynamic
import Control.Concurrent.MVar
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HM
import System.IO.Unsafe
data ReadVal where
ReadVal :: { useReadVal :: forall a. (Read a, Typeable a) => a } -> ReadVal
mkReadVal :: String -> ReadVal
mkReadVal s = unsafePerformIO $ do
v <- newMVar HM.empty
return $ ReadVal (readVal v)
where
readVal :: (Read a, Typeable a) => MVar (HashMap TypeRep Dynamic) -> a
readVal v = unsafePerformIO $ do
m <- readMVar v
let r = read s -- not evaluated
let typeRep = typeOf r
case HM.lookup typeRep m of
Nothing -> do
modifyMVar_ v (return . HM.insert typeRep (toDyn r))
return r
Just r' -> return $ fromDyn r' (error "impossible")