Type Constructor as Return Type

 ̄綄美尐妖づ 提交于 2020-07-05 07:22:43

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!