我有一个EDSL,为阵列提供类似列表的组合器(map
,zipWith
等。)
某些组合器要求某些输入是随机访问。例如。 Gather
的数据数组从另一个指定的索引处选择数据数组中的元素:
Gather (Manifest [10,11,12]) (Manifest [2,0,0,1,2]) = [12,10,10,11,12]
该语言使用data-reify
包来恢复共享。问题在于,有时相同的子树包含需要提供随机访问的节点和可以按顺序计算的节点。让他们共享会破坏后续的评估者。
例如,在下面的树中,我希望[1,2,3]
继续共享,但Manifests
将它们包装为恢复图中的不同节点:
[1, 2, 3]
/ \
Manifest Manifest
| |
| Map (+1)
\ /
Gather
更复杂的示例可能包含许多共享节点,所有共享节点都应该变得不同(即使可以共享Map (+1) (Manifest [1,2,3])
。
[1, 2, 3]
/ \
Manifest Manifest
| |
Map (+1) Map (+1)
| /|
Map (*2) / |
\ / |
Gather |
\ |
ZipWith (+)
即使我找到了简单案例(Gather
引用Manifest
直接)的解决方案,它也已经涵盖了大多数用例。
欢迎任何指示!
以下是该语言的简单模型。
module NoSharing where
data AST = Manifest [Int]
| Map (Int -> Int) AST
| ZipWith (Int -> Int -> Int) AST AST
| Gather AST -- ^ Data
AST -- ^ Indices
complex = ZipWith (+) gathered indexes
where
gathered = Gather (Map (*2) indexes) indexes
indexes = Map (+1) $ Manifest [1,2,3]
simple = Gather dat indexes
where
dat = Manifest [1,2,3]
indexes = Map (+1) dat
答案 0 :(得分:3)
您可以这样做的一种方法是在致电data-reify
之前手动取消共享。例如,此函数应该有望取消共享顶级Manifest
构造函数,但保持其参数共享:
rebuildManifest :: AST -> AST
rebuildManifest (Manifest xs) = Manifest xs
rebuildManifest t = t
现在要取消共享Manifest
下的任何Gather
,您可以递归地执行相同的操作,并在适当时重新使用原始文件
rebuildAllManifestsUnderGather :: AST -> (AST, Bool)
rebuildAllManifestsUnderGather t@(Map f t') =
let (newt', reuse) = rebuildAllManifestsUnderGather t'
in if reuse then (t, True) else (Map f newt', False)
rebuildAllManifestsUnderGather t@(ZipWith f t1 t2) =
let (newt1, reuse1) = rebuildAllManifestsUnderGather t1
(newt2, reuse2) = rebuildAllManifestsUnderGather t2
in if reuse1 && reuse2 then (t, True) else (ZipWith f newt1 newt2, False)
rebuildAllManifestsUnderGather t@(Gather t1 t2) =
let (newt1, reuse1) = rebuildManifest $ rebuildAllManifestsUnderGather t1
(newt2, reuse2) = rebuildManifest $ rebuildAllManifestsUnderGather t2
in if reuse1 && reuse2 then (t, True) else (Gather newt1 newt2, False)
where rebuildManifest (Manifest xs, _) = (Manifest xs, False)
rebuildManifest (t, reuse) = (t, reuse)
rebuildAllManifestsUnderGather t@(Manifest xs) = (t, True)
但是,会被警告:无法保证可观察的共享,并且可能在两个方向上都不可靠。 GHC优化者可以在法律上“重新分享”上述取消共享Manifest
的尝试。我不知道它在实践中会做些什么。
此解决方案也非常复杂,因此,考虑到其脆弱性,在调用data-reify
后进行明确的取消共享传递可能更好。