【发布时间】:2015-12-11 02:48:20
【问题描述】:
我正在寻找一个函数
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ConstraintKinds #-}
memoC :: (c => a) -> (c => a)
这样得到的as 只针对提供的约束计算一次。
另一个简短的版本
我怎样才能使某种类型的值 a 只能在存在某些约束证明 c 的情况下检查?
动机
长期以来,我一直在寻找记忆表单值的通用解决方案:
C a => a
C 是一些约束,a 涵盖所有类型。使用Typeable 约束a 和一些智能构造函数,可以通过在TypeReps 上构建trie 来安全地记忆Typeable a => b 的trie 的脊椎。这个问题是关于更难的部分,在这种树的叶子上放什么。
如果我们能够以某种方式将a 放入叶子中,那么对于某些具体类型a,trie 的叶子最初需要有一个值C a => a,因为无法从类型。从树中查找值需要C a 的字典。这似乎相当于根据传入的字典修改树的叶子中保存的值。
如果我们不能以某种方式将a 放入叶子中,那么对于单个b,叶子将会有一个更可怕的类型C a => b,并且,在提供字典时,我们需要证明该类型a(以及字典)可以由 b 中的内容来确定,它不会比 TypeRep 更强大。
邪恶
很想进入bag of evil 来构建一个构造函数来保存在树的叶子上。如果每个约束可能只有一个字典,那么根据传入的字典修改树的叶子中保存的值并不是坏事。
对此的任何“解决方案”都可能非常邪恶。我假设任何约束都只有一个字典。反射gives us another evil 可以为一个约束构造多个字典。
劝我远离这个邪恶。
示例
以下内容不应(也不会)记住提供约束TracedC String 的结果。
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleInstances #-}
import Debug.Trace (trace)
class TracedC a where
tracedC :: () -> a -- The () argument keeps a from being memoized in the dictionary for `TracedC a`
instance TracedC [Char] where
tracedC _ = trace "tracedC :: String" "Yes"
newtype Memoized c a = Memoized { getMemoized :: c => a }
example :: Memoized (TracedC a) a
example = Memoized (tracedC ())
main = do
let memo = example :: Memoized (TracedC [Char]) String
putStrLn $ getMemoized memo
putStrLn $ getMemoized memo
输出是
tracedC :: String
Yes
tracedC :: String
Yes
一个解决方案会承认一个类似的例子,但只在输出一次时评估tracedC () :: TracedC [Char] -> String
tracedC :: String
Yes
Yes
相关尝试
A map from types to values f a 可以在 monadic memoization 中使用,具有显式副作用。
【问题讨论】:
-
好吧,你可以考虑在你的
TypeReptrie 的叶子上存储一个Dynamic-alike。但是你真的可以做一个TypeRep试试吗? (你能列举出TypeReps 吗,这似乎比创建 trie 更容易?) -
我可以做一个
TypeReptrie;这比枚举它们更容易。将Dynamic存储在它的叶子上是您记忆forall a. Typeable a => f a的方式 -
我非常好奇。你能分享一些代码吗?
-
愚蠢而简单的方法是使用
TypeRep的Fingerprint,这是Internal实现一直用于Eq的东西。在基础 4.8.0.0 中,您可以通过typeRepFingerprint访问它。它和TypeRep的==一样好,对我来说已经足够了。 -
但这还不够——这只是第一步。您必须以某种方式从
Fingerprint转到Typeable实例以使Dynamic值成为适当的实际具体类型。
标签: haskell constraints memoization