I want to be able to polymorphically handle multiple Either
values who share the same Left
type, but not the same Right
type, e.g.:
foo = Left "foo" :: Either String Int
bar = Left "bar" :: Either String Char
list = [foo, bar]
The Left
value indicates an error message in case the computation failed, while the Right
value has different types depending on different computations. In case of failure I only care about gathering the errors on the left, so it would be useful to be able to treat all of them the same for this purpose.
However, this fails, and the compiler expects both type variables to match:
* Couldn't match type `Char' with `Int'
Expected type: Either String Int
Actual type: Either String Char
* In the expression: bar
In the expression: [foo, bar]
In an equation for `list': list = [foo, bar]
After a lot of searching, the only thing I stumbled upon is something called a "Partial Type Signature", which uses the "_" wildcard, and has to be enabled explicitly by a flag for it to compile. However, even after enabling it, it still doesn't seem to change anything:
foo = Left "foo" :: Either String Int
bar = Left "bar" :: Either String Char
list = [foo, bar] :: Either String _
And I receive the same error:
* Couldn't match type `Char' with `Int'
Expected type: Either String Int
Actual type: Either String Char
* In the expression: bar
In the expression: [foo, bar] :: Either String _
In an equation for `list': list = [foo, bar] :: Either String _
In Java (if it had an Either
type), I could simply do something like this:
List<Either<String, ?>> list = asList(foo, bar);
And it would have worked just fine, type safe and whatnot.
Is there a type variable wildcard in Haskell corresponding to ?
in Java? If not, how do you deal with such cases where you don't care about a certain type variable and want to abstract it? Is there an alternative?
More details:
Realizing that I wasn't clear enough about my problem, I will elaborate:
What I'm trying to implement is a command line parser. It should receive the user's input (command line arguments) and return a Params
value, where:
data Params = Params String Double Bool
For simplicity's sake I only included 3 values in the value constructor above, but actually there are many more.
Since there are many different parameters, each one can fail in its own way - the user might not provide the necessary argument, the provided argument is invalid, etc.
The parsing should succeed only if all of the arguments provided are valid. If any of them is invalid, then the entire computation should fail. So I thought about the following solution (again, it's simplified, so please ignore other issues like efficiency):
parse :: [String] -> Either [String] Params
parse args = if successful
then Right (Params strParam doubleParam boolParam)
else Left errors
where successful = null errors
(Right strParam) = eitherStr
(Right doubleParam) = eitherDouble
(Right boolParam) = eitherBool
errors = lefts [eitherStr, eitherDouble, eitherBool]
eitherStr = parseStr args
eitherDouble = parseDouble args
eitherBool = parseBool args
parseStr :: [String] -> Either String String
parseDouble :: [String] -> Either String Double
parseBool :: [String] -> Either String Bool
But it doesn't compile, since I can't handle eitherStr
, eitherDouble
and eitherBool
as having the same type, which would have been possible if there was a wildcard type variable. Hence my question.
Under the current circumstances, I have to resort to something like this:
parse :: [String] -> Either [String] Params
parse args = if successful
then Right (Params strParam doubleParam boolParam)
else Left errors
where successful = null errors
(Right strParam) = eitherStr
(Right doubleParam) = eitherDouble
(Right boolParam) = eitherBool
errors = concat [strErrors, doubleErrors, boolErrors]
strErrors = getErrors eitherStr
doubleErrors = getErrors eitherDouble
boolErrors = getErrors eitherBool
eitherStr = parseStr args
eitherDouble = parseDouble args
eitherBool = parseBool args
getErrors = lefts . (:[])
parseStr :: [String] -> Either String String
parseDouble :: [String] -> Either String Double
parseBool :: [String] -> Either String Bool
Doable, but messier. Of course, it's possible that my solution is completely off track, and there's a better idiomatic functional solution, which I'll be glad to hear about.
It was said that Java has Object
as a universal upper bound, so the comparison is misleading. But it seems to me that there can always be a theoretical upper bound, even if in the worst case that upper bound means a value which you can't use. Which is just fine for cases where you don't need it, such as mine.
After trying existential types:
Existential types are of no help to me. They define a new type that wraps the old one, so functions that accept Either
won't accept the new type, and can't be reused. So instead of using the existing lefts
function, I have to implement it myself for the new type, in addition to the hassle of defining a new type:
data SomeEither l = forall r. SomeEither (Either l r)
data Params = Params String Double Bool deriving (Show)
parse :: [String] -> Either [String] Params
parse args = if successful
then Right (Params strParam doubleParam boolParam)
else Left errors
where successful = null errors
(Right strParam) = eitherStr
(Right doubleParam) = eitherDouble
(Right boolParam) = eitherBool
errors = lefts' [SomeEither eitherStr, SomeEither eitherDouble, SomeEither eitherBool]
eitherStr = parseStr args
eitherDouble = parseDouble args
eitherBool = parseBool args
lefts' :: [SomeEither l] -> [l]
lefts' [] = []
lefts' ((SomeEither (Left l)):xs) = l:(lefts' xs)
lefts' (x:xs) = lefts' xs
parseStr :: [String] -> Either String String
parseDouble :: [String] -> Either String Double
parseBool :: [String] -> Either String Bool
The whole point was to do less work and have cleaner code. The use of existential types in this case does the opposite.