比方說,我的應用程序有兩種類型的錯誤:Haskell的數據構造W/Scala的子類比較
λ: data AppError = FailedLogin | InvalidMessage deriving Show
它login
和sendMessage
功能:
λ: let login user pw = Left FailedLogin :: Either AppError String
λ: let sendMessage msg token = Left InvalidMessage :: Either AppError Int
λ: login "foo" "bar" >>= (\token -> sendMessage "hello world" token)
Left FailedLogin
但是,請注意,返回類型是Either AppError String
。我不能指定Either FailedLogin ...
:
λ: let f = Left FailedLogin :: Either FailedLogin String
<interactive>:18:36:
Not in scope: type constructor or class ‘FailedLogin’
A data constructor of that name is in scope; did you mean DataKinds?
什麼是對這種現象的原因,即不能在Either
的Left
類型使用數據構建器?
其次,讓我們說,我複製上面的代碼中斯卡拉:
scala> sealed trait AppError
defined trait AppError
scala> case object FailedLogin extends AppError
defined object FailedLogin
scala> case object InvalidMessage extends AppError
defined object InvalidMessage
scala> def login(user: String, password: String): Either[FailedLogin.type, String] = ???
login: (user: String, password: String)Either[FailedLogin.type,String]
注意,我可以指定FailedLogin
作爲返回類型。如果我需要撥打電話:login >>= sendMessage
,這是沒有意義的。但是,如果我只預期在Either
的Left
單個值,將使用case object
是慣用?還是值得創建一個帶有單個子類的sealed trait ...
,引用Either
的左邊的特徵?
錯誤位於'::'的右側; 'FailedLogin ...'混合了兩種不同的東西 - 'Either'(一個類型構造函數)和'FailedLogin'(數據構造函數),你需要一些'類型構造函數',在這種情況下是'AppError'。注意'AppError'和'FailedLogin'存在於不同的命名空間中! – epsilonhalbe
我認爲這裏的關鍵區別可以概括如下。Scala具有子類型(來自OOP傳統),像大多數FP語言一樣,Haskell沒有子類型。這就是爲什麼Scala能夠將'FailedLogin.type'作爲一個類型,它是'AppError'的一個子類型。相反,Haskell沒有這種類型,只有'AppError'類型的_value_'FailedLogin'。 – chi