【问题标题】:Simplify Monadic Type Signature in Haskell简化 Haskell 中的 Monadic 类型签名
【发布时间】:2014-06-05 18:38:21
【问题描述】:

我还是 Haskell 的新手,很难为函数找到正确的类型签名。

我有这个使用 http-conduitauthenticate-oauth 的工作相当简单的函数,这是有副作用的,所以我不太关心返回值:

executeOAuthRequest oauth cred request =
    withManager $ \manager -> do
        signed <- signOAuth oauth cred request
        http signed manager

我想指定正确的类型签名,但是 GHCi 的输出对我来说看起来相当可怕:

executeOAuthRequest
  :: (monad-control-0.3.2.3:Control.Monad.Trans.Control.MonadBaseControl
        IO m,
      Control.Monad.Trans.Resource.Internal.MonadThrow m,
      Control.Monad.Trans.Resource.Internal.MonadUnsafeIO m,
      Control.Monad.IO.Class.MonadIO m) =>
     OAuth
     -> Credential
     -> Request
     -> m (Response (ResumableSource (ResourceT m) ByteString))

前三个参数(OAuthCredentialRequest)对我来说很有意义,但我不明白 m 的长前提条件,并且想知道是否有必要将完整的返回值指定为GHCi 建议。

不仅仅是提供正确的签名,我还想了解查找和减少正确签名的过程是什么样的。

【问题讨论】:

  • 请注意IO 实现了所有这些约束,因此如果您只需要使用IO,您可以删除约束并将m 专门化为IO,如下所示:OAuth -&gt; Credential -&gt; Request -&gt; IO (Respond (ResumableSource (ResourceT IO) ByteString))
  • @GabrielGonzalez 谢谢!这是因为 IO 是 * -&gt; * 还是因为它满足所有约束的另一个属性?
  • @passy 没有。 * -&gt; * 是一种。它只意味着IO 是一个类型构造函数(而IO a 是任何类型a 的类型)。 IO 统计所有约束,因为声明了类型类实例。例如,IO 有一个 MonadIO 实例。更复杂的类型签名很有用,因为还有其他类似于 IO 的 monad,可以使用它们来代替。
  • @passy 这个想法是这些约束使类型更加灵活,以便您的函数可以与 IO 以外的 monad 一起使用。但是,如果您不需要额外的灵活性,您可以通过选择实现这些约束的特定 monad 来简化类型。

标签: haskell types


【解决方案1】:

GHCi 为您提供最多态的签名,阅读“任何类型的 m,它是来自 Monad.Trans.Resource.Internal 模块、MonadIO 等的类 MonadThrow 的实例”。类型检查器在某种程度上采用了由您在executeOAuthRequest 中编写的那些多态函数的类型引入的所有约束的联合。

这是一个简单的例子:

Prelude> let f n = n + 1
Prelude> :t f  -- GHC infers the polymorphic signature constrained to `Num`:
f :: Num a => a -> a
Prelude> let f :: Int -> Int ; f n = n + 1
Prelude> :t f
f :: Int -> Int

在你的函数中满足所有这些约束的唯一类型可能是IO,或者可能有多个。通常的检查方法是阅读黑线鳕,或做例如

Prelude> :info IO
...
instance Monad IO -- Defined in ‘GHC.Base’
instance Functor IO -- Defined in ‘GHC.Base’
Prelude> :info Monad
...
instance Monad [] -- Defined in ‘GHC.Base’
instance Monad IO -- Defined in ‘GHC.Base’
instance Monad ((->) r) -- Defined in ‘GHC.Base’

所以如果你只在IO 中使用你的函数,你可以给它类型:

executeOAuthRequest
  :: OAuth
     -> Credential
     -> Request
     -> IO (Response (ResumableSource (ResourceT m) ByteString))

或者您可以保留多态签名,例如,如果您在库中导出此函数并认为您的用户可能希望在不同的 monad 中使用它(甚至可能通过定义自己的新类型并将其设为实例MonadIOMonadThrow 等)

【讨论】:

  • 很好的解释,谢谢。关于图书馆的注释真的很有趣。我天真地假设 GHC 的输出只是嘈杂,减少签名总是你想要的,但没有考虑过有人可能想要使用不同的 monad。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-22
  • 2016-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多