我如何在Haskell中创建一个异构列表? (最初在Java中) [英] How do I make an heterogeneous list in Haskell? (originally in Java)

查看:129
本文介绍了我如何在Haskell中创建一个异构列表? (最初在Java中)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何将下列Java实现转换成Haskell?

这里的主要目的是列出包含各种元素的列表,这些元素是特定界面的子类型。
我试图制作一个Haskell版本以下,但未能达到我的目的。
这里的要点是 xs 的类型是 [Bar] 而不是 Foo a => [a]



这是否意味着Haskell无法做到这一点,我是否应该以另一种方式思考?

Java $ b

  interface Foo {
void bar();
}

public class Bar1 implements Foo {
@Override
public void bar(){
System.out.println(我是第1小节类);



public class Bar2 implements Foo {
@Override
public void bar(){
System.out.println( 我是第二班);



public static void main(String [] args){
//这里的主要用途是列出
//包含属于Foo子元素的元素
List< Foo> ys = new ArrayList< Foo>();

Foo e1 = new Bar1();
Foo e2 = new Bar2();

ys.add(e1);
ys.add(e2); (Foo foo:ys)

{
foo.bar();


Haskell

  class Foo a where 
bar :: a - > IO()

data Bar = Bar1 | Bar2

实例Foo Bar其中
bar Bar1 = print我是Bar1
bar Bar2 = print我是Bar2

--xs :: Foo a => [a]
xs :: [Bar]
xs = [Bar1,Bar2]

main :: IO()
main = mapM_ bar xs


解决方案

简单的答案:的! Haskell不是一种面向对象的语言,假装它并没有什么好处,只是试图将继承模式转换为类型类和ADT的混合体。

Java中的 List< Foo> Foo a => Haskell中的[a] :这样的签名实际上意味着 forall a。 Foo a => [α] a 基本上是该函数的一个额外参数,即它可以从外部选择特定的 Foo 实例在这里使用。

在Java中恰恰相反:在那里你完全不能控制列表中的类型,只知道它们实现了 Foo 界面。在Haskell中,我们称之为存在类型,通常避免它,因为它很愚蠢。好吧,你不同意 - 对不起,你错了!

...不,认真的是,如果你有这样一个存在清单,你唯一能做的事情就是 1 执行操作。那么,为什么不直接把这个动作放在列表中呢!! IO()操作就像任何其他值一样(它们不是函数;反正它们也可以放在列表中)。我会写你的程序

pre $ $ c $ x $ xs :: [IO()]
xs = [bar Bar1,bar Bar2 ]



也就是说,如果你绝对坚持你可以拥有存在列表Haskell:

  { - #LANGUAGE ExistentialQuantification# - } 

数据AFoo = forall a。 Foo a => AFoo a

xs :: [AFoo]
xs = [AFoo Bar1,AFoo Bar2]

main = mapM_(\(AFoo f) - > bar f)xs






由于这已经变得相当咆哮:我确认OO风格对于某些应用程序比Haskell的功能风格更方便。而存在确实有他们的有效用例(虽然,像chunk的50,我宁愿写他们作为 GADT )。只有对于许多问题,Haskell允许更简洁,更强大,更通用,但在许多方面都比在OO编程中使用的如果你有一把锤子......继承更简单的解决方案,所以在使用存在之前,你应该对Haskell的原生功能有一个恰当的感觉。






1 是的,我知道你也可以在Java中进行类型安全动态转换等。在Haskell中,这种类型的是 Typeable 类。但如果你采取这样的路径,你也可以马上使用动态语言。


How to convert following Java implementation into Haskell?

The major purpose here is having a list that contains various elements which are sub-type of a particular interface.
I tried to make a Haskell version below but fail to meet my purpose. The point here is xs has type [Bar] rather than Foo a => [a]

Do that mean Haskell can not do this and shall I think in another way?

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

解决方案

Simple answer: 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.

Your List<Foo> in Java is quite substantially different from a Foo a => [a] in Haskell: such a signature actually means forall a . Foo a => [a]. The a is basically an extra argument to the function, i.e. it can be chosen from the outside what particular Foo instance is used here.

Quite the opposite in Java: there you don't have any control of what types are in the list at all, only know that they implement the Foo interface. In Haskell, we call this an existential type, and generally avoid it because it's stupid. Ok, you disagree – sorry, you're wrong!
...No, seriously, if you have such an existential list, the only thing you can ever do1 is execute the bar action. Well, then why not simply put that action in the list right away! IO() actions are values just like anything else (they aren't functions; anyway those can be put in lists just as well). I'd write your program

xs :: [IO ()]
xs = [bar Bar1, bar Bar2]


That said, if you absolutely insist you can have existential lists in Haskell as well:

{-# LANGUAGE ExistentialQuantification #-}

data AFoo = forall a. Foo a => AFoo a

xs :: [AFoo]
xs = [AFoo Bar1, AFoo Bar2]

main = mapM_ (\(AFoo f) -> bar f) xs


As this has become quite a rant: I do acknoledge that OO style is for some applications more convenient than Haskell's functional style. And existentials do have their valid use cases (though, like chunksOf 50, I rather prefer to write them as GADTs). Only, for lots of problems Haskell allows for far more concise, powerful, general, yet in many ways simpler solutions than the "if all you have's a hammer..." inheritance you'd use in OO programming, so before using existentials you should get a proper feeling for Haskell's "native" features.


1Yeah, I know you can also do "typesafe dynamic casts" etc. in Java. In Haskell, there's the Typeable class for this kind of stuff. But you might as well use a dynamic language right away if you take such paths.

这篇关于我如何在Haskell中创建一个异构列表? (最初在Java中)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆