Haskell 有可能改变一元序列中的一元类型吗?

Haskell 有可能改变一元序列中的一元类型吗?,haskell,monads,either,Haskell,Monads,Either,我知道可以更改包装类型,这样您就可以 f :: (a -> m b) g :: (b -> m c) f >>= g :: (a -> m c) 但是是否可以更改m?如果m是一个MonadError,并且由任一错误a和任一错误b实现,我可以以某种方式链接它们吗?显然,我不能直接链接它们,因为左的类型是什么?然而,在这两种情况下,我都会调用show,但我没有找到比这更好的解决方案 case mightFail1 of Left e -> show e

我知道可以更改包装类型,这样您就可以

f :: (a -> m b)
g :: (b -> m c)
f >>= g :: (a -> m c)
但是是否可以更改
m
?如果
m
是一个
MonadError
,并且由
任一错误a
任一错误b
实现,我可以以某种方式链接它们吗?显然,我不能直接链接它们,因为左
的类型是什么?然而,在这两种情况下,我都会调用
show
,但我没有找到比这更好的解决方案

case mightFail1 of
  Left e -> show e
  Right v -> either show doStuff mightFail2

它无法正确使用一元行为,即在第一个错误时停止,而无需我进行明确检查。

StackOverflow是一种优秀的橡皮鸭。我找到了一个解决方案,但我仍然好奇是否还有其他方法。既然我已经写完了这个问题,我还是要把它贴出来

不要考虑“在链期间更改monad类型”,只需在链接之前转换所有值,使它们返回
字符串a

f :: (Show a) => (Either a b) -> (Either String b)
f = either (Left . show) (Right)
这会将整个调用包装为(
f mightFail1
),可能有一个可组合的变量(
f.mightFail1


疯狂模式:将任意一个包装成一个新类型,使其成为functor的一个实例,在左侧而不是右侧映射函数,只需调用fmap show mightFail1(别忘了包装和打开您的新类型)。这有意义吗在一元链中不可能做到这一点

请注意,这与
Monad
一点关系都没有:在
Left
参数或类似的东西中,您没有以任何方式绑定嵌套的monadic操作,但您只是转换参数本身。它基本上是函子操作
fmap
,但在左边而不是右边:

fmap     :: (r->ρ) -> Either l r -> Either l ρ
fmapLeft :: (l->λ) -> Either l r -> Either λ r
令人惊讶的是,一个具有特定签名的函数。然而,具有两个协变参数的函子的概念显然比
更为普遍,事实上。它有(IMO相当不幸的命名,与之冲突)

事实上,它专门研究

first :: (a -> b) -> Either a c -> Either b c
所以你可以用

f :: (Show a) => (Either a b) -> (Either String b)
f = first show
“改变容器”的整个概念被称为“自然转换”。具体来说,我们需要一个函数,它可以在不影响容器内部内容的情况下转换容器。我们可以通过使用
forall
来确保类型系统中的情况

-- natural transformation
type (m :~> n) = forall a. m a -> n a
然后,我们可以随时应用这些方法。例如,如果您可以转换
ErrorA->ErrorB
,则有一个通用操作

mapE :: (e -> e') -> (Either e :~> Either e')
mapE f (Left e)  = Left (f e)
mapE _ (Right a) = a
您甚至可以使用类型运算符和求和类型

-- a generic sum type
infixr 9 :+:
newtype (a :+: b) = Inl a | Inr b

liftE :: (Either e :~> Either (e' :+: e))
liftE = mapE Inr

双弯折器实现的效果大致相同,但它们以完全不同的方式看待问题。它们通常不会改变容器,而是影响容器本身中的另一个协变参数(或索引)。因此,Bifunctor操作始终可以被视为自然转换,但NTs更为通用。

对于这种特定情况,您可以使用my
errors
库中的
fmapL

fmapL :: (a -> b) -> Either a r -> Either b r
由于您说过您最终将
show
这两个字符串,因此您可以使用
fmapL show
来统一这两个字符串,以便在
任一字符串上达成一致,并直接对它们进行排序:

do v <- fmapL show mightFail1
   fmapL show $ mightFail2 v

双刃剑!有趣。在您的回答之前,我最终实现了
mapLeft
,现在只是
第一个
。正如J.Abrahamson的回答所说,这是两个单子之间的自然转换,也称为单子态射。有一些方案支持这一观点和类似观点;Tekmo说你可以尝试阅读。但是,总的来说,我认为两个单子之间的态射可能需要有一些访问每个单子的“内脏”的途径才能在它们之间进行转换。
do v <- fmapL show mightFail1
   fmapL show $ mightFail2 v
example :: Either (Either Error1 Error2) ()
example = do
    v <- fmapL Left mightFail1
    fmapL Right $ mightFail2 v