HOAS的复制机翻译

时间:2017-04-27 03:30:32

标签: haskell ghc dsl

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

简单地说,他表明"虚假的一流多态性"与类型

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

不能令人满意,因为它不可扩展:我们必须为Wrapper上的每个约束设置不同的fromTree / repr。因此,我倾向于使用他的复制器解释器的解决方案。这个问题是关于如何使用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)

是否有某种方法可以为我的Lambda类型提供Dup递归实例,但不涉及undefined

我还尝试使用来自this paper的更强大的lam版本,它允许使用HOAS的 monadic 解释器,但我没有看到它如何帮助我使用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)

2 个答案:

答案 0 :(得分:3)

这是不可能的。

为了展示一个例子,我将首先制作一个非常简单的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个实例能够lam f :: Dup Id Id (Int -> Int)。 这可能看起来像

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

无法生成,因为y - lambda中无法提供x。 (使用undefined s会将y替换为undefined,只要它运行不正常就会抛出运行时错误。) 这种情况并不罕见:只要您在另一个结果中使用其中一个变量,就会发生这种情况。

我不确定你对强Monad - 一般化的Monad提出了什么要求,但这也发生在其他Maybe s上:例如,Maybe (Int -> Int),您无法将以下内容转换为f :: Maybe Int -> Maybe Int f m = m >>= \x -> if x > 5 then Just x else Nothing ,因为它取决于给定的值:

fromJust

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

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

还有一个建议:使用更详细的undefined消息而不是public String displayTree(){ if(root != null){ return this.toString(new StringBuilder(), true, new StringBuilder(), root).toString(); }else{ return "Empty tree"; } } private StringBuilder toString(StringBuilder prefix, boolean isLeft, StringBuilder sb, BinaryNode<T> node) { if(node.getRight() !=null) { toString(new StringBuilder().append(prefix).append(esIzquierdo ? "│ " : " "), false, sb, node.getRight()); } sb.append(prefix).append(isLeft? "└── " : "┌── ").append(node.getItem().toString()).append("\n"); if(node.getLeft() != null) { toString(new StringBuilder().append(prefix).append(esIzquierdo ? " " : "│ "), true, sb, node.getLeft()); } return sb; } ,以防 出错

答案 1 :(得分:0)

在使用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都可以解释它以进行调试等。任何重复都由{{1}处理}。类似地,您可以执行类似

的操作
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类或创建具有额外功能的派生类。