我正在尝试编写一个只能包含Num的新monad.当它失败时,它会返回0,就像Maybe monad在失败时返回Nothing一样.
这是我到目前为止:
data (Num a) => IDnum a = IDnum a
instance Monad IDnum where
return x = IDnum x
IDnum x >>= f = f x
fail :: (Num a) => String -> IDnum a
fail _ = return 0
Run Code Online (Sandbox Code Playgroud)
哈斯克尔抱怨说有
No instance for (Num a) arising from a use of `IDnum'
Run Code Online (Sandbox Code Playgroud)
它建议我为每个monad函数的类型签名的上下文添加一个add(Num a),但我尝试了它然后它抱怨他们需要工作"forall"a.
例如:
Method signature does not match class; it should be
return :: forall a. a -> IDnum a
In the instance declaration for `Monad IDnum'
Run Code Online (Sandbox Code Playgroud)
有谁知道如何解决这一问题?
Tik*_*vis 14
现有的Monad类型类期望您的类型适用于每个可能的类型参数.考虑一下Maybe:in Maybe a,a根本不受约束.基本上你不能有一个有约束的Monad.
这是如何Monad定义类的基本限制- 我不知道如何在不修改它的情况下绕过它.
这也是定义Monad其他常见类型的实例的问题,例如Set.
实际上,这种限制实际上非常重要.考虑(通常)函数不是实例Num.这意味着我们无法使用你的monad来包含一个函数!这实际上限制了像ap(<*>from Applicative)这样的重要操作,因为这取决于包含函数的monad:
ap :: Monad m => m (a -> b) -> m a -> m b
Run Code Online (Sandbox Code Playgroud)
你的monad不会支持我们从普通monad中获得的许多常用用法和习语!这宁可限制其效用.
另外,作为附注,您通常应该避免使用fail.它并不真正适合Monad类型类:它更像是一次历史性事故.大多数人都同意你应该一般地避免它:它只是一个黑客来处理失败的模式匹配.
也就是说,查看如何定义受限制的monad类是理解一些Haskell扩展和学习一些中间/高级Haskell的一个很好的练习.
在头脑里的缺点,这里有标准的一对夫妇的替代品,替代Monad该类不支持受限制的单子.
我可以想到几种可能的选择.最现代的将利用ConstraintKindGHC 中的扩展,它允许您将类型类约束作为种类来实现.这篇博文详细介绍了如何使用约束种类实现受限制的monad; 一旦我读完它,我会在这里总结一下.
基本思路很简单:有了ConstraintKind,我们可以将constrain(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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
有关更多详细信息,请查看此SO问题.
奥列格有一篇关于这个专门针对Set monad的文章,这可能很有趣:"如何限制monad而不破坏它".
最后,您还可以阅读几篇论文: