在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中,类型构造函数不能是返回类型?
答案 0 :(得分:10)
不同之处在于Scala如何选择实施ADT。 Scala使用以OOP样式扩展特征的case类,因此每个case都是它自己的类型,而Haskell只有多个相同类型的构造函数。由于它们不是单独的类型,而是基本上只是单独的函数,因此您无法在类型级别区分它们。有一些扩展可以让你有一些能够进行类型级别区分的扩展,但它与Scala所具有的不同。尝试将Haskell的类型系统融入Scala的类型系统可能不是最好的想法。
简而言之,Scala使用一种继承形式来近似ADT,而Haskell只有ADT。
答案 1 :(得分:0)
bhelkir和leftaroundabout指出了为什么你不能在Haskell中完全做到这一点:没有子类型的概念。
但请注意,对于ADT,通常有替代方案可以实现相同的效果。在这种情况下,一种候选技术是将the Void
type与Either
结合使用:
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 @a@ from @Either Void a@.
extract :: Either Void a -> a
extract (Left x) = absurd x
extract (Right a) = a
absurd x
基本上是这样的:因为x :: Void
表示实际上永远不会有x
的值,所以absurd :: Void -> a
给定其类型,是一个不可能打电话的功能。类型系统的工作方式意味着它可以声称返回调用者期望的任何类型。有关更多讨论,请参阅this question。(尽管可能有点高级)。