【问题标题】:Using >> without explicitly declaring it in a monad使用 >> 而不在 monad 中显式声明它
【发布时间】:2022-11-25 17:01:53
【问题描述】:

我正在努力精通 Monads 并编写了以下 Monads 和函数,其中我使用了 >>(在 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 ->                     
          (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

此外,我如何才能在运行应用程序时从控制台显示输出(打印)。目前我收到以下错误消息,尽管我的类型有 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

【问题讨论】:

  • 对于最后一个问题,您没有为 CMonad 派生任何实例,这是错误的来源。 GHC 无论如何都无法为函数类型派生那些实例;他们的要求没有多大意义。最后,&gt;&gt;= 的定义至少有一个拼写错误——确保你检查它是否遵守法律。

标签: haskell types functional-programming monads


【解决方案1】:

&gt;&gt; 运算符是可选的,不是必需的。 documentation 指出最小的完整定义&gt;&gt;=。当你能够同时实施&gt;&gt;return,您不必这样做。如果您不提供它们,Haskell 可以使用从 &gt;&gt; 和/或 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

请注意 &gt;&gt;= 缺少实现,这意味着您必须供应它。其他两个函数有一个默认实现,但您可以根据需要“覆盖”它们。

如果您想查看 GHCi 的输出,类型必须是 Show 实例。如果类型包装了一个函数,则没有明确的方法来做到这一点。

【讨论】:

    【解决方案2】:

    标准Prelude中Monad的声明如下:(由the Prelude source简化而来)

    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
    

    这是一个具有三个方法的类型类,(&gt;&gt;=)(&gt;&gt;)return

    在这三个中,两个有一个默认实现- 该功能在类型类中实现,而没有。

    MonadApplicative 的子类,returnpure 相同 - 由于历史原因,它包含在 Monad 类型类中(或根本不包含)。

    在剩下的两个中,(&gt;&gt;=) 是在 Haskell 中定义一个 Monad 所需要的。 (&gt;&gt;) 可以在类型类之外定义,如下所示:

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

    包含它的原因是为了防止 monad 作者想要用更高效的实现来覆盖默认实现。

    不需要的类型类方法称为可选的.

    Haddock 文档根据没有默认实现的方法自动生成“最小完整定义”。可以看到hereMonad的最小定义确实是(&gt;&gt;=)

    有时,所有方法都可以有默认实现,但它们不是可选的。当必须提供两种方法中的一种,而另一种是根据它定义时,就会发生这种情况。 Traversable 类型类就是这种情况,其中 traversesequenceA 都是根据彼此实现的。不实施任何一种方法都会导致它们进入无限循环。

    为了让您标记这一点,GHC 提供了 MINIMAL pragma,它会生成必要的编译器警告,并确保 Haddocks 是正确的。

    顺便说一句,未能实现所需的类型类方法默认情况下是编译器警告,而不是错误,如果调用,将导致运行时异常。这种行为没有充分的理由。

    您可以使用 -Werror=missing-methods GHC 标志更改此默认值。

    哈斯克林快乐!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-01
      • 2011-04-05
      • 2019-01-19
      • 1970-01-01
      • 1970-01-01
      • 2019-01-06
      • 2017-09-03
      相关资源
      最近更新 更多