Полиморфная функция 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 конструктор данных.

Другие вопросы по тегам