3

Holo314/Coeffect:使用Loom的ExtentLocals将部分Coeffect系统添加到Java

 1 year ago
source link: https://www.jdon.com/61861
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Holo314/Coeffect:使用Loom的ExtentLocals将部分Coeffect系统添加到Java
在 Java 中,通常有两种策略来管理方法所需的参数:

  1. 将值作为参数传递
  2. 将值作为类的字段

此外,为了确保线程安全,我们需要做更多的工作。对于第一种方法,问题不太明显,但对于后者,则更难处理。
确保安全的一种方法是使用 Java 的ThreadLocal,它可以确保参数不能通过不同的线程:

public class Example {
    private static ThreadLocal<String> TL = new ThreadLocal<>();

    public void foo() {
        System.out.println(Example.get());
    }

    public static void main(String[] args) {
        var x = new Example();
        CompletableFuture.runAsync(() -> {
           TL.set("^o^");
           Thread.sleep(3000); // omitting exception handling
            x.foo();
        });
        CompletableFuture.runAsync(() -> {
            Thread.sleep(1000); // omitting exception handling
            TL.set("o7");
            x.foo();
        });
    }
}

这将打印

o7
^o^

Project Loom 已经添加了ExtentLocal,它基本上就是一个结构化的ThreadLocal.

ThreadLocal和ExtentLocal的最有问题的是:我们失去了类型安全。对于ThreadLocal,你可以得到意想不到的空值,而对于ExtentLocal,你会得到一个异常。

任何对ThreadLocal或ExtentLocal的使用都应该附加一个null检查或绑定检查。此外,如果这两者中的一个不是私有的,就会产生耦合、安全问题和模糊的API。

另一方面,将依赖项作为参数发送还有其他问题,但我要谈的主要有两个:

  1. 强制显式绑定

第一点很清楚,您可以获取具有 5/6 或更多参数的方法,这会在调用站点中创建长签名以及长签名。
第二点更容易忽略,但这里有一个例子:

public static void main(String[] args) {
    foo(666)
}
public static void foo(int x) {
    bar(x);
}
public static void bar(int x) {
   System.out.println(x);
}

请注意,foo接收一个参数只是为了将它传递给bar,它实际上并没有对它做任何事情。

解决方案
这个库提供的解决方案是创建一个(部分)Coeffect System
这个想法是使用ExtentLocal一个编译器插件来增加安全性和明确性。
Implementation note:无法创建此系统,ThreadLocal因为无法控制ThreadLocalremove.

在深入了解细节之前,让我们看看上面的例子是什么样子的:

public static void main(String[]args){
    Coeffect.with(666)
        .run(() -> foo());
}

@WithContext(Integer.class)
public static void foo(){
   bar();
}

@WithContext(Integer.class)
public static void bar(){
   System.out.println(Coeffect.get(Integer.class));
}
  • 我们在bar中使用Coeffect.get(Integer.class)来获取存储在全局Coeffect中的顶部整数。
  • 我们用@WithContext(Integer.class)对bar进行了注解,以表示我们在方法中使用Integer。
  • 我们在foo中调用了bar。
  • 我们用@WithContext(Integer.class)注释了foo,以表示我们正在使用一个需要Integer的方法。
  • 我们调用Coeffect.with(666)将666放在Integer.class的栈顶。
  • 我们在Coeffect.with(666)上调用run,在当前栈中运行一个Runnable。
  • 在Coeffect.with(666).run子句中,我们正在运行foo
  • 我们不需要在main方法中指定@WithContext(Integer.class),因为我们没有使用任何非绑定的依赖关系

请注意,所有这些点都是在编译时强制执行的,删除任何一个,@WithContext编译器都会对你大喊大叫。

Coeffect建立在ExtentLocal项目 Loom 附带的基础之上,以补充结构化并发,这意味着所有与线程一起工作并Coeffect一起使用的都应该使用结构化并发,任何非结构化并发的使用都可能导致误报。

名字Coeffect来自Effectsystem。Java确实有一个(部分)Effect System,checked exceptions,一个 effect 和一个 coeffect 的区别是比较细的,我希望以后给Coeffect类型系统和 Checked Exceptions 一样的力量

详细点击标题


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK