问题
In Scala, I can define an 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
It's possible to return a function, f
, with a return type of Maybe[A]
.
scala> def f[A](x: A): Maybe[A] = Just(x)
f: [A](x: A)Maybe[A]
However, it's also possible to specify that a Just[A]
is returned.
scala> def f[A](x: A): Just[A] = Just(x)
f: [A](x: A)Just[A]
Now I'll do the similar exercise in Haskell:
Prelude> data Option a = None | Some a deriving Show
Prelude> let f x = Some x :: Option Int
Prelude> f 10
Some 10
But, I can't set a return type of a type constructor.
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
Is the simple difference that Scala's Just
is a class, i.e. a legitimate return type? Whereas, in Haskell, a type constructor cannot be a return type?
回答1:
The difference is how Scala chose to implement ADTs. Scala uses case classes that extend a trait in an OOP style, so each case is its own type, whereas Haskell just has multiple constructors for the same type. Since they aren't separate types but essentially just separate functions, you can't distinguish them at the type level. There are extensions that give you some ability to make that type level distinction, but it won't be the same thing as what Scala has. And trying to fit Haskell's type system into Scala's type system is probably not the best of ideas.
In short, Scala approximates ADTs using a form of inheritance, whereas Haskell just has ADTs.
回答2:
bhelkir and leftaroundabout have pointed out why you can't do this exactly in Haskell: there is no notion of subtypes.
But note that with ADTs there are often alternatives that allow you achieve the same effect. In this case, one candidate technique would be to use the Void type in conjunction with Either
:
import Data.Void
f :: Int -> Either Void Int
f x = Right x
Void
is a type that has no defined values. Therefore if you see the type Either Void a
, that means that since there's no value x :: Void
nobody can ever construct any value of the form Left x :: Either Void a
. (The exception is if x
is a guaranteed nonterminating value, but we customarily ignore that possibility.)
That means that an Either Void a
is always of the form Right a
, so for example, you can write this function:
-- | Extract the @a@ from @Either Void a@.
extract :: Either Void a -> a
extract (Left x) = absurd x
extract (Right a) = a
The absurd x
there works basically like this: since x :: Void
that means that there can never actually be a value for x
, then absurd :: Void -> a
, given its type, is a function that is impossible to ever call. The way the type system works this means that it can claim to return any type at all that the caller expects. See this question for some more discussion (though maybe a bit advanced).
来源:https://stackoverflow.com/questions/30623622/type-constructor-as-return-type