【问题标题】:Haskell polymorphic function Using Either Left RightHaskell 多态函数使用 Either Left Right
【发布时间】:2018-10-09 00:59:51
【问题描述】:

我是 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    | 

【问题讨论】:

标签: haskell either polymorphic-functions


【解决方案1】:

问题是您将EvalResult 类型定义为:

type EvalResult = Either EvalError Value

因为Either 是一个type 构造函数,它接受两种类型,这意味着你现在已经构造了一个类型(没有任何类型参数)。如果你写了一个带有(a -> b -> c) -> EvalResult a -> EvalResult b -> EvalResult c类型签名的函数,那么Haskell最终将不得不构造类型EvalResult a ~ Either EvalError Value a,因为Either只接受两个类型参数,这没有意义。

我猜你想定义

type EvalResult <b>a</b> = Either EvalError <b>a</b>

或更短:

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 =&gt; (a -&gt; b -&gt; c) -&gt; m a -&gt; m b -&gt; 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 是否为Right 来评估mx &gt;&gt;= (...),如果它是Left,我们返回Left 的内容。由于我们已经用两个Lefts 处理了这种情况,这种情况不再可能,所以我们知道第二个EvalResultRight,在这种情况下,我们因此返回第一个Left。如果mxRight x,我们现在检查my &gt;&gt;= (..) 并检查my 的状态。如果myLeft,我们再次返回Left,否则,我们返回return (f x y),因为returnEither a monad,实际上是Right,因此我们包装了f x yRight 数据构造函数中。

【讨论】:

  • 谢谢。我很难理解在您的实现中使用liftM2。你有没有机会说如果不使用liftM2,你将如何实现它?
  • @sheldonzy 与您在尝试中所做的相同。 liftM2 恰好为您实现了最后 3 个案例。
猜你喜欢
  • 1970-01-01
  • 2021-01-18
  • 2021-11-12
  • 2017-03-25
  • 2014-08-05
  • 2019-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多