【问题标题】:Duplicator Interpreter with HOAS带有 HOAS 的复印机口译员
【发布时间】:2017-04-27 03:30:31
【问题描述】:

these 的第 2.3 节中,关于 DSL 的无标记最终解释器的非常酷的注释中,Oleg Kiselyov 展示了如何解决解析序列化 DSL 表达式一次的问题,并解释 em> 多次。

简而言之,他用类型证明了“假的一流多态性”

newtype Wrapped = Wrapped (∀ repr. ExpSYM repr ⇒ repr)
fromTree :: String → Either ErrMsg Wrapped

不令人满意,因为它不可扩展:对于repr 上的每个 约束,我们必须有一个不同的Wrapper/fromTree。因此我倾向于使用他的复制器解释器解决方案。这个问题是关于如何在 HOAS 中使用该解释器。

具体来说,考虑以下语言用于目标语言绑定:

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)

有没有办法为我的Dup 类型而不涉及undefined 的东西提供Lambda递归 实例?

我还尝试使用来自this paper 的更强大的lam 版本,它允许monadic 解释器使用HOAS,但我不知道它对我的实例有什么帮助为Dup。使用任何一个版本的 lam 和 HOAS 的解决方案都会很棒!


*:Oleg 展示了如何使用 de Bruijn 索引定义声音实例,但我对 HOAS 的解决方案非常感兴趣。


    class Lam repr where
      lam :: repr (a,g) b -> repr g (a -> b)
      app :: repr g (a->b) -> repr g a -> repr g b

    data Dup repr1 repr2 g a = Dup{d1:: repr1 g a, d2:: repr2 g a}

    instance (Lam repr1, Lam repr2) => Lam (Dup repr1 repr2) where
      lam (Dup e1 e2) = Dup (lam e1) (lam e2)
      app (Dup f1 f2) (Dup x1 x2) = Dup (app f1 x1) (app f2 x2)

【问题讨论】:

  • 这似乎不太可能。你不能从(r1, r2) -> (r1, r2)(r1 -> r1, r2 -> r2),这需要使用(Lam r1, Lam r2) 来实现Lam (Dup r1 r2)
  • @Li-yaoXia 我也想过从(a -> m b) -> m (a -> b) 到任意Monad m,直到我阅读了第二篇链接的论文。我正在等待另一个类似的惊人解决方案。
  • @crockeea 是一个可以在论文上下文之外描述的“技巧”,或者我可以跳转到某个页面以快速了解它是如何工作的吗?跨度>
  • “不纯但卫生代码的组合器”的第 3 节有一个很好的描述,特别是第 28 页,他们切中要害。
  • 一种解决方案是从 HOAS 切换到 PHOAS,但我不知道对于您需要此代码的任何其他目的,这是否会使您的生活更轻松或更困难。但是这个改变将修复赏金评论中的三点。更改非常简单——将lam 的类型更改为(v a -> repr v b) -> repr v (a -> b);对于不绑定变量的类型,repr 变为 repr v;你需要var :: v a -> repr v a

标签: haskell ghc dsl


【解决方案1】:

这是不可能的。

为了展示一个例子,我将首先创建一个非常简单的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)

现在我将创建一个在Dups 上运行的函数:

f :: Dup Id Id Int -> Dup Id Id Int
f (Dup (Id x) (Id y)) = Dup (Id x*y) (Id y)

我可以通过Lam 实例执行lam f :: Dup Id Id (Int -> Int)。 这可能看起来像

Dup (Id (\x -> x*y)) (Id (\y -> y))

无法实现,因为 y 不能从 x-lambda 获得。 (在这里使用undefineds 将y 替换为undefined,只要它不能很好地工作就会抛出运行时错误。) 这种情况并不罕见:只要您在另一个结果中使用其中一个变量,就会发生这种情况。

我不太确定您对更强的Monad-generalized 的要求是什么,但其他Monads 也会发生这种情况:例如,使用Maybe,您无法转动跟随Maybe (Int -> Int),因为它取决于给定的值:

f :: Maybe Int -> Maybe Int
f m = m >>= \x -> if x > 5 then Just x else Nothing

(您可以在上面使用fromJust 并希望没有人这样做,但它与undefined 解决方案相同。)

不过,undefineds 只有在函数需要查看其他变量时才会抛出错误。如果您绝对确定它永远不会在这样的任何东西上运行(例如,您将展开/创建限制为经过广泛测试的隐藏模块),那么undefined 方式将起作用。

还有一个建议:使用更详细的error 消息而不是undefined,以防出现问题确实

【讨论】:

    【解决方案2】:

    在使用 Template 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 的定义here

    最终用户在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
    

    L a 可以轻松转换为您想要的任何其他 repr,如果您需要更多功能,只需将其添加到 Lam 类或创建具有额外功能的派生类。

    【讨论】:

      猜你喜欢
      • 2013-02-25
      • 2015-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-23
      • 1970-01-01
      • 1970-01-01
      • 2015-09-04
      相关资源
      最近更新 更多