如何将以下 Java 实现转换为 Haskell?
这里的主要目的是拥有一个包含各种元素的列表,这些元素是特定界面的子类型。
我试图在下面制作一个 Haskell 版本,但未能达到我的目的。
这里的重点是xs
有类型 [Bar]
而不是 Foo a => [a]
这是否意味着 Haskell 不能做到这一点,我应该换一种方式思考吗?
java
interface Foo {
void bar ();
}
public class Bar1 implements Foo {
@Override
public void bar() {
System.out.println("I am bar 1 class");
}
}
public class Bar2 implements Foo {
@Override
public void bar() {
System.out.println("I am bar 2 class");
}
}
public static void main(String[] args) {
// The major purpose here is having a list
// that contains elements which are sub-type of "Foo"
List<Foo> ys = new ArrayList<Foo>();
Foo e1 = new Bar1();
Foo e2 = new Bar2();
ys.add(e1);
ys.add(e2);
for (Foo foo : ys) {
foo.bar();
}
}
haskell
class Foo a where
bar :: a -> IO ()
data Bar = Bar1 | Bar2
instance Foo Bar where
bar Bar1 = print "I am Bar1"
bar Bar2 = print "I am Bar2"
--xs :: Foo a => [a]
xs :: [Bar]
xs = [Bar1, Bar2]
main :: IO ()
main = mapM_ bar xs
请您参考如下方法:
简单回答:don't! Haskell is not an OO language, and it's not much good pretending it is and just trying to translate inheritance patterns to a mixture of type classes and ADTs .
您的 List<Foo>
在 Java 中与 Foo a => [a]
完全不同。在 Haskell 中:这样的签名实际上意味着 forall a . Foo a => [a]
. a
基本上是函数的一个额外参数,即可以从外部选择特定的 Foo
此处使用实例。
在 Java 中恰恰相反:你根本无法控制列表中的类型,只知道它们实现了 Foo
界面。在 Haskell 中,我们称其为存在类型,并且通常避免使用它,因为它很愚蠢。好吧,你不同意——对不起,你错了!
...不,说真的,如果你有这样一个存在列表,你唯一能做的就是执行 bar
行动。好吧,那为什么不立即将该操作放入列表中! IO()
Action 和其他任何东西一样都是值(它们不是函数;无论如何,它们也可以放在列表中)。我会写你的程序
xs :: [IO ()]
xs = [bar Bar1, bar Bar2]
也就是说,如果你绝对坚持你也可以在 Haskell 中拥有存在列表:
{-# LANGUAGE ExistentialQuantification #-}
data AFoo = forall a. Foo a => AFoo a
xs :: [AFoo]
xs = [AFoo Bar1, AFoo Bar2]
main = mapM_ (\(AFoo f) -> bar f) xs
因为这已经成为一种咆哮:我承认 OO 风格对于某些应用程序来说比 Haskell 的函数风格更方便。存在主义确实有其有效的用例(尽管像 chunksOf 50 一样,我更喜欢将它们写为 GADTs )。只是,对于许多问题,Haskell 允许更简洁、强大、通用,但在许多方面比在 OO 编程中使用的“如果你只有一把锤子......”继承更简单的解决方案,所以在使用存在之前你应该对 Haskell 的“原生”特性有一个正确的感觉。
1是的,我知道您还可以在 Java 中执行“类型安全动态转换”等。在 Haskell 中,有
Typeable
这类东西的课。但是,如果您采用这样的路径,您不妨立即使用动态语言。