如何将以下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();
}
}
Run Code Online (Sandbox Code Playgroud)
哈斯克尔
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
Run Code Online (Sandbox Code Playgroud)
lef*_*out 18
简单回答:不要!Haskell不是一种面向对象语言,假装它并不是很好,只是试图将继承模式转换为类型类和ADT的混合.
你List<Foo>的Java Foo a => [a]与Haskell中的完全不同:这样的签名实际上意味着forall a . Foo a => [a].这a基本上是函数的额外参数,即它可以从外部选择在此Foo使用的特定实例.
在Java中恰恰相反:您根本无法控制列表中的类型,只知道它们实现了Foo接口.在Haskell中,我们将其称为存在类型,并且通常避免它,因为它是愚蠢的.好的,你不同意 - 对不起,你错了!
......不,严重的是,如果你有这样一个存在列表,你可以永远做的唯一的事情1是执行bar动作.好吧,那么为什么不立刻把这个动作放在列表中呢!IO()动作就像其他任何东西一样(它们不是函数;无论如何,它们也可以放在列表中).我会写你的程序
xs :: [IO ()]
xs = [bar Bar1, bar Bar2]
Run Code Online (Sandbox Code Playgroud)
{-# LANGUAGE ExistentialQuantification #-}
data AFoo = forall a. Foo a => AFoo a
xs :: [AFoo]
xs = [AFoo Bar1, AFoo Bar2]
main = mapM_ (\(AFoo f) -> bar f) xs
Run Code Online (Sandbox Code Playgroud)
因为这已经变得非常咆哮:我确实认为OO风格对于某些应用来说比Haskell的功能风格更方便.并且存在确实有它们的有效用例(但是,就像chunksOf 50,我宁愿把它们写成GADT).只是,对于许多问题,Haskell允许更简洁,更强大,更通用,但在许多方面比简单的解决方案更简单,而不是"如果你有一把锤子......"继承你在OO编程中使用,所以在使用存在之前你应该对Haskell的"原生"特征有一个正确的感觉.
1是的,我知道你也可以用Java做"类型安全动态演员"等.在Haskell中,有Typeable这类东西的类.但是,如果采取这种方式,你也可以立即使用动态语言.
您的翻译有一个重要的缺点.而在您的Java版本中,您可以轻松添加一个Bar3也支持该Foo界面,但如果不触及BarHaskell版本中的类型,则无法轻松实现相同的功能.所以这不是你要找的版本.
你真正想要的是完全摆脱类型类的需要.而是具有表示以下行为的数据类型Foo:
data Foo = Foo { bar :: IO () }
Run Code Online (Sandbox Code Playgroud)
然后,您可以构建满足Foo接口的对象列表作为[Foo].
这是可能的,但可能不可取.像"存在类型"这样的语言扩展允许动态多态.
存在类型的想法如下:
data Foo = Foo a
Run Code Online (Sandbox Code Playgroud)
请注意,类型变量"a"不会出现在ADT声明的左侧.可能实现动态多态列表和映射函数的简单示例:
{-# LANGUAGE UnicodeSyntax, Rank2Types, GADTs, ConstraintKinds #-}
import Data.Constraint
-- List datatype:
data PList ? where
Nil ? PList ?
(:*) ? ? a ? a ? PList ? ? PList ?
infixr 6 :*
-- Polymorphic map:
pmap ? (? a. ? a ? a ? b) ? PList ? ? [b]
pmap _ Nil = []
pmap f (a :* t) = f a : pmap f t
main = let
-- Declare list of arbitrary typed values with overloaded instance Show:
l ? PList Show
l = "Truly polymorphic list " :* 'a' :* 1 :* (2, 2) :* Nil
in do
-- Map show to list:
print $ pmap show l
Run Code Online (Sandbox Code Playgroud)
输出:
["\"Truly polymorphic list \"","'a'","1","(2,2)"]
Run Code Online (Sandbox Code Playgroud)
使用此技术的另一个例子:
class Show a ? Named a where
name ? a ? String
instance Named Int where
name a = "I'm Int and my value is " ++ show a
instance Named Char where
name a = "I'm Char and my value is " ++ show a
main = let
-- Declare list of arbitrary typed values with overloaded instance Named:
l2 :: PList Named
l2 = 'a' :* (1 ? Int) :* Nil
in do
print $ pmap name l2
print $ pmap show l2
Run Code Online (Sandbox Code Playgroud)
输出:
["I'm Char and my value is 'a'","I'm Int and my value is 1"]
["'a'","1"]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2082 次 |
| 最近记录: |