【问题标题】:Why can I use `>>=` without an explicit or implicit definition?为什么我可以在没有显式或隐式定义的情况下使用`>>=`?
【发布时间】:2015-08-29 00:07:30
【问题描述】:

我已经定义了一个类型X

newtype X i o = X { runX :: Int -> i -> IO o }

我已经创建了FunctorApplicativeMonad 的实例

instance Functor (X i) where
  fmap f a = X $ \ i o -> liftA f $ runX a i o

instance Applicative (X i) where
  pure x  = X $ \ _ _ -> return x
  a <*> b = X $ \ i o -> liftA2 (<*>) (runX a i o) (runX b i o)

instance Monad (X i) where
  return = pure
  a >> b = X $ \ i o -> runX a i o >> runX b i o

您可能会说,到目前为止,我一直无法为&gt;&gt;= 定义一个定义,因此将其排除在外。我预计这会在编译时出错,但实际上,它所做的只是发出警告。很好,所以它不会检查一个类的所有方法是否都已定义,但我确实不能使用 &gt;&gt;=。不对,又错了。令我惊讶的是,GHCi 很高兴地评估了let x = pure 5 &gt;&gt;= pureControl.Monad没有导出&gt;&gt;=的默认定义,我当然也没有定义,这怎么可能?

【问题讨论】:

  • 我无法让您的 Functor 实例进行类型检查。它抱怨它无法将类型 IO (f0 a)i0 -&gt; IO o0 匹配。
  • 发布的代码无法编译(例如,在fmap 定义中应该是runX a i o)。也许您在这里粘贴了错误的版本?
  • 这对我仍然不起作用,但取出 liftA 可以。
  • 仍然不进行类型检查。我认为您不需要fmap 定义中的fmap,也不需要(&lt;*&gt;) 定义中的liftA2。如果我删除它们,它会进行类型检查并收到您描述的警告。
  • 我相信 GHC 确实会发出 警告。但它可能需要使用-Wall 选项进行编译。此外,如果您定义了一个类,您可以使用MINIMAL 来准确地告诉编译器该实例应该提供哪些方法。通过这种方式,您可以提供相互递归的默认实现,并且仍然让编译器知道必须至少实现这两种方法中的一种才能使事情正常进行。

标签: haskell monads typeclass type-systems


【解决方案1】:

鉴于您的更正定义,如果我尝试定义然后使用 x,我会得到预期的运行时异常:

λ> let x = pure 5 >>= pure :: X Int Int
λ> runX x 5 5
*** Exception: foo.hs:12:10-20: No instance nor default method for class operation GHC.Base.>>=

您看不到这一点的可能原因有两个。

首先是您刚刚运行了let,但从未尝试评估结果。由于 Haskell 是懒惰的,let x = ... 实际上并没有做任何事情。 x 只会在您实际尝试使用它时进行评估(例如,runX),因此您会遇到错误。

另一种可能性是您使用了let 而不指定类型:

λ> let x = pure 5 >>= pure
λ> x
5

这里,x 在它使用的单子 m 中是多态的。要打印对像这样的多态术语有用的东西,ghci 默认将m 设置为IO,它可以正常工作并为您提供5,但不会告诉您有关自定义monad 的任何有用信息。

【讨论】:

  • 这也是我所期望的,但两者都不是真的。我在这里使用了高度类型受限的函数而不是pures,我尝试简单地将x 插入GHCi。它成功了,没有错误。然而,我使用的 IO 实际上代表了 WX 小部件的创建,当我尝试评估时,我得到了 SegFault。关于 wxHaskell 中的什么可能导致这种情况的任何想法?
  • @Kwarrtz:这就变成了一个不同的问题,我不确定我能提供多少帮助。我认为您最好单独询问有关您案件的更多详细信息。
猜你喜欢
  • 2014-09-03
  • 1970-01-01
  • 1970-01-01
  • 2012-09-16
  • 2021-04-21
  • 2017-08-05
  • 2017-02-21
  • 1970-01-01
相关资源
最近更新 更多