我正在尝试做某事,我认为Monad态射是答案,但我不确定如何在这里正确应用它们。简而言之,我有以下设置:
import Control.Monad.Identity
import Control.Monad.Writer
data F = F Bool Bool
instance Monoid F where
mempty = F True False
(F s0 c0) `mappend` (F s1 c1) = F (s0 && s1)
(c0 || c1 || not (s0 || s1))
type S = Writer F
type SInt = S Int
上面我用两个布尔标志定义了数据类型F,它使用Writer monad跟踪状态。例如,我将在函数foo中使用它们:
foo :: SInt -> SInt -> SInt
foo = liftM2 (+)
现在,我希望在某些情况下,实现不同的标志处理逻辑,并将其与foo
一起使用,而不更改foo
的类型签名。特别是,我想将上面的Monoid定义更改为:
instance Monoid F where
mempty = F True False
(F s0 c0) `mappend` (F s1 c1) = F (s0 && s1) (c0 || c1)
这是我在Monad Morphisms的帮助下能够做到的吗?怎么样?
答案 0 :(得分:3)
如果你真正想要的是重用foo
,你可以让它变成多态......
foo :: (Monoid b) => Writer b Int -> Writer b Int -> Writer b Int
...并将您的实例分为两种不同的类型(我会将它们作为newtypes
围绕元组编写,但如果您愿意,可以采用不同的方式:
newtype F = F { unF :: (Bool, Bool) }
deriving (Eq, Show)
instance Monoid F where -- etc.
newtype G = G { unG :: (Bool, Bool) }
deriving (Eq, Show)
instance Monoid G where -- etc.
至于使用monad态射,你当然可以编写一个函数,如...
bar :: Writer F a -> Writer G a
bar = writer . fmap (G . unF) . runWriter
...实际上只是交换Monoid
实例,就像你原来想要的那样。但是,您将无法使用 mmorph 机制,因为它实际上不是monad态射。引用Control.Monad.Morph
:
-- A monad morphism is a natural transformation:
morph :: forall a . m a -> n a
-- ... that obeys the following two laws:
morph (m >>= f) = morph m >>= morph . f
morph (return x) = return x
bar
的明显实施违反了这些法律中的第一条,因为mappend
和m >>= f
中隐含的morph m >>= morph . f
可以提供不同的结果,即使基础对布尔是一样的。