我想定义一个monad实例,其中容器M为monad,并且包含类型a
,它应该是类Show
的成员。这种约束(a
是Show
的成员)应该由类型系统确保。
我尝试过这样的尝试,但遗憾的是M
不合适:
data M = forall a. Show a => M a
instance Monad M where
return x = M x
所有其他尝试实现此目的,遇到以下问题:由于Monad
是构造函数类,因此我无法显式访问所包含元素的类型a
,所以我不能限制它。
有没有人知道这个解决方案而没有定义新的Monad
类?
答案 0 :(得分:7)
嗯,实际上可以使用GADT在某种意义上限制类型构造函数的参数:
data M a where
M :: (Show a) => a -> M a
不幸的是,这实际上对你没有帮助。在某种程度上,它实际上使事情变得更糟,因为没有约束的Monad
实例,根本不可能编写实例。
如果你看一下上面的构造函数类型签名,它显然类似于return
- 这说明了为什么你所做的事情从根本上是不可能的。返回的类型是:(Monad m) => a -> m a
,并且始终在最外层隐式普遍量化未绑定类型变量,因此您可以将其读作“对于所有可能的类型a
,以及所有可能的类型{{ 1}}这是m
的实例,给定类型Monad
的值,您可以构造类型a
的值“。 “for all”措辞在那里非常直接 - 返回的类型不只是使用类型变量,而是主动断言任何类型m a
必须被允许。
所以,简而言之,没有。没有办法做你想要的,因为标准a
类明确指定相反。
答案 1 :(得分:5)
您可能无法完全按照自己的要求行事,但另一种可能性是,您的特定单身人士可以提供明确执行您正在考虑使用Show
的任何操作的操作。也就是说,假设你有:
data M a = {- ... -}
instance Monad M where -- notice: no Show constraint
{- ... -}
然后你可以另外提供一些动作:
report :: Show a => M a -> M a
我无法想到用Show
很好地利用这种模式,但我知道一个类似的例子,你可能想要Ord
约束。设置是你想制作一个不确定的monad(如[a]
),但没有重复(如Set a
)。删除重复项需要一些上下文,例如Eq
或Ord
,但我们无法在每个return
/ >>=
操作中要求这样做。因此,我们要求用户明确标记应该合并重复项的点:
newtype Setlike a = Setlike { toList :: [a] }
instance Monad Setlike where
return x = Setlike [x]
Setlike xs >>= f = [y | x <- xs, let Setlike ys = f x, y <- ys]
collapse :: Ord a => Setlike a -> Setlike a
collapse = Setlike . Data.Set.toList . Data.Set.fromList . toList
这可以这样使用:
valuesOfInterest = collapse $ do
v1 <- allValues
v2 <- allValues
doSomethingInteresting v1 v2
然后,即使v1
和v2
的某些配对恰好产生相同的兴趣值,该值也只会在结果中出现一次。
您的用例也可能有类似的技巧。
答案 2 :(得分:2)
没有。这是不可能的,虽然很容易解释。看看return
的类型签名:
return :: Monad m => a -> m a
签名无法更改,因此您的monad必须接受任何内容类型。由于类型类本身没有提及内容类型,因此无法对其实施约束。
你可以做的一件事是在需要它的所有函数上写下这个约束并删除全局限制。如果只有在需要时才能证明Show
约束的存在,系统的稳健性仍然有保证。
答案 3 :(得分:1)
这可能是一些极端的诡计。有关实现,请参阅rmonad包。但是,它可能不值得。
为什么需要a
Show
能够拥有约束?