34

假设我有以下课程:

public class FixExpr {
  Expr<FixExpr> in;
}

现在我想介绍一个通用论点,对 Expr 的使用进行抽象:

public class Fix<F> {
  F<Fix<F>> in;
}

但 Eclipse 不喜欢这样:

F 类型不是通用的;它不能用参数 <Fix<F>> 参数化

这是可能的,还是我忽略了导致这个特定实例中断的东西?

一些背景信息:在 Haskell 中,这是编写泛型函数的常用方法;我正在尝试将其移植到 Java。上例中的类型参数 F 具有种类 * -> * 而不是通常的种类 *。在 Haskell 中,它看起来像这样:

newtype Fix f = In { out :: f (Fix f) }
4

6 回答 6

29

我认为Java泛型根本不支持您尝试做的事情。更简单的情况

public class Foo<T> {
    public T<String> bar() { return null; }
}

也不使用 javac 编译。

由于 Java 在编译时不知道什么T是,它不能保证它T<String>是有意义的。例如,如果您创建了Foo<BufferedImage>,bar将具有签名

public BufferedImage<String> bar()

这是荒谬的。由于没有机制强制您仅Foo使用泛型Ts 实例化 s,因此它拒绝编译。

于 2009-05-18T10:15:26.540 回答
25

也许您可以尝试 Scala,它是一种在 JVM 上运行的函数式语言,它支持更高种类的泛型。


[拉胡尔 G编辑]

以下是您的特定示例大致转换为 Scala 的方式:

trait Expr[+A]

trait FixExpr {
  val in: Expr[FixExpr]
}

trait Fix[F[_]] {
  val in: F[Fix[F]]
}
于 2009-05-25T03:29:34.973 回答
4

为了传递一个类型参数,类型定义必须声明它接受一个(它必须是通用的)。显然,您F不是泛型类型。

更新:这条线

F<Fix<F>> in;

声明一个类型变量,F它接受一个类型参数,其值为Fix,它本身接受一个类型参数,其值为FF在您的示例中甚至没有定义。我想你可能想要

Fix<F> in;

这将为您提供一个类型变量Fix(您在示例中定义的类型),您将向其传递带有 value 的类型参数F。由于Fix被定义为接受类型参数,因此这是可行的。

更新 2:重新阅读您的标题,现在我认为您可能正在尝试做一些类似于“为高级类型实现平等权利”(PDF 警报)中提出的方法。如果是这样,Java 不支持,但您可以尝试 Scala。

于 2009-05-18T09:55:26.613 回答
2

尽管如此,还是有一些方法可以在 Java 中编码更高种类的泛型。请看看high-kinded-java 项目

使用它作为库,您可以像这样修改代码:

public class Fix<F extends Type.Constructor> {
    Type.App<F, Fix<F>> in;
}

您可能应该为您的课程添加@GenerateTypeConstructor注释Expr

@GenerateTypeConstructor
public class Expr<S> {
    // ...
}

此注释生成 ExprTypeConstructor 类。现在您可以像这样处理您的 Expr 修复:

class Main {
    void run() {
        runWithTyConstr(ExprTypeConstructor.get);
    }

    <E extends Type.Constructor> void runWithTyConstr(ExprTypeConstructor.Is<E> tyConstrKnowledge) {
        Expr<Fix<E>> one = Expr.lit(1);
        Expr<Fix<E>> two = Expr.lit(2);

        // convertToTypeApp method is generated by annotation processor
        Type.App<E, Fix<E>> oneAsTyApp = tyConstrKnowledge.convertToTypeApp(one);
        Type.App<E, Fix<E>> twoAsTyApp = tyConstrKnowledge.convertToTypeApp(two);

        Fix<E> oneFix = new Fix<>(oneAsTyApp);
        Fix<E> twoFix = new Fix<>(twoAsTyApp);

        Expr<Fix<E>> addition = Expr.add(oneFix, twoFix);
        process(addition, tyConstrKnowledge);
    }

    <E extends Type.Constructor> void process(
            Fix<E> fixedPoint,
            ExprTypeConstructor.Is<E> tyConstrKnowledge) {

        Type.App<E, Fix<E>> inTyApp = fixedPoint.getIn();

        // convertToExpr method is generated by annotation processor
        Expr<Fix<E>> in = tyConstrKnowledge.convertToExpr(inTyApp);

        for (Fix<E> subExpr: in.getSubExpressions()) {
            process(subExpr, tyConstrKnowledge);
        }
    }

}
于 2016-08-15T14:12:27.610 回答
1

看起来你可能想要类似的东西:

public class Fix<F extends Fix<F>> {
    private F in;
}

(请参阅 Enum 类,以及有关其泛型的问题。)

于 2009-05-18T10:13:31.757 回答
1

正如 Victor 所指出的,有一种迂回的方法可以在 Java 中编码更高种类的类型。它的要点是引入一种类型H<F, T>进行编码F<T>。然后可以使用它来编码函子的定点(即 HaskellFix类型):

public interface Functor<F, T> {
    <R> H<F, R> map(Function<T, R> f);
}

public static record Fix<F extends H<F, T> & Functor<F, T>, T>(F f) {
    public Functor<F, Fix<F, T>> unfix() {
        return (Functor<F, Fix<F, T>>) f;
    }
}

从这里您可以继续并在初始代数上实现变态:

public interface Algebra<F, T> extends Function<H<F, T>, T> {}

public static <F extends H<F, T> & Functor<F, T>, T> Function<Fix<F, T>, T> cata(Algebra<F, T> alg) {
    return fix -> alg.apply(fix.unfix().map(cata(alg)));
}

请参阅我的GitHub 存储库以获取工作代码,包括一些示例代数。(注意,IDE 之类的 IntelliJ 与代码斗争,尽管它在 Java 15 上编译和运行得很好)。

于 2021-01-02T22:01:34.927 回答