我无法理解为什么这样的代码行实际起作用:
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8
main :: IO ()
main = httpBS "http://example.com" >>= B8.putStrLn . getResponseBody
我可以用符号重写这些东西:
test = do
request <- return "http://example.com"
result <- httpBS request
let body = getResponseBody result
B8.putStrLn body
即使我无法弄清楚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"
这给出了错误:
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.
确定。在解释器中输入相同的东西:
*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
}
看起来像我已经偶然遇到的可怕的单态限制内容c.f this question
所以我理解我必须给出类型。没关系但是我无法弄清楚如何使用&gt;&gt; =表示法,我只能设法使用符号:
-# 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
Q2:如果要先建立请求,如何将请求和httpBS与(&gt;&gt; =)运算符一起使用?
答案 0 :(得分:6)
我无法弄清楚
的类型是什么return "http://example.com"
幸运的是,GHCi可以:
> :t return "http://example.com"
return "http://example.com" :: Monad m => m [Char]
因此对于返回String的任何monad来说,这是一个有效的monad操作。
由于return
是Monad类型定义的一部分,所以这不应该是一个惊喜。
Q1:编译器如何设法找到我想要使用的Monad?
它使用类型推断。在do-block中,默认情况下将其限制为monad,return "..."
也是如此。 (RebindableSyntax
允许您将do-blocks的隐式(>>=)
重载到任何运算符,但这不是您经常看到的。)
行动httpBS :: MonadIO m => Request -> m (Response ByteString)
进一步将此约束为MonadIO m
,这显然是一种具有liftIO :: Monad m => IO a -> m a
的特殊Monad。 MonadIO m
的最简单示例是IO
,但编译器还没有具体化。
最后,B8.putStrLn :: ByteString -> IO ()
约束test :: IO ()
。
Q2:如果要先建立请求,如何将请求和httpBS与(&gt;&gt; =)运算符一起使用? 如何使用单行和&gt;&gt; =表示法?
do-blocks和无点单行都可以方便且略显神奇。您可以通过对do-block进行去糖处理来桥接这两种表示法,然后执行η-reduction。理解do-notation的好读物仍然是Philip Wadler的Monads for functional programming。
以下内容:
test = do
request <- ioRequest
response <- httpBS request
B8.putStrLn (getResponseBody response)
去糖:
test =
ioRequest >>= \request ->
httpBS request >>= \response ->
B8.putStrLn (getResponseBody response)
但是B8.putStrLn (getResponseBody response)
是(B8.putStrLn . getResponseBody) response
。
(这种转变的关键是将两个lambda表示为\x -> f x
。)
所以这就变成了:
test =
(ioRequest >>= \request -> httpBS request)
>>= \response -> (B8.putStrLn . getResponseBody) response
但\request -> httpBS request
只是httpBS
。
\response -> (B8.putStrLn . getResponseBody) response
只是B8.putStrLn . getResponseBody
。
所以这就变成了:
test =
(ioRequest >>= httpBS)
>>= B8.putStrLn . getResponseBody
重新格式化以适合一行:
test = ioRequest >>= httpBS >>= B8.putStrLn . getResponseBody
答案 1 :(得分:3)
Q1:do-block的每一行必须“in”相同的 monad。 (这就是Maybe
不起作用的原因;你不能将IO
monad与ioRequest
monad混合。)
Q2:关闭,但不完全。
>>=
是一项I / O操作,因此您需要使用test = ioRequest >>= httpBS >>= B8.putStrLn . getResponseBody
从中获取请求:
request <- return "http://example.com/"
虽然我在这里,但我也会指出
let request = "http://example.com/"
完全等同于
Magento 1