如何从 do 表示法传递到 (>>=) 运算符表示法?
How to pass from a do notation to a (>>=) operator notation?
我真的不明白为什么这样的代码行真的有效:
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8
main :: IO ()
main = httpBS "http://example.com" >>= B8.putStrLn . getResponseBody
我可以用 do 符号重写这些东西:
test = do
request <- return "http://example.com"
result <- httpBS request
let body = getResponseBody result
B8.putStrLn body
即使我无法弄清楚 return "http://example.com"
的类型,这仍然有效。
问题 1:编译器如何找到我要使用的 Monad?
我的猜测是:它来自于 do 块的 return 是一个 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
}
它看起来像我已经偶然发现的可怕的单态限制内容
所以我知道我必须给 type.It 确定,但后来我不知道如何用 >>= 符号来使用它,我只能设法让它与 do 一起工作表示法:
-# 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:如果Request是先构建的,如何使用Request和httpBS加上(>>=)操作符?
Q1: do-block 的每一行必须是 "in" 相同的 monad。 (这就是 maybeRequest
不起作用的原因;您不能将 Maybe
monad 与 IO
monad 混合使用。)
Q2:接近,但不完全。
ioRequest
是一个 I/O 动作,因此您需要使用 >>=
从中提取请求:
test = ioRequest >>= httpBS >>= B8.putStrLn . getResponseBody
虽然我在这里,但我还要指出
request <- return "http://example.com/"
完全等同于
let request = "http://example.com/"
I cannot figure out what is the type of return "http://example.com"
幸运的是,GHCi 可以:
> :t return "http://example.com"
return "http://example.com" :: Monad m => m [Char]
因此对于 returns 一个字符串的任何 monad 来说,它都是一个有效的 monad 动作。
因为 return
是 Monad 类型 class 定义的一部分,所以这不足为奇。
Q1 : How the compiler manage to find which Monad I want to use ?
它使用类型推断。默认情况下,在 do 块内将其限制为 monad,return "..."
也是如此。 (RebindableSyntax
允许您将 do 块的隐式 (>>=)
重载给任何运算符,但这不是您经常看到的东西。)
动作 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 : how to use the Request and httpBS with the (>>=) operator if the Request is to be build previously ? How to have it working with a one-liner and the >>= notation ?
do-blocks 和 point-free one-liner 都可以很方便,也有点神奇。您可以通过对 do 块进行去糖处理来桥接这两个符号,然后执行 η-reduction. A good read for understanding do-notation is still Philip Wadler's 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
我真的不明白为什么这样的代码行真的有效:
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8
main :: IO ()
main = httpBS "http://example.com" >>= B8.putStrLn . getResponseBody
我可以用 do 符号重写这些东西:
test = do
request <- return "http://example.com"
result <- httpBS request
let body = getResponseBody result
B8.putStrLn body
即使我无法弄清楚 return "http://example.com"
的类型,这仍然有效。
问题 1:编译器如何找到我要使用的 Monad?
我的猜测是:它来自于 do 块的 return 是一个 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
}
它看起来像我已经偶然发现的可怕的单态限制内容
所以我知道我必须给 type.It 确定,但后来我不知道如何用 >>= 符号来使用它,我只能设法让它与 do 一起工作表示法:
-# 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:如果Request是先构建的,如何使用Request和httpBS加上(>>=)操作符?
Q1: do-block 的每一行必须是 "in" 相同的 monad。 (这就是 maybeRequest
不起作用的原因;您不能将 Maybe
monad 与 IO
monad 混合使用。)
Q2:接近,但不完全。
ioRequest
是一个 I/O 动作,因此您需要使用 >>=
从中提取请求:
test = ioRequest >>= httpBS >>= B8.putStrLn . getResponseBody
虽然我在这里,但我还要指出
request <- return "http://example.com/"
完全等同于
let request = "http://example.com/"
I cannot figure out what is the type of
return "http://example.com"
幸运的是,GHCi 可以:
> :t return "http://example.com"
return "http://example.com" :: Monad m => m [Char]
因此对于 returns 一个字符串的任何 monad 来说,它都是一个有效的 monad 动作。
因为 return
是 Monad 类型 class 定义的一部分,所以这不足为奇。
Q1 : How the compiler manage to find which Monad I want to use ?
它使用类型推断。默认情况下,在 do 块内将其限制为 monad,return "..."
也是如此。 (RebindableSyntax
允许您将 do 块的隐式 (>>=)
重载给任何运算符,但这不是您经常看到的东西。)
动作 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 : how to use the Request and httpBS with the (>>=) operator if the Request is to be build previously ? How to have it working with a one-liner and the >>= notation ?
do-blocks 和 point-free one-liner 都可以很方便,也有点神奇。您可以通过对 do 块进行去糖处理来桥接这两个符号,然后执行 η-reduction. A good read for understanding do-notation is still Philip Wadler's 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