从我的问题的具体实例开始,我们都知道(并且喜欢)Monad
类型的类:
class ... => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> mb
...
请考虑以下可能的实例,其中我们使用nub
修改标准列表/“不确定性”实例,以仅保留每个“结果”的一个副本:
type DistinctList a = DL { dL :: [a] }
instance Monad DistinctList where
return = DL . return
x >>= f = DL . nub $ (dL x) >>= (dL . f)
...发现错误了吗?问题是nub :: Eq a => [a] -> [a]
以及x >>= f
仅在条件f :: Eq b => a -> DistinctList b
下定义,而编译器要求f :: a -> DistinctList b
。我有什么办法可以继续吗?
后退一步,假设我有一个仅在某些条件下在参数类型的变量上定义的准实例。我知道这通常是不允许的,因为不能保证使用类型类编写的其他代码提供遵守条件的参数值。但是在某些情况下仍然可以执行此操作吗?如果可以,怎么办?
答案 0 :(得分:6)
这是set-monad中应用的技术针对您的情况的改编。
请注意,必须存在一些“作弊”。该结构包括表示“返回”和“绑定”的额外值构造函数。这些充当需要运行的暂挂计算。 bcd
实例是Eq
函数的一部分,而创建“ suspension”的构造函数是run
免费的。
Eq
这是一个肮脏的技巧,可以提高效率,解决了下面有关绑定评估的问题。
{-# LANGUAGE GADTs #-}
import qualified Data.List as L
import qualified Data.Functor as F
import qualified Control.Applicative as A
import Control.Monad
-- for reference, the bind operation to be implemented
-- bind operation requires Eq
dlbind :: Eq b => [a] -> (a -> [b]) -> [b]
dlbind xs f = L.nub $ xs >>= f
-- data structure comes with incorporated return and bind
-- `Prim xs` wraps a list into a DL
data DL a where
Prim :: [a] -> DL a
Return :: a -> DL a
Bind :: DL a -> (a -> DL b) -> DL b
-- converts a DL to a list
run :: Eq a => DL a -> [a]
run (Prim xs) = xs
run (Return x) = [x]
run (Bind (Prim xs) f) = L.nub $ concatMap (run . f) xs
run (Bind (Return x) f) = run (f x)
run (Bind (Bind ma f) g) = run (Bind ma (\a -> Bind (f a) g))
-- lifting of Eq and Show instance
-- Note: you probably should provide a different instance
-- one where eq doesn't depend on the position of the elements
-- otherwise you break functor laws (and everything else)
instance (Eq a) => Eq (DL a) where
dxs == dys = run dxs == run dys
-- this "cheats", i.e. it will convert to lists in order to show.
-- executing returns and binds in the process
instance (Show a, Eq a) => Show (DL a) where
show = show . run
-- uses the monad instance
instance F.Functor DL where
fmap = liftM
-- uses the monad instance
instance A.Applicative DL where
pure = return
(<*>) = ap
-- builds the DL using Return and Bind constructors
instance Monad DL where
return = Return
(>>=) = Bind
-- examples with bind for a "normal list" and a "distinct list"
list = [1,2,3,4] >>= (\x -> [x `mod` 2, x `mod` 3])
dlist = (Prim [1,2,3,4]) >>= (\x -> Prim [x `mod` 2, x `mod` 3])
答案 1 :(得分:2)
如果您的类型可以是Monad,则它将需要在所有Monad或所有应用程序中参数化的函数中工作。但这不能,因为人们在他们的monad中存储了各种奇怪的东西。最值得注意的是,函数经常在应用上下文中作为值存储。例如,考虑:
pairs :: Applicative f => f a -> f b -> f (a, b)
pairs xs ys = (,) <$> xs <*> ys
即使a
和b
都是Eq
,为了将它们组合成(a, b)
对,我们需要先将函数映射到{{1} },简要产生类型为xs
的值。如果我们将f (b -> (a, b))
设为您的DL monad,我们会发现这是行不通的,因为此函数类型没有f
实例。
由于Eq
被保证适用于所有Applicatives,并且不适用于您的类型,因此我们可以确保您的类型不是Applicative。而且由于所有Monad也是适用的,因此我们可以得出结论,您的类型不可能成为Monad的实例:它将违反法律。