I'm new to Haskell. I have the types:
type Variable = String
type Value = Float
type EvalError = [Variable]
type EvalResult = Either EvalError Value
And I want to create a function that I'll use a function to use it on 2 EvalResult types, and get a EvalResult accordingly.
If I get 2 Value types, I want to use the function on them (sum/sub for example), and If I'll get EvalError I want to return the EvalError.
What I did:
evalResultOp :: (a -> b -> c) -> EvalResult a -> EvalResult b -> EvalResult c
evalResultOp f (Left a) (Left b) = Left (a ++ b)
evalResultOp f (Left a) (Right b) = Left a
evalResultOp f (Right a) (Left b) = Left b
evalResultOp f (Right a) (Right b) = Right (f a b)
The error:
hs3.hs:46:34: error:
• Expecting one fewer arguments to ‘EvalResult’
Expected kind ‘* -> *’, but ‘EvalResult’ has kind ‘*’
• In the type signature:
evalResultOp :: (a -> b -> c)
-> EvalResult a -> EvalResult b -> EvalResult c
|
46 | evalResultOp :: (a -> b -> c) -> EvalResult a -> EvalResult b -> EvalResult c | ^^^^^^^^^^^^
hs3.hs:46:50: error:
• Expecting one fewer arguments to ‘EvalResult’
Expected kind ‘* -> *’, but ‘EvalResult’ has kind ‘*’
• In the type signature:
evalResultOp :: (a -> b -> c)
-> EvalResult a -> EvalResult b -> EvalResult c
|
46 | evalResultOp :: (a -> b -> c) -> EvalResult a -> EvalResult b -> EvalResult c | ^^^^^^^^^^^^
hs3.hs:46:66: error:
• Expecting one fewer arguments to ‘EvalResult’
Expected kind ‘* -> *’, but ‘EvalResult’ has kind ‘*’
• In the type signature:
evalResultOp :: (a -> b -> c)
-> EvalResult a -> EvalResult b -> EvalResult c
|
46 | evalResultOp :: (a -> b -> c) -> EvalResult a -> EvalResult b -> EvalResult c |
The problem is that you define your
EvalResulttype as:since
Eitheris a type constructor, that takes two types, this means that you have constructed a type now (without any type parameters). If you write a function with type signature(a -> b -> c) -> EvalResult a -> EvalResult b -> EvalResult c, then Haskell thus will eventually have to construct typesEvalResult a ~ Either EvalError Value a, and sinceEitheronly takes two type parameters, this makes no sense.My guess is that you want to define
or shorter:
Now
EvakResultthus acts like a type constructor that can take one type parameter, and then the function indeed works over types.We can make the implementation more compact by writing:
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m cis a function that works over monadic typesm.Either ais a monadic type, it is defined as:liftM2is implemented as:which is syntactical sugar for:
So basically we evaluate
mx >>= (...)by checking ifmxis aRight, if it is aLeft, we return the content of theLeft. Since we already handled the case with twoLefts, that case is no longer possible, so we know that the secondEvalResultis aRight, in that case we thus return the firstLeft. In casemxis aRight x, we now inspectmy >>= (..)and check the state ofmy. Ifmyis aLeft, we again return thatLeft, otherwise, we return thereturn (f x y), sincereturnfor anEither amonad, is actuallyRight, we thus wrap the content off x yin theRightdata constructor.