【问题标题】:Using 'Either' in Haskell在 Haskell 中使用“任意”
【发布时间】:2011-09-11 13:17:19
【问题描述】:

我有两个值,t1t2,类型为 Either String TypeLeft-value 用于错误处理。这些值用于返回 Either String Type 的函数中。

我要做的是检查t1t2 是否都是Right-values 并满足p :: Type -> Bool。如果他们这样做,我想返回Right (the type inside t1)。如果t1t2都是Right-values,但不满足p,我想返回Left someString。如果t1t2 之一是Left 值,我只想传递该值。

我怎样才能优雅地做到这一点?我有一种预感,使用 Either 作为 monad 是正确的做法,但我不确定如何去做。

【问题讨论】:

    标签: haskell monads either


    【解决方案1】:

    如果你真的想的话,你可以从Left 值的传播中分离出一元动作:

    import Control.Monad
    import Control.Applicative
    import Control.Monad.Instances
    

    这产生了简单的一元动作:

    foo :: Type -> Type -> Either String Type
    foo t1 t2 | p t1 && p t2 = Right t1
              | otherwise    = Left somestring
    

    您可以将其应用于单子参数以获得所需的函数,使用

    fooM :: Either String Type -> Either String Type -> Either String Type
    fooM t1 t2 = join (foo <$> t1 <*> t2)
    

    或等效

    fooM t1 t2 = do
        a <- t1
        b <- t2
        foo a b
    

    【讨论】:

      【解决方案2】:

      您可以创建自己的 Error 数据类型并使其成为 Monad 的实例。

      data Computation a = Error String | Result a
      
      
      instance Monad Computation where
          (Result x)  >>= k   =  k x
        e@(Error a)   >>= k   =  e
      

      然后使用method described by Ganesh Sittampalam。 (您还需要添加一个实例 MonadPlus Computation。

      更新为了完整性,它看起来像这样:

      import Control.Monad
      
      data Computation a = Error String | Result a
      
      
      
      instance Monad Computation where
        return a = Result a
        (Result x)  >>= k   =  k x
        (Error a)   >>= k   =  Error a
      
      instance MonadPlus Computation where
        mzero              = Error "Always fail"
        mplus (Error a) r  = r
        mplus l         _  = l
      
      
      check :: (Int -> Bool) -> Computation Int  
      check p =   do v1 <- Result 4
                     v2 <- Result 2
                     guard (p v1 && p v2) `mplus` Error "someString"
                     return v1
      

      【讨论】:

      • 您对mplus 的定义并没有达到您想要的效果。
      • @Tsuyoshi Ito,我修改了代码,以便 mplus 显示正确的行为。
      【解决方案3】:

      如果您确实想使用 Monad 来执行此操作,它看起来像这样,但最近更改了 EitherMonad 实例,因此这实际上在最近的 GHC 中不起作用:

      do v1 <- t1
         v2 <- t2
         guard (p v1 && p v2) `mplus` Left someString
         return v1
      

      【讨论】:

      • 如果我从 mtl 包导入 Control.Monad.Error,它确实适用于 GHC 7.0.3。也可以通过将Left 替换为throwError . strMsg 来完成对Either 的抽象。并不是说它非常有用。
      • 您能否提供有关更改的更多详细信息,特别是更改的内容和时间?
      • mightybyte:我知道的唯一变化是“失败”不再映射到“左”。不过,我不确定这对上述示例有何影响。
      • @Antoine Latter: guard False = mzero,但Either a 不是MonadPlus 的实例,因为该实例在Control.Monad.Instances 中定义,并且无法从Error 类中访问Control.Monad.Trans.Error 不在transformers 中。 mtl 然后通过Control.Monad.Error 重新导出实例。
      • @mightybyte:我们将Either 的实例从transformers(以及在此之前的mtl)移动到base,但因为该实例同时涉及一个类和 i> Prelude 中的一个类型并且没有在任何当前的 Haskell 标准中说明,我们必须从 Control.Monad.Instances 导出它,而不是把它放到任何地方的范围内。在此过程中,我们将Error 的假设从左侧参数中删除为Either。这具有将fail 的行为从Left . strMsg 更改为error 的副作用。我们在transformers 中留下了MonadPlusAlternative
      【解决方案4】:

      为什么是单子?

      test p (Right t1) (Right t2) | p t1 && p t2 = Right t1
                                   | otherwise = Left "nope"
      test _ (Left t1) _ = Left t1
      test _ _ (Left t2) = Left t2
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多