haskell 使用>>,而不在单子中显式声明它

eyh26e7m  于 2022-11-30  发布在  其他
关注(0)|答案(2)|浏览(134)

我正在努力学习Monad,并编写了以下Monad和函数,其中我使用了>>(在apply-函数中),尽管它没有在Monad本身中声明。这是如何编译的呢?据我所知,http://learnyouahaskell.com/a-fistful-of-monads#walk-the-line要求在Monad的示例化中声明它,就像Maybe Monad的情况一样。

data Value =
    NoneVal
  | TrueVal | FalseVal
  | IntVal Int
  | StringVal String
  | ListVal [Value]
  deriving (Eq, Show, Read)

data RunErr = EBadV VName | EBadF FName | EBadA String
  deriving (Eq, Show)

newtype CMonad a = CMonad {runCMonad :: Env -> (Either RunErr a, [String]) }

instance Monad CMonad where
  return a = CMonad (\_ -> (Right a, []))
  m >>= f = CMonad (\env ->  case runCMonad m env of                   
          (Left a, strLst) -> (Left a, strLst)
          (Right a, strLst) -> let (a', strLst') = runCMonad (f a) env in (a', strLst ++ strLst')) 

output :: String -> CMonad ()
output s = CMonad(\env -> (Right (),  [] ++ [s]))

apply :: FName -> [Value] -> CMonad Value
apply "print" [] = output "" >> return NoneVal

此外,我如何才能在运行apply时从控制台显示输出(打印输出)呢?目前我得到了以下错误消息,尽管我的类型有derive Show

<interactive>:77:1: error:
* No instance for (Show (CMonad Value)) arising from a use of `print'
* In a stmt of an interactive GHCi command: print it
cpjpxq1n

cpjpxq1n1#

>>运算符是可选的,不是必需的。文档中指出,* 最小完整定义 * 是>>=。虽然您 * 可以 * 实现>>return,但您不必这样做。如果您不提供它们,Haskell可以使用默认的实现,这些实现是从>>和/或Applicativepure派生的。
类型类的定义是(当前GHC源代码,简化为基本要素):

class Applicative m => Monad m where
    (>>=)       :: forall a b. m a -> (a -> m b) -> m b

    (>>)        :: forall a b. m a -> m b -> m b
    m >> k = m >>= \_ -> k
  
    return      :: a -> m a
    return      = pure

注意>>=缺少一个实现,这意味着你 * 必须 * 提供它。另外两个函数有一个默认实现,但是如果你想的话你可以“覆盖”它们。
如果你想看到GHCi的输出,类型必须是一个Show示例。如果类型 Package 了一个函数,没有明确的方法可以做到。

k3bvogb1

k3bvogb12#

标准Prelude中Monad的声明如下:(简化自Prelude来源)

class Applicative m => Monad m where
    (>>=) :: forall a b. m a -> (a -> m b) -> m b

    (>>) :: forall a b. m a -> m b -> m b
    m >> k = m >>= \_ -> k
    {-# INLINE (>>) #-}

    return      :: a -> m a
    return      = pure

它是一个类型类,有三个方法:(>>=)(>>)return
在这三个函数中,有两个有 *default实现 * -函数在类型类中实现,而另一个没有。
MonadApplicative的一个子类,而returnpure相同--由于历史原因,它被包含在Monad类型类中(或根本不包含)。
在剩下的两个中,(>>=)是在Haskell中定义Monad所需要的全部。(>>)可以在类型类之外定义,如下所示:

(>>) :: (Monad m) => forall a b. m a -> m b -> m b
m >> k = m >>= \_ -> k

之所以包括这个,是为了防止单子作者希望用更有效的实现来覆盖默认实现。
一个不是必需的类型类方法被称为 optional
Haddock文档基于没有默认实现的方法自动生成了一个“最小完整定义”。您可以在这里看到Monad的最小定义实际上是(>>=)
有时候,所有的方法都可以有默认的实现,但是它们不是可选的。当必须提供两个方法中的一个,而另一个是根据它定义的时候,就会发生这种情况。Traversable类型类就是这种情况,其中traversesequenceA都是根据对方实现的。不实现任何一个方法都会导致它们进入无限循环。
为了标记这一点,GHC提供了MINIMAL编译指示,它生成必要的编译器警告,并确保Haddock是正确的。
顺便说一句,默认情况下,未能实现所需的类型类方法是一个编译器警告,而不是一个错误,如果调用它,将导致运行时异常。这种行为没有很好的理由。
您可以使用-Werror=missing-methods GHC标志更改此默认值。
哈斯克林快乐!

相关问题