问题 什么是Java的奇怪重复通用模式的Haskell等价物?


一个  刚刚提到了java泛型。示例代码是:

public interface A < T extends A < T> > {

}  

一个链接的问题询问

Class Enum<E extends Enum<E>> ...

当我尝试阅读关于这些通用表达式的java文档时,我的眼睛茫然,这对我来说是希腊语。

我希望我可以用Haskell等价来理解它们!

Haskell中的一个或两个示例中的等价(或类似)是什么?


5137
2017-11-18 19:08


起源

你无法真正比​​较命令式和函数式语言。他们只是不能很好地互相翻译。 - marco-fiset
@marcof - 这不是关于FP与命令的关系,而是关于类型系统。我已经读过(但忘了哪里)Java从Haskell的类型系统中获得了泛型类型的灵感,我认为(并且希望)我理解Haskell更好一些。 - Matt Fenwick
Haskell没有子类型,因此很难用Java来解释细微差别 extends 关于Haskell的关键字。 - Dan Burton


答案:


这个技巧用于允许接口引用具体的实现类型,例如强制参数的类型和结果的类型与实现类的类型相同,如下所示:

public interface Num<A extends Num<A>> {
    A add(A other); 
}

这与您在Haskell中使用类型类免费获得的类似:

class Num a where
    (+) :: a -> a -> a

10
2017-11-18 19:19



令我困惑的部分是 T extends A < T>  - 所以基本上,这甚至没有出现在Haskell版本中?凉!是 parametric polymorphism 这个的正确用语? - Matt Fenwick
这不是真正等价的,因为Java版本允许我定义一个类型 Foo implements Num<Bar>,而Haskell版本只允许 Foo implements Num<Foo>。 - sepp2k
@ sepp2k:是的,这是一个近似值。我不认为你可以在Java中获得Haskell的等价物。 - hammar
@ sepp2k你 可以 这样做,但似乎这种Java模式的典型用法是完成hammar描述的内容。 - Dan Burton
@MattFenwick parametric polymorphism 指所有仿制药。即每当你看到 Foo<T> 这意味着 Foo 通过参数化是多态的。 - Erick G. Hagstrom


这很有趣,因为这也使我感到困惑。我们试着对它进行建模。我可能不会在Java中使用这个习惯用法。

如果是一种类型 A 继承自一种类型 B在功能性土地上,这意味着有一种功能 B -> A。此时不要担心类和接口之间的区别;功能翻译几乎没有区别(界面只是功能记录)。让我们做一个非递归的翻译来感受它:

interface Showable {
    string show();
}

interface Describer<T extends Showable> { }

转换为功能记录:

data Showable = Showable { show :: String }

data Describer t = Describer { showable :: t -> Showable }

如果我们忘记了向下转换,那么如果我们在Java中有一些对象,那么我们所知道的就是它是一个 Showable,那么它对应于有一个 Showable Haskell中的对象。在表面上,通过一个 Showable 并通过 string 感觉像是不同的东西,但它们是等价的。

extends Showable 约束进入,如果我们有一个 Describer t 那我们就知道了 t “是” Showable;即存在一种功能 t -> Showable

makeDescriber :: (t -> Showable) -> Describer t
makeDescriber f = Describer { showable = f }

现在让我们来看看hammar的exmaple,结合多态性。

interface Number<A extends Number<A>> {
    A add(A other); 
}

转换为功能记录

data Number a = Number {
    add :: a -> a,
    number :: a -> Number a
}

所以现在如果我们有一个 Number a那我们就知道了 a “是”a Number a;即有一个功能 a -> Number a

java接口的实例 Number 成为一种类型的功能。

intNumber :: Integer -> Number Integer
intNumber x = Number { add = \y -> x + y, number = intNumber }

此功能对应于a class Integer extends Number<Integer>。如果我们有两个整数 x 和 y,我们可以使用这种“OO”风格添加它们:

z :: Integer -> Integer -> Integer
z x y = intNumber x `add` y

通用功能怎么样:

T Add< T extends Number<T> >(T x, T y) { return x.add(y); }

(嗯是正确的Java语法?我对这种风格的体验来自C#)

记住约束成为函数,所以:

add' :: (t -> Number t) -> t -> t -> t
add' n x y = n x `add` y

当然,在Haskell中,我们看到将对象与它支持的操作捆绑在一起是多么复杂,所以我们更喜欢将它们分开:

data Num t = Num { add :: t -> t -> t }

add' :: Num t -> t -> t -> t
add' n x y = add n x y

我们实例化了 Num 具有实际操作的字典,例如。

integerNum :: Num Integer
integerNum = Num { add = (+) }

对于后一种想法,类型类只是一些语法糖。

也许这有帮助吗?我只想看看它是如何翻译的。


3
2017-11-18 20:33



+1最后一部分不清楚。实际添加的位置在哪里 Num t?为什么首先定义它? - is7s
@ is7s,添加了一个实例 Num Integer。 - luqui


我不知道Haskell是否有相同的声明,但Haskell确实有 类型类。例如,Show是类型类,许多对象“扩展”或“实现”显示,也就是说,你可以“显示3”,“显示[1,2,3,4]”等。


-1
2017-11-18 19:15