2015-06-03 93 views
6

在Scala中,我可以定义一个Algebraic Data Type类型构造函数返回值类型

scala> sealed trait Maybe[A] 
defined trait Maybe 

scala> case class Just[A](x: A) extends Maybe[A] 
defined class Just 

scala> case object NothingHere extends Maybe[Nothing] 
defined object NothingHere 

这是可能的返回功能,f,与Maybe[A]返回类型。

scala> def f[A](x: A): Maybe[A] = Just(x) 
f: [A](x: A)Maybe[A] 

但是,也可以指定返回Just[A]。现在

scala> def f[A](x: A): Just[A] = Just(x) 
f: [A](x: A)Just[A] 

我会做类似的工作在Haskell:

Prelude> data Option a = None | Some a deriving Show 
Prelude> let f x = Some x :: Option Int 
Prelude> f 10 
Some 10 

但是,我不能设置一个类型构造的返回类型。

Prelude> let f x = Some x :: Some Int 

<interactive>:10:21: 
    Not in scope: type constructor or class `Some' 
    A data constructor of that name is in scope; did you mean DataKinds? 
Prelude> let f x = None :: None 

是一个简单的区别是Scala的Just是一类,即合法的返回类型?而在Haskell中,类型的构造函数不能是返回类型吗?

回答

10

区别在于Scala选择如何实现ADT。 Scala使用扩展OOP风格的特征的case类,所以每个case都是它自己的类型,而Haskell只有相同类型的多个构造函数。由于它们不是单独的类型,而是基本上单独的函数,因此无法在类型级别上区分它们。有扩展可以让你有一定的能力来实现这种类型的区别,但它不会和Scala具有的一样。试图将Haskell的类型系统纳入Scala的类型系统可能不是最好的想法。

总之,Scala使用一种继承形式来逼近ADT,而Haskell只有ADT。

+3

有扩展给你_similar_的能力,但是非常重要的是Haskell没有子类化,所以你肯定不能做同样的事情.. .. – leftaroundabout

+0

@leftaroundabout改变了一下措辞,使它更清楚我是指的是可以让您在类型级别区分构造函数的扩展,但这不是Scala所具有的。认为它现在更清楚了吗? – bheklilr

0

bhelkir和leftaroundabout指出了你为什么不能在Haskell中完全做到这一点:没有子类型的概念。

但请注意,使用ADT通常会有替代方法,可以使您获得相同的效果。在这种情况下,一个候选技术将结合使用the Void typeEither

import Data.Void 

f :: Int -> Either Void Int 
f x = Right x 

Void是一个没有定义的值的类型。因此,如果您看到类型Either Void a,这意味着由于没有值x :: Void,任何人都无法构造Left x :: Either Void a表单的任何值。 (唯一的例外是,如果x是保证无穷的价值,但we customarily ignore that possibility

这意味着,一个Either Void a的形式Right a的始终,因此,例如,你可以写这个函数:

-- | Extract the @[email protected] from @Either Void [email protected] 
extract :: Either Void a -> a 
extract (Left x) = absurd x 
extract (Right a) = a 

absurd x那里的工作基本上是这样的:因为x :: Void这意味着x实际上永远不会有价值,那么absurd :: Void -> a,因为它的类型,是一个不可能调用的函数。类型系统的工作方式意味着它可以声明返回任何类型的调用者所期望的。有关更多讨论,请参见this question(尽管可能有点高级)。

+0

也许我错过了一些东西,但为什么不直接返回'a'? – dfeuer

+0

@dfeuer:哦!是的,我正在删除那个位... –

+0

我的意思是说,如果你排除底部,'或者Void a'与a'同构。为什么要去那么麻烦? – dfeuer