2013-08-05 25 views
5

我知道这是可以改变包装类型,这样就可以有是否可以改变monadic序列中的monad类型?

f :: (a -> m b) 
g :: (b -> m c) 
f >>= g :: (a -> m c) 

但有可能改变m?如果mMonadError并且由Either ErrorAEither ErrorB实现,我可以以某种方式链接它们吗?显然我不能直接链接它们,因为Left的类型是什么?不过,我在我结束了在这两种情况下调用show的情况,但我还没有发现比

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

一个更好的解决方案,它不能正确使用在第一个错误停止的单子行为,而不我必须明确检查。

+1

正如J. Abrahamson的回答所说,这是两个单子之间的自然转换,也称为**单体态射**。有几个软件包来支持这个和类似的概念; Tekmo的[mmorph'包](http://www.haskellforall.com/2013/03/mmorph-100-monad-morphisms.html)上有一篇博客文章,您可以尝试阅读。然而,总的来说,我认为两个monad之间的态度可能需要有一些访问每个monad的“胆量”来在它们之间进行转换。 –

回答

6

在单子链中不可能做到这一点。

请注意,这不是真的关心Monad都:你以任何方式结合在Left说法或类似的东西嵌套单子动作不是,但你只能转换参数本身。它基本上是仿函数操作fmap,但对左不是右部分:

fmap  :: (r->ρ) -> Either l r -> Either l ρ 
fmapLeft :: (l->λ) -> Either l r -> Either λ r 

与特定签名的函数,奇怪的是,doesn't seem to exist。然而,这种带有两个协变论元的函数的想法显然比Either更实际,实际上是there's a dedicated class。它具有(IMO相当不幸的命名,上有冲突Arrow

Data.Bifunctor.first :: (a -> b) -> p a c -> p b c 

专门从事事实

first :: (a -> b) -> Either a c -> Either b c 

所以,你可以使用

f :: (Show a) => (Either a b) -> (Either String b) 
f = first show 
+0

bifunctors!有趣。我最终在响应之前实现了'mapLeft',现在只是'第一'。 – BruceBerry

1

StackOverflow是一个优秀的橡皮鸭。我发现a解决方案,但我仍然好奇,如果有另一种方法。由于我已经完成了这个问题的写作,所以我会发布它。

而不是“链过程中改变单子类型”,简单地变换所有值它们是链接之前,使它们返回Either String a思维:

f :: (Show a) => (Either a b) -> (Either String b) 
f = either (Left . show) (Right) 

这包装整个呼叫或者(f mightFail1) ,可能会有一个可组合的变体(f . mightFail1

疯狂模式:将其包装成新类型,使其成为函数的实例,将函数映射到左侧而不是右侧,然后调用fmap show mightFail1(不要忘记包装和解开你的新类型)。那有意义吗? :D

10

的“改变容器的整个概念“被称为”自然变革“。具体而言,我们需要一个转换容器的功能,而不会影响内部的功能。我们可以通过使用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 

Bifunctors实现大致相同的效果,但他们是一个完全不同的方式来查看问题。它们通常不会改变容器,而是会影响容器本身的另一个协变参数(或索引)。所以,Bifunctor的行为总是可以被看作是自然的转变,但NT更普遍。

3

对于您可以使用fmapL从我errors库这个特定的情况下:

fmapL :: (a -> b) -> Either a r -> Either b r 

既然你说你最终将show他们两个,你可以使用fmapL show来统一他们两人商定在Either String单子,直接测序他们:

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

现在你可以两者都使用做记号序列,并让他们共享同一个错误公顷ndling机制。

请注意show不是统一左值的唯一方法。你也可以统一不显示值... Either

example :: Either (Either Error1 Error2)() 
example = do 
    v <- fmapL Left mightFail1 
    fmapL Right $ mightFail2 v 
相关问题