Полиморфная функция Haskell, использующая либо лево-право
Я новичок в Хаскеле. У меня есть типы:
type Variable = String
type Value = Float
type EvalError = [Variable]
type EvalResult = Either EvalError Value
И я хочу создать функцию, которую я буду использовать функцию, чтобы использовать его на 2 EvalResult
типы, и получить EvalResult
соответственно. Если я получу 2 типа значений, я хочу использовать для них функцию (например, sum/sub), а если я получу EvalError, я хочу вернуть EvalError.
Что я сделал:
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)
Ошибка:
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 |
1 ответ
Проблема в том, что вы определяете свой EvalResult
введите как:
type EvalResult = Either EvalError Value
поскольку Either
является конструктором типов, который принимает два типа, это означает, что вы уже создали тип (без каких-либо параметров типа). Если вы пишете функцию с подписью типа (a -> b -> c) -> EvalResult a -> EvalResult b -> EvalResult c
, тогда Haskell, таким образом, в конечном итоге придется конструировать типы EvalResult a ~ Either EvalError Value a
, и с тех пор Either
принимает только два типа параметров, это не имеет смысла.
Я думаю, что вы хотите определить
type EvalResult a = Either EvalError a
или короче:
type EvalResult = Either EvalError
Сейчас EvakResult
таким образом действует как конструктор типов, который может принимать один параметр типа, и тогда функция действительно работает над типами.
Мы можем сделать реализацию более компактной, написав:
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) -> m a -> m b -> m c
это функция, которая работает над монадическими типами m
, Either a
является монадическим типом, он определяется как:
instance Monad (Either a) where return = Right (>>=) (Right x) f = f x (>>=) (Left l) _ = Left l
liftM2
реализован как:
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c liftM2 f xm ym = do x <- mx y <- my return (f x y)
который является синтаксическим сахаром для:
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
liftM2 f xm ym = mx >>= (\x -> my >>= \y -> return (f x y))
Так что в основном мы оцениваем mx >>= (...)
проверяя, если mx
это Right
если это Left
мы возвращаем содержимое Left
, Поскольку мы уже рассмотрели дело с двумя Left
с, этот случай больше не возможен, поэтому мы знаем, что второй EvalResult
это Right
в таком случае мы возвращаем первое Left
, В случае mx
это Right x
мы сейчас проверяем my >>= (..)
и проверить состояние my
, Если my
это Left
мы опять вернем что Left
в противном случае мы возвращаем return (f x y)
, поскольку return
для Either a
монада, на самом деле Right
таким образом, мы обертываем содержимое f x y
в Right
конструктор данных.