有没有办法强制GHC在特定值的生命周期内进行特定计算?
我显然可以将值放入记录中,为所述计算结果创建惰性记录条目,并创建一个构建记录的制造商函数,并将值置于所述条目中。
我讨厌每次我想要它时都需要从记录中取出原始值。并且Haskell没有任何特殊的多态性 - 像C ++或Java这样的关系。
是否有任何技巧可以在具有相同参数的函数的多个不相关调用中记忆值?
我可以模糊地想象各种形式的依赖类型的技巧,它们有效地告诉编译器多种用法即将到来。 Haskell中没有任何依赖类型,但可能是隐式参数?我想不是,但我想我会问。也许是一个pragma?
想象一下,我有一个Necklace
数据结构的向量,我需要一个有理数的结果向量,存储为公分母和分子向量。
{-# LANGUAGE ImplicitParams #-}
import qualified Data.Vector as V
data Necklace = Necklace { ... }
necklace_length n = ...
denominator :: (necklaces :: V.Vector Necklace) => Int
denominator = V.foldl' lcm 30 $ V.map necklace_length ?necklaces
numerators :: (necklaces :: V.Vector Necklace) => V.Vector Int
numerators = V.map f ?necklaces
where f x = ... denominator ...
kittytoy :: (necklaces :: V.Vector Necklace) => Meow -> ...
kittytoy = \meow -> ... numerators ...
先验,我希望,如果我调用kittytoy
数百万次,每次都有不同的参数meow
,那么GHC会生成调用numerators
一百万次的代码,每个都有相同的隐式参数necklaces
。
然而很明显numerators
只需要调用一次,第一次?necklaces
被分配,这意味着GHC可能会注意到这种优化。
甚至应该有一个明确的代码重构方法,使用模板haskell通过生成类似?numerators = numerators
的代码并将numerators :: V.Vector Int
添加到调用它的函数的类型约束来显式传递thunk。
答案 0 :(得分:7)
您可能正在寻找由data-memocombinators实施的纯备忘录。基本上,它的工作原理是创建一个(懒惰的,可能是无限的)树结构,其中包含每个叶子上函数的所有可能值,然后创建一个简单访问相关位置值的新函数。例如,您可以为函数Bool -> a
编写备忘录,如下所示:
memoBool :: (Bool -> a) -> (Bool -> a)
memoBool f =
let fTrue = f True
fFalse = f False
in \b -> if b then fTrue else fFalse
在这种情况下,“树状结构”是盆景,只有两片叶子。
data-memocombinators将Memo a
类型定义为forall r. (a -> r) -> (a -> r)
,并使用有用的组合器(如pair :: Memo a -> Memo b -> Memo (a, b)
)将其打包(读取:如果可以记忆参数类型{{1}的函数}和memoise参数类型a
的函数,你可以记住参数类型b
)的函数。
这是纯粹的,非常优雅,依赖于基本上所有Haskell实现实现的共享(这与使您的记录想法有效的事情相同)。不幸的是,它也不是很快,所以在实际应用中你可能想要使用uglymemo代替它,它在幕后使用了一个可变的(a, b)
(但是暴露了一个外部纯接口)。
答案 1 :(得分:0)
如type
Philip JF中所述,type Necklaces = (necklaces :: V.Vector Necklace,
denominator :: Int,
numerators :: V.Vector Int)
kittytoy :: (Necklaces) => Meow -> ...
现在为类型约束定义了同义词这一事实引发了另一种似是而非的方法。
您可以为类型约束创建同义词,该约束为所有各种派生值创建隐式参数:
?numerators
您最初使用某种形式的模板Haskell构造来分配所有值,如{{1}}。我会玩它看它是否有效。