我们可以为 WrappedArrow 定义一个 Monad 实例吗?
Can we define a Monad instance for WrappedArrow?
WrappedArrow
是 Applicative
的一个实例,但它可以做成 Monad
(可能如果箭头是 ArrowApply
)吗?
instance ArrowApply a => Monad (WrappedArrow a b) where
return = pure
(>>) = (*>)
(>>=) = ???
编辑:
我最初的目的是为(包装的)Kleisli 提供一个 Monad 实例,这样我就可以编写
runWithInput input $ do
action1
action2
action3
...
而不是
do
output1 <- action1 input
output2 <- action2 output1
output3 <- action3 output2
...
但我意识到这没有什么意义。除去新类型,Kleisli f a b >> Kleisli f a c
是
(a -> f b) -> (a -> f c) -> (a -> f c)
我需要的是 >>>
的模拟,即 b 而不是第二个 a
:
(a -> f b) -> (b -> f c) -> (a -> f c)
所以我想如果我想用 do
以这种方式对动作进行排序,我将不得不使用 StateT
或自定义 monad。
由于这是一个 XY 问题,我将回答您提出的问题以及您可能想问的问题:
WrappedArrow
is an instance of Applicative
, but can it be made a Monad
(probably if the arrow is ArrowApply
)?
是的,可以,但您需要更多限制。事实上,我认为有几种方法可以做到这一点。例如,您可以采用@user2407038 建议的方法:
class Arrow a => ArrowSwap a where
swap :: a b (a c d) -> a c (a b d)
instance (ArrowApply a, ArrowSwap a) => Monad (WrappedArrow a b) where
return = pure
WrapArrow m >>= f = WrapArrow $ swap (arr (unwrapArrow . f)) &&& m >>> app
您可以通过 (->)
:
的实例获得 ArrowSwap
的一些直觉
instance ArrowSwap (->) where
swap :: (b -> (c -> d)) -> c -> b -> d
swap = flip
当然,目前还不清楚这是否有用...
My initial purpose was to have a Monad instance for (wrapped) Kleisli,
so I could write
runWithInput input $ do
action1
action2
action3
...
instead of
do
output1 <- action1 input
output2 <- action2 output1
output3 <- action3 output2
...
这就是 RebindableSyntax
的用途:
{-# LANGUAGE RebindableSyntax #-}
import Control.Monad (>=>)
action1 :: Monad m => T1 -> m T2
action2 :: Monad m => T2 -> m T3
action3 :: Monad m => T3 -> m T4
action4 :: Monad m => T1 -> m T4
action4 = let (>>) = (>=>) in do
action1
action2
action3
WrappedArrow
is an instance of Applicative
, but can it be made a Monad
(probably if the arrow is ArrowApply
)?
我将 WrappedArrow
暂时搁置一旁,考虑一下略有不同的问题 "Can we successfully implement instance ArrowApply y => Monad (y r)
?"。这个问题的答案是"yes"。一种证明它的方法依赖于ArrowMonad
newtype chi mentions...
newtype ArrowMonad a b = ArrowMonad (a () b)
...以及下面的同构(cf.this cstheory.SE question and page 18 of Idioms are oblivious, arrows are meticulous, monads are promiscuous):
kleislify :: ArrowApply y => y r a -> (r -> y () a)
kleislify af = \r -> arr (const r) >>> af
unkleislify :: ArrowApply y => (r -> y () a) -> y r a
unkleislify f = arr f &&& arr (const ()) >>> app
-- unkleislify . kleislify = kleislify . unkleislify = id
ArrowMonad
给了我们一个 monad 实例,我们可以通过 kleislify
-ing 箭头并为结果函数提供一个公共参数来使用它(换句话说,我们正在使用 ArrowMonad
通过函数的应用实例绑定):
bindY :: ArrowApply y => y r a -> (a -> y r b) -> y r b
bindY af h = unkleislify $ (\(ArrowMonad am) -> am) . (\r ->
ArrowMonad (kleislify af r) >>= \x -> ArrowMonad (kleislify (h x) r))
相关的 return
也是 ArrowMonad
一个,包含在适当的样板层中:
returnY :: ArrowApply y => a -> y r a
returnY x = unkleislify $ \r -> (\(ArrowMonad am) -> am) (return x)
但是,这并没有回答您的问题。为此,bindY
和 returnY
应该与 WrappedArrow
的 Applicative
实例一致;也就是说,我们应该有 returnY x = arr (const x)
,而我们可以用 bindY
和 returnY
写的 ap
应该等同于 (<*>)
for WrappedMonad
。例如,我们可能会尝试使用相关 ArrowMonad
实例的定义...
instance Arrow a => Applicative (ArrowMonad a) where
pure x = ArrowMonad (arr (const x))
ArrowMonad f <*> ArrowMonad x = ArrowMonad (f &&& x >>> arr (uncurry id))
instance ArrowApply a => Monad (ArrowMonad a) where
ArrowMonad m >>= f = ArrowMonad $
m >>> arr (\x -> let ArrowMonad h = f x in (h, ())) >>> app
...扩展(然后希望简化)returnY
:
returnY
unkleislify $ \r -> (\(ArrowMonad am) -> am) (return x)
unkleislify $ \r -> (\(ArrowMonad am) -> am) (ArrowMonad (arr (const x)))
unkleislify $ \r -> arr (const x)
arr (\r -> arr (const x)) &&& arr (const ()) >>> app
arr (const (arr (const x))) &&& arr (const ()) >>> app
arr (\r -> (r, r)) >>> arr (const (arr (const x))) *** arr (const ()) >>> app
arr (\r -> (arr (const x), ())) >>> app
我不知道是否可以将任何 ArrowApply
简化为 arr (const x)
。也许能够翻转箭头(如 Alec 和 user2407038 所建议的那样)将有助于摆脱 ()
,但我还没有解决这个问题。无论如何,至少Kleisli
我们可以继续:
arr (\r -> (arr (const x), ())) >>> app
Kleisli (\r -> return (arr (const x), ())) >>> Kleisli (\(Kleisli f, x) -> f x)
Kleisli ((\r -> return (arr (const x), ())) >=> (\(Kleisli f, x) -> f x))
Kleisli ((\r -> return (Kleisli (return . const x), ()))
>=> (\(Kleisli f, x) -> f x))
Kleisli (\r -> return (Kleisli (return . const x), ())
>>= (\(Kleisli f, x) -> f x))
Kleisli (\r -> (\(Kleisli f, x) -> f x) (Kleisli (return . const x), ()))
Kleisli (\r -> (return . const x) ())
Kleisli (\r -> return x)
Kleisli (return . const x)
arr (const x)
我没有尝试对 bindY
做同样的事情,但我不知情的猜测是会产生类似的情况。
WrappedArrow
是 Applicative
的一个实例,但它可以做成 Monad
(可能如果箭头是 ArrowApply
)吗?
instance ArrowApply a => Monad (WrappedArrow a b) where
return = pure
(>>) = (*>)
(>>=) = ???
编辑:
我最初的目的是为(包装的)Kleisli 提供一个 Monad 实例,这样我就可以编写
runWithInput input $ do
action1
action2
action3
...
而不是
do
output1 <- action1 input
output2 <- action2 output1
output3 <- action3 output2
...
但我意识到这没有什么意义。除去新类型,Kleisli f a b >> Kleisli f a c
是
(a -> f b) -> (a -> f c) -> (a -> f c)
我需要的是 >>>
的模拟,即 b 而不是第二个 a
:
(a -> f b) -> (b -> f c) -> (a -> f c)
所以我想如果我想用 do
以这种方式对动作进行排序,我将不得不使用 StateT
或自定义 monad。
由于这是一个 XY 问题,我将回答您提出的问题以及您可能想问的问题:
WrappedArrow
is an instance ofApplicative
, but can it be made aMonad
(probably if the arrow isArrowApply
)?
是的,可以,但您需要更多限制。事实上,我认为有几种方法可以做到这一点。例如,您可以采用@user2407038 建议的方法:
class Arrow a => ArrowSwap a where
swap :: a b (a c d) -> a c (a b d)
instance (ArrowApply a, ArrowSwap a) => Monad (WrappedArrow a b) where
return = pure
WrapArrow m >>= f = WrapArrow $ swap (arr (unwrapArrow . f)) &&& m >>> app
您可以通过 (->)
:
ArrowSwap
的一些直觉
instance ArrowSwap (->) where
swap :: (b -> (c -> d)) -> c -> b -> d
swap = flip
当然,目前还不清楚这是否有用...
My initial purpose was to have a Monad instance for (wrapped) Kleisli, so I could write
runWithInput input $ do action1 action2 action3 ...
instead of
do output1 <- action1 input output2 <- action2 output1 output3 <- action3 output2 ...
这就是 RebindableSyntax
的用途:
{-# LANGUAGE RebindableSyntax #-}
import Control.Monad (>=>)
action1 :: Monad m => T1 -> m T2
action2 :: Monad m => T2 -> m T3
action3 :: Monad m => T3 -> m T4
action4 :: Monad m => T1 -> m T4
action4 = let (>>) = (>=>) in do
action1
action2
action3
WrappedArrow
is an instance ofApplicative
, but can it be made aMonad
(probably if the arrow isArrowApply
)?
我将 WrappedArrow
暂时搁置一旁,考虑一下略有不同的问题 "Can we successfully implement instance ArrowApply y => Monad (y r)
?"。这个问题的答案是"yes"。一种证明它的方法依赖于ArrowMonad
newtype chi mentions...
newtype ArrowMonad a b = ArrowMonad (a () b)
...以及下面的同构(cf.this cstheory.SE question and page 18 of Idioms are oblivious, arrows are meticulous, monads are promiscuous):
kleislify :: ArrowApply y => y r a -> (r -> y () a)
kleislify af = \r -> arr (const r) >>> af
unkleislify :: ArrowApply y => (r -> y () a) -> y r a
unkleislify f = arr f &&& arr (const ()) >>> app
-- unkleislify . kleislify = kleislify . unkleislify = id
ArrowMonad
给了我们一个 monad 实例,我们可以通过 kleislify
-ing 箭头并为结果函数提供一个公共参数来使用它(换句话说,我们正在使用 ArrowMonad
通过函数的应用实例绑定):
bindY :: ArrowApply y => y r a -> (a -> y r b) -> y r b
bindY af h = unkleislify $ (\(ArrowMonad am) -> am) . (\r ->
ArrowMonad (kleislify af r) >>= \x -> ArrowMonad (kleislify (h x) r))
相关的 return
也是 ArrowMonad
一个,包含在适当的样板层中:
returnY :: ArrowApply y => a -> y r a
returnY x = unkleislify $ \r -> (\(ArrowMonad am) -> am) (return x)
但是,这并没有回答您的问题。为此,bindY
和 returnY
应该与 WrappedArrow
的 Applicative
实例一致;也就是说,我们应该有 returnY x = arr (const x)
,而我们可以用 bindY
和 returnY
写的 ap
应该等同于 (<*>)
for WrappedMonad
。例如,我们可能会尝试使用相关 ArrowMonad
实例的定义...
instance Arrow a => Applicative (ArrowMonad a) where
pure x = ArrowMonad (arr (const x))
ArrowMonad f <*> ArrowMonad x = ArrowMonad (f &&& x >>> arr (uncurry id))
instance ArrowApply a => Monad (ArrowMonad a) where
ArrowMonad m >>= f = ArrowMonad $
m >>> arr (\x -> let ArrowMonad h = f x in (h, ())) >>> app
...扩展(然后希望简化)returnY
:
returnY
unkleislify $ \r -> (\(ArrowMonad am) -> am) (return x)
unkleislify $ \r -> (\(ArrowMonad am) -> am) (ArrowMonad (arr (const x)))
unkleislify $ \r -> arr (const x)
arr (\r -> arr (const x)) &&& arr (const ()) >>> app
arr (const (arr (const x))) &&& arr (const ()) >>> app
arr (\r -> (r, r)) >>> arr (const (arr (const x))) *** arr (const ()) >>> app
arr (\r -> (arr (const x), ())) >>> app
我不知道是否可以将任何 ArrowApply
简化为 arr (const x)
。也许能够翻转箭头(如 Alec 和 user2407038 所建议的那样)将有助于摆脱 ()
,但我还没有解决这个问题。无论如何,至少Kleisli
我们可以继续:
arr (\r -> (arr (const x), ())) >>> app
Kleisli (\r -> return (arr (const x), ())) >>> Kleisli (\(Kleisli f, x) -> f x)
Kleisli ((\r -> return (arr (const x), ())) >=> (\(Kleisli f, x) -> f x))
Kleisli ((\r -> return (Kleisli (return . const x), ()))
>=> (\(Kleisli f, x) -> f x))
Kleisli (\r -> return (Kleisli (return . const x), ())
>>= (\(Kleisli f, x) -> f x))
Kleisli (\r -> (\(Kleisli f, x) -> f x) (Kleisli (return . const x), ()))
Kleisli (\r -> (return . const x) ())
Kleisli (\r -> return x)
Kleisli (return . const x)
arr (const x)
我没有尝试对 bindY
做同样的事情,但我不知情的猜测是会产生类似的情况。