gpt4 book ai didi

haskell - 如何废弃我的样板

转载 作者:行者123 更新时间:2023-12-02 10:02:52 24 4
gpt4 key购买 nike

我正在使用syntactic制作 AST 的库。要将 AST 计算为 (Haskell) 值,我的所有节点都需要是语法类 EvalEnv 的实例:

class EvalEnv sym env where
compileSym :: proxy env -> sym sig -> DenotationM (Reader env) sig

语法还提供了“默认”实现:

compileSymDefault :: (Eval sym, Signature sig) 
=> proxy env -> sym sig -> DenotationM (Reader env) sig

但是在 EvalEnv 的实例中无法访问 sig 的约束,使得以下(例如,重叠)实例不可能:

instance EvalEnv sym env where
compileSym = compileSymDefault

我的所有用户定义的 AST 节点都是 GADT,通常具有多个构造函数,其中 a 参数始终满足 compileSymDefault 的约束:

data ADDITIVE a where
Add :: (Num a) => ADDITIVE (a :-> a :-> Full a)
Sub :: (Num a) => ADDITIVE (a :-> a :-> Full a)

结果,我发现 所有 EvalEnv 实例如下所示:

instance EvalEnv ADDITIVE env where
compileSym p Add = compileSymDefault p Add
compileSym p Sub = compileSymDefault p Sub

此样板实例对于所有 AST 节点都是相同的,并且每个 GADT 构造函数都需要单独列出,因为 GADT 构造函数签名暗示了 compileSymDefault 约束。

有什么方法可以避免列出我创建的每个节点类型的每个构造函数?

最佳答案

如果我正确理解了这个问题,那么样板文件是由于需要对每个构造函数使用模式匹配来将所需的上下文引入范围内而产生的。除了构造函数名称之外,所有 case 分支都是相同的。

下面的代码使用了 removeBoilerplate 等级 2 函数,可用于将上下文纳入范围内。首先使用样板代码定义两个示例函数,然后转换为使用帮助器 removeBoilerplate 函数。

如果您有多个 GADT,则需要为每个 GADT 定制一个 removeBoilerplate。因此,如果您需要为每种类型多次删除样板文件,则此方法非常有用。

我不熟悉语法,无法 100% 确定这会起作用,但看起来它有很好的机会。您可能需要稍微调整 removeBoilerplate 函数的类型。

{-# LANGUAGE GADTs , ExplicitForAll , ScopedTypeVariables ,
FlexibleContexts , RankNTypes #-}

class Class a where

-- Random function requiring the class
requiresClass1 :: Class a => a -> String
requiresClass1 _ = "One!"

-- Another one
requiresClass2 :: Class a => a -> String
requiresClass2 _ = "Two!"

-- Our GADT, in which each constructor puts Class in scope
data GADT a where
Cons1 :: Class (GADT a) => GADT a
Cons2 :: Class (GADT a) => GADT a
Cons3 :: Class (GADT a) => GADT a

-- Boring boilerplate
boilerplateExample1 :: GADT a -> String
boilerplateExample1 x@Cons1 = requiresClass1 x
boilerplateExample1 x@Cons2 = requiresClass1 x
boilerplateExample1 x@Cons3 = requiresClass1 x

-- More boilerplate
boilerplateExample2 :: GADT a -> String
boilerplateExample2 x@Cons1 = requiresClass2 x
boilerplateExample2 x@Cons2 = requiresClass2 x
boilerplateExample2 x@Cons3 = requiresClass2 x

-- Scrapping Boilerplate: let's list the constructors only here, once for all
removeBoilerplate :: GADT a -> (forall b. Class b => b -> c) -> c
removeBoilerplate x@Cons1 f = f x
removeBoilerplate x@Cons2 f = f x
removeBoilerplate x@Cons3 f = f x

-- No more boilerplate!
niceBoilerplateExample1 :: GADT a -> String
niceBoilerplateExample1 x = removeBoilerplate x requiresClass1

niceBoilerplateExample2 :: GADT a -> String
niceBoilerplateExample2 x = removeBoilerplate x requiresClass2

关于haskell - 如何废弃我的样板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26109981/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com