Reputation: 579
Why doesn't extractEither
typecheck?
data MyEither a b = MyLeft a | MyRight b
deriving (Read, Show)
extractEither :: MyEither a b -> c
extractEither (MyLeft p) = p
The compiler shows:
Couldn't match type `a' with `c'
`a' is a rigid type variable bound by
the type signature for extractEither :: MyEither a b -> c
at /Users/tongmuchenxuan/playground/test.hs:5:1
`c' is a rigid type variable bound by
the type signature for extractEither :: MyEither a b -> c
at /Users/tongmuchenxuan/playground/test.hs:5:1
In the expression: p
In an equation for `extractEither': extractEither (MyLeft p) = p
Isn't `c' general enough to catch any type?
Upvotes: 4
Views: 316
Reputation: 30237
extractEither :: MyEither a b -> c
extractEither (MyLeft p) = p
Isn't `c' general enough to catch any type?
The intuition you're bringing to the table works in object-oriented languages or other languages with subtyping, but not in Haskell or other parametric typing systems (e.g., Java generic). Take this method signature in Java:
public Object blah(Foo whatever);
This method returns Object
, which is the highest type in the hierarchy. This means that an implementation of this method can choose to return any type it wants, and it will be trivially upcast to Object
.
Haskell types don't work like that. You're thinking that your extractFilter
can just return p
because you're implicitly assuming that you, the author of extractEither
, get to pick which type to use for c
, just like the author of blah
gets to pick the runtime type of the result. But in Haskell it's the other way around: the caller of your function gets to pick the types to use for a
, b
and c
, and they can choose an incompatible a
and c
. If I pick any a
≠ c
, there is no terminating piece of code you can write to turn my arbitrary choice of a
into my arbitrary choice of c
.
Upvotes: 1
Reputation: 8937
Basically, your type signature says your function may return a value of any type c
given a MyEither a b
, again, for every a
and b
. However, as the compiler points out, it's not possible to produce any such c
here, as you're actually returning a value of type a
(being p :: a
).
Furthermore, your definition still do not manage the case in which your MyEither a b
is not a Left a
. What should it do, e.g., when you call extractEither (MyRight 1)
? If you try to run a function like that (after correcting the type signature of course), you may incur in what is called a non-exhaustive pattern exception, meaning there's no body definition for extractEither
to handle some possible input patterns.
If you're trying to write a single function that works both for extracting the MyLeft
and the MyRight
values, I'm afraid you should change your mind. In order to extract whatever, left or right, you should have the same type on both sides (i.e. MyEither a a
), which is not so useful.
You should consider to have functions like these in order to extract values. Here, I return Maybe
to avoid the burden of managing calls to error
when for example you try to extract a left from a right value:
extractMyLeft :: MyEither a b -> Maybe a
extractMyRight :: MyEither a b -> Maybe b
edit: see also dblhelix's answer for a suggestion on another possible refactoring that may be useful to almost obtain the type signature you was looking for.
Upvotes: 11
Reputation: 8050
In addition to Riccardo's answer: if you want a function that is able to extract a value of some type c
from a MyEither
value no matter whether it is built by MyLeft
or MyRight
, you could, rather than requiring that the value has type MyEither c c
, "instruct" the function on how to obtain a c
-typed value from either side:
extractEither :: (a -> c) -> (b -> c) -> MyEither a b -> c
extractEither f g (MyLeft x) = f x
extractEither f g (MyRight y) = g y
That is, extractEither
now takes two additional arguments f
and g
: functions that are used to take the extracted values to values of the required type.
This is a very common and useful idiom when working with sum types.
Upvotes: 8
Reputation: 120741
No. c
is any type the caller could wish the function to return, i.e. for a function f :: a -> c
it would always need to be possible to write f x + 1 :: Int
as well as putStr $ f x
as well as main = f x
. Which your function does of course not permit.
What you want is returning a dynamic type. Haskell deliberately does not permit this as easily as some other languages do, because it obviously can lead to runtime errors easily when the returned type is something unexpected. There are various ways to do it, but which is the right one depends on what you actually want this for. Could you give some context? It's likely that the best solution to your problem would not use dynamic types, but something more Haskell-idiomatic.
Perhaps what you want is simply
extractEither :: MyEither a a -> a
extractEither (MyLeft p) = p
extractEither (MyRight p) = p
which requires that the types on both "sides" are the same.
Upvotes: 12