2015-06-03 126 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