Monad 结果类型在 `fail` 时不产生 `Either String`
Monad result type does not produce `Either String` on `fail`
给定以下函数,它产生一个包装到 Monad 中的结果:
ab :: (Monad m) => Char -> m Bool
ab 'a' = return True
ab 'b' = return False
ab _ = fail "say what?"
以下用途如我所料:
ab 'a' :: [Bool] -- results in [True]
ab 'c' :: [Bool] -- results in []
ab 'b' :: Maybe Bool -- results in Just b
ab 'c' :: Maybe Bool -- results in Nothing
ab 'a' :: Either String Bool -- results in Right True
但是,对于 Either String
,fail
实际上会产生异常,尽管我希望它是 Left
并带有错误消息:
> ab 'c' :: Either String Bool
*** Exception: say what?
这是为什么?有没有办法改变上面的代码(函数实现,或者它的应用方式)以便在失败的情况下产生 Left
(当然保持它的通用性)。
过去,"standard" 库中没有 Either
的 Monad
实例。相反,这个实例是 defined in the mtl library. However, to make fail
work as you desire we need a way to convert a string into whatever the type e
is in Either e
. This led to the Error
class. Back in the day, the Monad
instance in mtl had Error
as a constraint on the Either
instance of Monad
. This was solely to make fail
Do The Right Thing, and most people thought of fail
as a bit of a wart on the Monad
class. Unfortunately, it meant that you couldn't use any Monad
operations on Either
except when your "error" type was an instance of Error
which wasn't typical or usually desirable. The orphan instance for Either
also caused much trouble when a rival monad library, transformers,出现了。不过,最终,无论 "error" 类型如何,在 Either
上使用 monad 操作的有效性以及将 Monad
实例移动到 base 中占了上风,导致约束被删除。但是,现在没有可以为 fail
.
定义的合理的东西
Why is it?
记住fail
的类型:Monad m => String -> m a
。现在,如果 Either
的 monad 实例仅为 Either String
定义,这将很容易:
instance Monad (Either String) where
fail = Left
...
不过,实际实例比较笼统:
instance Monad (Either e) where
...
因此fail
的类型也更通用,即使我们将它限制在这个特定的实例中:
-- No v restriction on e v
fail :: forall e a. String -> Either e a
-- ^^^^^^^^^^
-- This is implicitly there every time you use a polymorphic function
-- (unless you start toying around with some extensions and move it further
-- to the right or into parentheses, see RankNTypes or similar extensions.)
并且由于 e
不限于 String
,因此没有通用的方法将错误消息存储在 Left e
中。例如,下面的例子应该怎样 return?
example :: Either () ()
example = fail "Example"
即使您使用 Either String a
,更通用的实例仍在使用。另一种方法是使用新类型或您自己的具有合适实例的 ADT:
data EitherString a = ELeft String | ERight a
instance Monad EitherString where
fail = ELeft
...
newtype EitherWrap a = Wrapped { unWrap :: Either String a }
instance Monad EitherWrap where
fail = Wrapped . Left
请注意,a proposal 将 Monad
类型类中的 fail
拆分为 MonadFail
类型类。
因为Either
是析取,而不仅仅是success/error。常用于表示success/error(因为方便),但一般情况下不是
这就是数据构造函数被命名为 Left
和 Right
的原因。他们在代表价值的权利上是平等的。
例如,我有 Either Computation Result
或类似的东西是完全可以的。 Either Gold Money
,等等
如果 fail
最终返回析取的分支之一,则可能不正确。
似乎可以使用 Data.String(IsString)
class,即用于 OverloadedStrings
扩展的那个。
instance IsString a => MonadFail (Either a ) where
fail msg = Left $ fromString msg
但我想它不在标准库中是有原因的
给定以下函数,它产生一个包装到 Monad 中的结果:
ab :: (Monad m) => Char -> m Bool
ab 'a' = return True
ab 'b' = return False
ab _ = fail "say what?"
以下用途如我所料:
ab 'a' :: [Bool] -- results in [True]
ab 'c' :: [Bool] -- results in []
ab 'b' :: Maybe Bool -- results in Just b
ab 'c' :: Maybe Bool -- results in Nothing
ab 'a' :: Either String Bool -- results in Right True
但是,对于 Either String
,fail
实际上会产生异常,尽管我希望它是 Left
并带有错误消息:
> ab 'c' :: Either String Bool
*** Exception: say what?
这是为什么?有没有办法改变上面的代码(函数实现,或者它的应用方式)以便在失败的情况下产生 Left
(当然保持它的通用性)。
过去,"standard" 库中没有 Either
的 Monad
实例。相反,这个实例是 defined in the mtl library. However, to make fail
work as you desire we need a way to convert a string into whatever the type e
is in Either e
. This led to the Error
class. Back in the day, the Monad
instance in mtl had Error
as a constraint on the Either
instance of Monad
. This was solely to make fail
Do The Right Thing, and most people thought of fail
as a bit of a wart on the Monad
class. Unfortunately, it meant that you couldn't use any Monad
operations on Either
except when your "error" type was an instance of Error
which wasn't typical or usually desirable. The orphan instance for Either
also caused much trouble when a rival monad library, transformers,出现了。不过,最终,无论 "error" 类型如何,在 Either
上使用 monad 操作的有效性以及将 Monad
实例移动到 base 中占了上风,导致约束被删除。但是,现在没有可以为 fail
.
Why is it?
记住fail
的类型:Monad m => String -> m a
。现在,如果 Either
的 monad 实例仅为 Either String
定义,这将很容易:
instance Monad (Either String) where
fail = Left
...
不过,实际实例比较笼统:
instance Monad (Either e) where
...
因此fail
的类型也更通用,即使我们将它限制在这个特定的实例中:
-- No v restriction on e v
fail :: forall e a. String -> Either e a
-- ^^^^^^^^^^
-- This is implicitly there every time you use a polymorphic function
-- (unless you start toying around with some extensions and move it further
-- to the right or into parentheses, see RankNTypes or similar extensions.)
并且由于 e
不限于 String
,因此没有通用的方法将错误消息存储在 Left e
中。例如,下面的例子应该怎样 return?
example :: Either () ()
example = fail "Example"
即使您使用 Either String a
,更通用的实例仍在使用。另一种方法是使用新类型或您自己的具有合适实例的 ADT:
data EitherString a = ELeft String | ERight a
instance Monad EitherString where
fail = ELeft
...
newtype EitherWrap a = Wrapped { unWrap :: Either String a }
instance Monad EitherWrap where
fail = Wrapped . Left
请注意,a proposal 将 Monad
类型类中的 fail
拆分为 MonadFail
类型类。
因为Either
是析取,而不仅仅是success/error。常用于表示success/error(因为方便),但一般情况下不是
这就是数据构造函数被命名为 Left
和 Right
的原因。他们在代表价值的权利上是平等的。
例如,我有 Either Computation Result
或类似的东西是完全可以的。 Either Gold Money
,等等
如果 fail
最终返回析取的分支之一,则可能不正确。
似乎可以使用 Data.String(IsString)
class,即用于 OverloadedStrings
扩展的那个。
instance IsString a => MonadFail (Either a ) where
fail msg = Left $ fromString msg
但我想它不在标准库中是有原因的