代码之家  ›  专栏  ›  技术社区  ›  stusmith

Haskell:为什么Maybe和这两种类型在用作单子时表现不同?

  •  13
  • stusmith  · 技术社区  · 14 年前

    我在想Haskell的错误处理方法。我找到了那篇文章” 8 ways to report errors in Haskell “但我不明白为什么可能和其中任何一个表现不同。

    例如:

    import Control.Monad.Error
    
    myDiv :: (Monad m) => Float -> Float -> m Float
    myDiv x 0 = fail "My divison by zero"
    myDiv x y = return (x / y)
    
    testMyDiv1 :: Float -> Float -> String
    testMyDiv1 x y =
        case myDiv x y of
            Left e  -> e
            Right r -> show r
    
    testMyDiv2 :: Float -> Float -> String
    testMyDiv2 x y =
        case myDiv x y of
            Nothing -> "An error"
            Just r  -> show r
    

    打电话 testMyDiv2 1 0 "An error" ,但是打电话 testMyDiv1 1 0 给予:

    "*** Exception: My divison by zero
    

    (注意没有右引号,表示这不是一个字符串,而是一个例外)。

    2 回复  |  直到 4 年前
        1
  •  15
  •   mokus    14 年前

    简而言之,Haskell中的Monad类添加了 fail 对单子的原始数学思想的运算,这使得如何将这两种类型转换成a(Haskell)有些争议 Monad ,因为有很多方法。

    • fail = Left . 这似乎是大多数人所期待的,但实际上不能在严格的Haskell 98中实现。实例必须声明为 instance Monad (Either String) ,这在H98下是不合法的,因为它提到了一个特定类型的 Either s参数(在GHC中,FlexibleInstances扩展将导致编译器接受它)。
    • 忽略 失败 error
    • 失败 实现调用其他类将字符串转换为任何类型。这是在MTL中完成的 Control.Monad.Error instance Error e => Monad (Either e) . 在这个实施过程中, fail msg = Left (strMsg msg) . 这一个同样是合法的H98,而且偶尔也会让用户感到惊讶,因为它引入了另一个类型类。不过,与上一个示例不同的是,惊喜出现在编译时。
        2
  •  4
  •   Thomas M. DuBuisson    14 年前

    monads-fd .

    $ ghci t.hs -hide-package mtl
    *Main Data.List> testMyDiv1 1 0
    "*** Exception: My divison by zero
    *Main Data.List> :i Either
    ...
    instance Monad (Either e) -- Defined in Control.Monad.Trans.Error
    ...
    

    transformers 包裹,在哪里 单子fd

    instance Monad (Either e) where
        return        = Right
        Left  l >>= _ = Left l
        Right r >>= k = k r
    

    所以,没有失败的定义。一般来说, fail 是令人沮丧的,因为它并不总是保证失败干净的单子(许多人希望看到) 失败 从Monad类中删除)。

    编辑:我应该补充一点,这肯定不清楚 失败 打算作为默认值保留 error

    编辑2:The mtl 实例已被删除 moved to base fail = Left 失败 Just x <- e 哪里 e -->* m Nothing