在关于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)
答案 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
类或创建具有额外功能的派生类。