Haskell 带HOAS的复印机-解释器
在DSL无标记最终解释器的非常酷的注释的第2.3节中,Oleg Kiselyov展示了如何解决一次解析序列化DSL表达式,然后多次解析它的问题 简单地说,他展示了类型的“假一级多态性”Haskell 带HOAS的复印机-解释器,haskell,ghc,dsl,Haskell,Ghc,Dsl,在DSL无标记最终解释器的非常酷的注释的第2.3节中,Oleg Kiselyov展示了如何解决一次解析序列化DSL表达式,然后多次解析它的问题 简单地说,他展示了类型的“假一级多态性” newtype Wrapped = Wrapped (∀ repr. ExpSYM repr ⇒ repr) fromTree :: String → Either ErrMsg Wrapped 不令人满意,因为它是不可扩展的:对于repr上的每一组约束,我们必须使用不同的包装器/fromTree。因此,我倾向
newtype Wrapped = Wrapped (∀ repr. ExpSYM repr ⇒ repr)
fromTree :: String → Either ErrMsg Wrapped
不令人满意,因为它是不可扩展的:对于repr
上的每一组约束,我们必须使用不同的包装器
/fromTree
。因此,我倾向于使用他的复印机-解释器解决方案。这个问题是关于如何在HOA中使用该解释器
具体来说,考虑以下语言的目标语言绑定:
class Lam repr where
lam :: (repr a -> repr b) -> repr (a -> b)
app :: repr (a -> b) -> repr a -> repr b
我在为我的复印机解释器提供Lam
类的声音实例时遇到问题。以下是我所拥有的:
data Dup repr1 repr2 a = Dup {unDupA :: repr1 a, unDupB :: repr2 a}
instance (Lam repr1, Lam repr2) => Lam (Dup repr1 repr2) where
lam f = Dup (lam $ unDupA . f . flip Dup undefined) (lam $ unDupB . f . Dup undefined)
app (Dup fa fb) (Dup a b) = Dup (app fa a) (app fb b)
对于像myDup
类型这样的不涉及未定义的的对象,是否有某种方法可以给出Lambda
的递归实例
我还尝试使用功能更强大的lam
from版本,该版本允许使用HOA的一元翻译器,尽管我不知道它对我的Dup
实例有什么帮助。将lam
的任何一个版本与HOA结合使用的解决方案都非常棒
*:Oleg演示了如何使用de Bruijn索引定义声音实例,但我对HOA的解决方案非常感兴趣
班级报告在哪里
lam::repr(a,g)b->repr g(a->b)
应用程序::repr g(a->b)->repr g a->repr g b
数据Dup repr1 repr2 g a=Dup{d1::repr1 g a,d2::repr2 g a}
实例(Lam repr1,Lam repr2)=>Lam(Dup repr1 repr2),其中
lam(Dup e1 e2)=Dup(lam e1)(lam e2)
app(Dup f1 f2)(Dup x1 x2)=Dup(app f1 x1)(app f2 x2)
这是不可能的
为了展示一个示例,我将首先创建一个非常简单的Lam
:
newtype Id a = Id a
instance Lam Id where
lam (Id f) = Id (\x -> let Id r = f x in r)
app (Id f) (Id x) = Id (f x)
现在我将创建一个在Dup
s上运行的函数:
f :: Dup Id Id Int -> Dup Id Id Int
f (Dup (Id x) (Id y)) = Dup (Id x*y) (Id y)
从Lam
实例,我将能够执行lamf::Dup Id Id(Int->Int)
。
这看起来像
Dup (Id (\x -> x*y)) (Id (\y -> y))
无法创建,因为y
无法从x
-lambda获取。(此处使用undefined
s将y
替换为undefined
,在运行时出错时抛出。)
这种情况并不罕见:只要在另一个结果中使用一个变量,就会发生这种情况
我不太清楚你对更强的单子的要求是什么,但是其他单子也会发生这种情况:例如,对于Maybe
,你不能将以下内容转换为Maybe(Int->Int)
,因为它取决于给定的值:
f :: Maybe Int -> Maybe Int
f m = m >>= \x -> if x > 5 then Just x else Nothing
(您可以在它上面使用fromJust
,希望没有人这样做,但它与未定义的解决方案相同。)
但是,undefined
s仅在函数需要查看其他变量时才会抛出错误。如果您绝对确定它永远不会在这样的东西上运行(例如,您将展开/创建限制在经过广泛测试的隐藏模块上),则未定义的方法将起作用
还有一个建议:使用更详细的错误
消息,而不是未定义
,以防出现问题。在使用模板Haskell做了一些工作之后,我有了一个适用于此的想法。另一种选择是按照TH的方式进行:
class Fail.MonadFail m => Quasi m where
-- All the things here, like inspecting types, generating names, etc.
...
-- Many instances, including
instance Quasi IO where ... -- So you can debug your TH
instance TH.Quasi GHCiQ where ... -- see https://github.com/ghc/ghc/blob/master/libraries/ghci/GHCi/TH.hs#L167
instance TH.Quasi TcM where ... -- see https://github.com/ghc/ghc/blob/master/compiler/typecheck/TcSplice.hs#L835
data Q a = { unQ :: forall m. Quasi m => m a }
instance Quasi Q where ...
您可以找到Q
monad的定义
最终用户在Q
monad中工作,它可以由任何内部编译器解释器解释,也可以由IO
monad进行调试等。任何复制都由forall
处理。同样,你也可以这样做
data L a = { unL :: forall repr. Lam repr => repr a }
instance Lam L where ...
myEndUserThing :: L ((a -> b) -> a -> b)
myEndUserThing = lam $ \f -> lam $ \x -> app f x
la
可以轻松地转换为您想要的任何其他repr
,如果您需要更多功能,只需将其添加到Lam
类或创建一个具有额外功能的派生类。这似乎不太可能。你不能从(r1,r2)->(r1,r2)
到(r1->r1,r2->r2)
,这将是使用(Lam r1,Lam r2)
实现Lam(Dup r1 r2)
所需要的,对于任意单子m
,我对从a(a->(a->mb b)->m(a->b)
,也有同样的想法,直到我读到第二篇链接文章。我在等待另一个类似的令人惊奇的解决方案。@crockeea是一个可以在论文背景之外描述的“技巧”,或者我可以跳转到某一页快速了解其工作原理吗?“不纯但卫生代码的组合器”的第3节有很好的描述,特别是第28页,一个解决方案是从HOA转换到PHOA,但我不知道这是否会让你的生活变得更轻松或更困难,因为你需要这些代码。但这一改变将修正悬赏评论中的三点。更改非常简单-将lam
的类型更改为(va->reprvb)->reprv(a->b)
;对于不绑定变量的类型,repr
变为repr v
;您需要var::va->repr va
。
data L a = { unL :: forall repr. Lam repr => repr a }
instance Lam L where ...
myEndUserThing :: L ((a -> b) -> a -> b)
myEndUserThing = lam $ \f -> lam $ \x -> app f x