简体   繁体   中英

Haskell polymorphic function Using Either Left Right

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 EvalResult type as:

type EvalResult = Either EvalError Value

since Either is 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 types EvalResult a ~ Either EvalError Value a , and since Either only takes two type parameters, this makes no sense.

My guess is that you want to define

type EvalResult  = Either EvalError 

or shorter:

type EvalResult = Either EvalError

Now EvakResult thus 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:

import Control.Monad(liftM2)

evalResultOp :: (a -> b -> c) -> EvalResult a -> EvalResult b -> EvalResult c
evalResultOp f (Left a) (Left b) = Left (a ++ b)
evalResultOp f x y = liftM2 f x y

liftM2 :: Monad m => (a -> b -> c) -> ma -> mb -> mc is a function that works over monadic types m . Either a is a monadic type, it is defined as:

 instance Monad (Either a) where return = Right (>>=) (Right x) f = fx (>>=) (Left l) _ = Left l 

liftM2 is implemented as:

 liftM2 :: Monad m => (a -> b -> c) -> ma -> mb -> mc liftM2 f xm ym = do x <- mx y <- my return (fxy) 

which is syntactical sugar for:

liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
liftM2 f xm ym = mx >>= (\x -> my >>= \y -> return (f x y))

So basically we evaluate mx >>= (...) by checking if mx is a Right , if it is a Left , we return the content of the Left . Since we already handled the case with two Left s, that case is no longer possible, so we know that the second EvalResult is a Right , in that case we thus return the first Left . In case mx is a Right x , we now inspect my >>= (..) and check the state of my . If my is a Left , we again return that Left , otherwise, we return the return (fxy) , since return for an Either a monad, is actually Right , we thus wrap the content of fxy in the Right data constructor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM