微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 Bool 或 Iff 中实现 Exp in the Paper Extensibility for the Masses

如何解决如何在 Bool 或 Iff 中实现 Exp in the Paper Extensibility for the Masses

我目前正在阅读论文面向大众的可扩展性。对象代数的实用可扩展性 by Bruno C. d. S. Oliveira 和 William R. Cook(可在互联网上的许多地方找到 - 例如此处:https://www.cs.utexas.edu/~wcook/Drafts/2012/ecoop2012.pdf)。

在第 10 页,他们写道:

添加新的数据变体很容易。第一步是创建新类 BoolIff 以通常的面向对象风格(如 LitAdd):

class Bool implements Exp {...}
class Iff implements Exp {...}

Exp 的实现似乎留给读者作为练习。然而,我不清楚 Exp 在本文的这一部分中是如何定义的。我的问题是:

应该如何实现BoolIff

这是我尝试过的:

Exp 的第一个定义

在论文的早期,Exp 接口是这样定义的:

interface Exp {
    Value eval();
}

这里,Value 是由另一个接口定义的:

interface Value {
    Integer getInt();
    Boolean getBool();
}

然而,这篇论文很快就背离了 Exp 的这个定义,转而支持基于访问者的定义。

Bool 的可能实现

根据该定义,应该如何实现 Bool 类?

这样的事情似乎是一个开始:

class Bool implements Exp {
    boolean x;
    public Bool(boolean x) { this.x = x; }
    public Value eval() {
        return new VBool(x);
}}

然而,问题变成了如何正确实现Value

论文只显示了这一点:

class VBool implements Value {...}

这个实现对我来说似乎并不完整:

class VBool implements Value {
    boolean x;
    public VBool(boolean x) { this.x = x; }
    public Boolean getBool() {
        return new Boolean(x);
    }
    public Integer getInt() {
        // What to return here?
    }
}

正如我上面的尝试所示,不清楚从 getInt 返回什么。我想我可以返回 null 或抛出异常,但这意味着我的实现是 partial

无论如何,Exp 的第一个定义似乎只是作为论文中的一个激励示例存在,然后继续定义更好的替代方案。

Exp 的第二个定义

在第 4 页,论文将 Exp 重新定义为内部访问者:

interface Exp {    
    <A> A accept(IntAlg<A> vis);
}

其中 IntAlg<A> 是另一个接口:

interface IntAlg<A> {
    A lit(int x);
    A add(A e1,A e2);
}

到目前为止,事情似乎很清楚,直到我们开始实施 BoolIff...

Bool 的可能实现

我们应该如何根据 Bool 的这个定义来实现建议的 Exp 类?

class Bool implements Exp {
    boolean x;
    public Bool(boolean x) { this.x = x; }
    public <A> A accept(IntAlg<A> vis) {
        // What to return here?
}}

没有办法凭空想象 A 值,因此必须与 vis 交互才能产生 A 值。但是,vis 参数仅定义了 litadd 方法

lit 方法需要 int,这在 Bool 中不可用。

同样,add 需要两个 A 值,这些值也不可用。我又一次陷入了僵局。

Exp 的第三个定义?

然后,在第 8 页,论文展示了这个例子:

int x = exp(base).eval();

这里,exp(base) 返回 Exp,但这是 eval 的哪个定义?

显然,Exp 仍然(或再次?)有一个 eval 方法,但现在它返回 int。看起来像这样吗?

interface Exp {
    int eval();
}

论文没有给出这个定义,所以我可能误解了一些东西。

Bool 的可能实现

我们可以使用 Bool 的这个定义来实现 IffExp 吗?

class Bool implements Exp {
    boolean x;
    public Bool(boolean x) { this.x = x; }
    public int eval() {
        // What to return here?
}}

同样,目前还不清楚如何实现接口。当然,您可以将 0 返回为 false,将 1 返回为 true,但这只是一个随意的决定。这似乎不太合适。

我遗漏了 Exp 的第四个定义吗?或者论文中是否有其他一些信息让我无法理解?

顺便说一句,如果我在尝试中犯了错误,我深表歉意。我通常不编写 Java 代码

解决方法

@马克。让我试着澄清你的困惑点。

Exp的定义

我们在第 10 页中假设的 Exp 的定义是:

interface Exp {
    Value eval();
}

在论文前面的图 1 中介绍过。可以忽略带有访问者的 Exp 的替代版本。我们仅用它来讨论访问者以及与对象代数的关系,在本文后面的部分中没有发挥作用。

另外,关于第8页的代码,我认为论文中有一个错字。您是对的:似乎我们假设了一个返回 int 的 eval 方法。当我们写这篇论文时,我们使用了多个版本的 Exp,可能我们错误地使用了另一个版本的代码。第 8 页中的代码,根据论文中的表述进行了修改,应该是:

int x = exp(base).eval().getInt();

价值选择

我们打算用于实现 Value 类的代码使用异常,与您自己的尝试类似,并且确实如论文中所示那样是部分的。在论文中,我们的重点是关于源表达式的可扩展性和类型安全性。对于值,我们想要的只是值足够丰富以支持源表达式,并且假设值本身不可扩展(稍后在第 7 节中我们简要讨论了可扩展值)。我们在论文中选择的表示旨在保持代码简单,并避免在处理错误管理(与可扩展性正交)时分心。

很高兴这不是一个很好的表示,但至少对于当时的 Java 版本,这似乎是一个合理的妥协。在现代 Java 中,我认为要走的路是将 Value 建模为代数数据类型。 Java 16 支持 pattern matching and sealed types,它可用于对函数式编程中的代数数据类型进行本质建模。因此,您可以使用以下内容对 Value 进行建模:

sealed interface Value {}
record VInt(int x) implements Value {}
record VBool(boolean b) implements Value {} 

回想起来,为了避免像您遇到的那种混淆,我认为最好仅使用界面来展示论文:

interface Exp {
    int eval();
}

并使用 if 构造 à la C,其中整数扮演布尔值的角色。这首先可以避免因价值的表示而分心。

论文中价值的表示

无论如何,回到论文的表述,以及你关于偏袒的观点,我接下来展示了一个最小但完整的代码实现。为了说明偏向性不是一个基本问题,我还从使用未检查异常切换到检查异常。

首先我们可以定义如下值:

interface Value {
    // You can choose checked exceptions or unchecked exceptions.
    // In the paper we opted for unchecked exceptions.
    // But here,I show that you can also use checked exceptions,// if you want to ensure that you deal with exception cases.
    int intValue() throws Exception;
    boolean boolValue() throws Exception;
}

class VInt implements Value {
    int x;

    public VInt(int x) {
        this.x = x;
    }

    public int intValue() throws Exception {
        return x;
    }

    public boolean boolValue() throws Exception {
        throw new Exception();
    }

    public String toString() {
        return Integer.valueOf(x).toString();
    }
}

class VBool implements Value {
    boolean b;

    public VBool(boolean b) {
        this.b = b;
    }

    public int intValue() throws Exception {
        throw new Exception();
    }

    public boolean boolValue() throws Exception {
        return b;
    }

    public String toString() {
        return Boolean.valueOf(b).toString();
    }
}

然后你可以用 for Object Algebras 编写代码如下:

    // Integer Expressions
interface IntAlg<Exp> {
    Exp lit(int x);
    Exp add(Exp e1,Exp e2);
}

interface Exp {
    Value eval() throws Exception;
}

class IntEval implements IntAlg<Exp> {
    public Exp lit(int x) {
        return () -> new VInt(x);
    }

    public Exp add(Exp e1,Exp e2) {
        return () -> new VInt(e1.eval().intValue() + e2.eval().intValue());
    }
}

// Boolean Expressions
interface BoolAlg<Exp> extends IntAlg<Exp> {
    Exp bool(boolean b);
    Exp iff(Exp e1,Exp e2,Exp e3);
}

class BoolEval extends IntEval implements BoolAlg<Exp> {
    public Exp bool(boolean b) {
        return () -> new VBool(b);
    }

    public Exp iff(Exp e1,Exp e3) {
        return () -> e1.eval().boolValue() ? e2.eval() : e3.eval();
    }
}

public class Main {
    public static <Exp> Exp m(IntAlg<Exp> alg) {
        return alg.add(alg.lit(4),alg.lit(3));
    }

    public static <Exp> Exp m2(BoolAlg<Exp> alg) {
        return alg.iff(alg.bool(false),m(alg),alg.bool(true));
    }

    public static void main(String[] args) {
        Exp e = m(new IntEval());
        try {
            System.out.println(e.eval());
        } catch (Exception exp) {
            System.out.println("Ops! There is an error...");
        }

        Exp e2 = m2(new BoolEval());
        try {
            System.out.println(e2.eval());
        } catch (Exception exp) {
            System.out.println("Ops! There is an error...");
        }
    }
}

正如你在main的定义中看到的,我们最终需要处理异常。事实上,如果您想更好地处理错误,您应该更改解释器中的代码以捕获那里的异常,然后给出错误消息,例如“您不能将布尔值添加到整数...”和很快。这确实涉及额外的代码(与技术的要点不太相关),但很容易做到。

另请注意,我使用 lambdas 来避免一些带有匿名类的样板文件。在 Java 中,具有单个方法实现的匿名类可以写为 lambda。

如果您不喜欢使用异常的代码,并且您更喜欢更函数式的方法,您还可以使用 Java 的 Optional 类来模拟失败(就像您在 Haskell 等函数式语言中所做的那样) .在这种情况下,您可能会遇到以下情况:

// Values
interface Value {
    Optional<Integer> intValue();
    Optional<Boolean> boolValue();
} 

// The expression interface
interface Exp {
    Optional<Value> eval();
}

我们现在使用 Optional 而不是使操作部分化。带有此选项的 Java 代码(没有模式匹配)不会那么好,但也许有了新的模式匹配功能,它不会那么糟糕。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?