如何从do符号传递给(>> =)运算符表示法?

san*_*ood 1 monads haskell

我真的不明白为什么这样的代码行实际工作:

来自 Network.HTTP.Simple

{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8

main :: IO ()
main = httpBS "http://example.com" >>= B8.putStrLn . getResponseBody
Run Code Online (Sandbox Code Playgroud)

我可以用符号重写这些东西:

test = do
          request <- return "http://example.com"
          result <- httpBS request
          let body = getResponseBody result
          B8.putStrLn body
Run Code Online (Sandbox Code Playgroud)

这工作,即使我无法弄清楚是什么类型return "http://example.com".

Q1:编译器如何设法找到我想要使用的Monad?

我的猜测是:它来自do块的返回是一个IO(),因此它将是一个IO(请求)?

现在,当尝试在更复杂的代码中使用httpBS时,我遇到了一些困难

test.hs文件:

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8


request = parseRequest "http://example.com" 
Run Code Online (Sandbox Code Playgroud)

这给出了错误:

Prelude> :load test.hs
[1 of 1] Compiling Main             ( test.hs, interpreted )

test.hs:8:11: error:
    * Ambiguous type variable `m0' arising from a use of `parseRequest'
      prevents the constraint `(Control.Monad.Catch.MonadThrow
                                  m0)' from being solved.
      Relevant bindings include
        request :: m0 Request (bound at test.hs:8:1)
      Probable fix: use a type annotation to specify what `m0' should be.
      These potential instances exist:
        instance e ~ GHC.Exception.SomeException =>
                 Control.Monad.Catch.MonadThrow (Either e)
          -- Defined in `Control.Monad.Catch'
        instance Control.Monad.Catch.MonadThrow IO
          -- Defined in `Control.Monad.Catch'
        instance Control.Monad.Catch.MonadThrow Maybe
          -- Defined in `Control.Monad.Catch'
        ...plus one other
        ...plus 15 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    * In the expression: parseRequest "http://example.com"
      In an equation for `request':
          request = parseRequest "http://example.com"
  |
8 | request = parseRequest "http://example.com"
  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded. 
Run Code Online (Sandbox Code Playgroud)

好.在解释器中输入相同的东西:

*Main Network.HTTP.Simple> import Network.HTTP.Simple
*Main Network.HTTP.Simple> req = parseRequest "http://example.com"
*Main Network.HTTP.Simple> :t req
req :: Control.Monad.Catch.MonadThrow m => m Request
*Main Network.HTTP.Simple> req
Request {
  host                 = "example.com"
  port                 = 80
  secure               = False
  requestHeaders       = []
  path                 = "/"
  queryString          = ""
  method               = "GET"
  proxy                = Nothing
  rawBody              = False
  redirectCount        = 10
  responseTimeout      = ResponseTimeoutDefault
  requestVersion       = HTTP/1.1
}
Run Code Online (Sandbox Code Playgroud)

它看起来像可怕的单态限制的东西,我已经偶然发现 这个问题

所以我明白我必须给出类型.它没关系,但后来我无法弄清楚如何使用>> =表示法,我只能设法使用符号:

-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8

url  = "http://example.com"
maybeRequest :: Maybe Request
maybeRequest = parseRequest url 

ioRequest :: IO Request
ioRequest = parseRequest url

--no. wrong type ioRequest.
--testKO = httpBS ioRequest >>= B8.putStrLn . getResponseBody

--How to have it working with a one-liner and  the >>= notation ? 

--do notation ok
test = do 
        request <- ioRequest
        response <- httpBS request
        let body = getResponseBody response
        B8.putStrLn body
Run Code Online (Sandbox Code Playgroud)

Q2:如果要先建立Request,如何使用Request和httpBS与(>> =)运算符?

Sim*_*ine 6

我无法弄清楚它的类型是什么 return "http://example.com"

幸运的是,GHCi可以:

> :t return "http://example.com"
return "http://example.com" :: Monad m => m [Char]
Run Code Online (Sandbox Code Playgroud)

所以对于任何返回String的monad来说,这是一个有效的monad动作.

由于return是Monad类型定义的一部分,这不应该是一个惊喜.

Q1:编译器如何设法找到我想要使用的Monad?

它使用类型推断.在do-block中,默认情况下将其限制为monad,因此也是如此return "...".(RebindableSyntax允许你将(>>=)do-blocks 的隐式重载到任何运算符,但这不是你经常看到的.)

这个动作httpBS :: MonadIO m => Request -> m (Response ByteString)进一步将这种情况限制在所谓的MonadIO m,这显然是一种特殊的Monad liftIO :: Monad m => IO a -> m a.最简单的例子MonadIO mIO,但编译器还没有具体化.

最后,B8.putStrLn :: ByteString -> IO ()约束test :: IO ().

Q2:如果要先建立Request,如何使用Request和httpBS与(>> =)运算符?如何使用单线和>> =符号?

do-blocks和point-free one-liners既方便又略显神奇.您可以通过对do-block进行除糖然后执行η-reduction来桥接这两种符号.理解do-notation的好读物仍然是Philip Wadler的Monads用于函数式编程.

下列:

test = do
  request <- ioRequest
  response <- httpBS request
  B8.putStrLn (getResponseBody response)
Run Code Online (Sandbox Code Playgroud)

去糖成:

test =
  ioRequest >>= \request ->
  httpBS request >>= \response ->
  B8.putStrLn (getResponseBody response)
Run Code Online (Sandbox Code Playgroud)

不过B8.putStrLn (getResponseBody response)
(B8.putStrLn . getResponseBody) response.

(这种转变的关键是将两个lambda表示为\x -> f x.)

所以这变成了:

test =
  (ioRequest >>= \request -> httpBS request)
    >>= \response -> (B8.putStrLn . getResponseBody) response
Run Code Online (Sandbox Code Playgroud)
  • \request -> httpBS request就是这样httpBS.

  • 而且\response -> (B8.putStrLn . getResponseBody) response
    只是B8.putStrLn . getResponseBody.

所以这变成了:

test =
  (ioRequest >>= httpBS)
    >>= B8.putStrLn . getResponseBody
Run Code Online (Sandbox Code Playgroud)

重新格式化以适合一行:

test = ioRequest >>= httpBS >>= B8.putStrLn . getResponseBody
Run Code Online (Sandbox Code Playgroud)