现有的Monad 类型类希望您的类型适用于每个 可能的类型参数。考虑Maybe:在Maybe a 中,a 根本不受约束。基本上你不能有一个有约束的 Monad。
这是 Monad 类定义的一个基本限制——我不知道有什么方法可以在不修改它的情况下绕过它。
这也是为其他常见类型定义Monad 实例的问题,例如Set。
在实践中,这个限制实际上非常重要。考虑(通常)函数是Num 的非实例。这意味着我们不能使用你的 monad 来包含一个函数!这确实限制了像 ap(<*> 来自 Applicative)这样的重要操作,因为这取决于包含函数的 monad:
ap :: Monad m => m (a -> b) -> m a -> m b
您的 monad 将不支持我们从普通 monad 中获得的许多常见用途和习语!这反而会限制它的实用性。
另外,作为旁注,您通常应该避免使用fail。它并不真正适合Monad 类型类:它更像是一个历史性事故。大多数人都同意你应该避免它:它只是一种在 do-notation 中处理失败的模式匹配的技巧。
也就是说,看看如何定义一个受限的 monad 类对于理解一些 Haskell 扩展和学习一些中级/高级 Haskell 是一个很好的练习。
替代方案
考虑到缺点,这里有几个替代方案——替换标准的Monad 类,确实支持受限单子。
约束种类
我能想到几个可能的替代方案。最现代的方法是利用 GHC 中的 ConstraintKind 扩展,它允许您将类型类约束具体化为种类。 This blog post 详细介绍了如何使用约束类型来实现受限的 monad;看完了,在这里总结一下。
基本思想很简单:使用ConstraintKind,我们可以将约束(Num a)转换为类型。然后我们可以有一个新的Monad 类,它包含这个类型作为成员(就像return 和fail 是成员一样)并允许使用Num a 重载约束。代码是这样的:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
module Main where
import Prelude hiding (Monad (..))
import GHC.Exts
class Monad m where
type Restriction m a :: Constraint
type Restriction m a = ()
return :: Restriction m a => a -> m a
(>>=) :: Restriction m a => m a -> (a -> m b) -> m b
fail :: Restriction m a => String -> m a
data IDnum a = IDnum a
instance Monad IDnum where
type Restriction IDnum a = Num a
return = IDnum
IDnum x >>= f = f x
fail _ = return 0
RMonad
现有的 hackage 库名为 rmonad(用于“受限制的 monad”),它提供了更通用的类型类。您可能可以使用它来编写您想要的 monad 实例。 (我自己没用过,所以有点不好说。)
它不使用ConstraintKinds 扩展并且(我相信)支持旧版本的 GHC。但是,我认为这有点难看;我不确定这是否是最佳选择。
这是我想出的代码:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
import Prelude hiding (Monad (..))
import Control.RMonad
import Data.Suitable
data IDnum a = IDnum a
data instance Constraints IDnum a = Num a => IDnumConstraints
instance Num a => Suitable IDnum a where
constraints = IDnumConstraints
instance RMonad IDnum where
return = IDnum
IDnum x >>= f = f x
fail _ = withResConstraints $ \ IDnumConstraints -> return 0
进一步阅读
更多详情请关注this SO question。
Oleg 有一篇专门与 Set monad 相关的文章,这可能很有趣:"How to restrict a monad without breaking it"。
最后,您还可以阅读几篇论文: