您如何理解 Purescript 中的错误消息?
How do you understand error messages in Purescript?
我正在使用 Purescript 构建一个加密货币套利机器人,主要是为了了解函数式编程范例。作为主要的 JS 程序员,我发现 Purescript 的错误消息很难解释。目前,在尝试使用 Affjax 库发出 http 请求时,我 运行 陷入 'TypesDoNotUnify' 错误。我的代码如下所示:
import Affjax.ResponseFormat (ResponseFormat(..))
import Affjax.ResponseFormat as ResponseFormat
import Data.Argonaut.Core (Json, fromString, stringify)
import Data.Either (Either(..))
import Data.HTTP.Method (Method(..))
import Effect (Effect)
import Effect.Aff (Aff, launchAff_)
import Effect.Console (log)
import Node.Express.App (App, listenHttp, get)
import Node.Express.Response (send)
import Node.HTTP (Server)
import Node.HTTP.Client (method)
makeQuoteRequest :: String -> String -> String -> String
makeQuoteRequest fromAddress toAddress amount = "https://api.1inch.exchange/v3.0/137/quote?fromTokenAddress=" <> fromAddress <> "&toTokenAddress=" <> toAddress <> "&amount=" <> amount
sendQuoteRequest :: Either Error Unit
sendQuoteRequest = launchAff_ do
result <- AX.request(AX.defaultRequest {
url = makeQuoteRequest "0xd6df932a45c0f255f85145f286ea0b292b21c90b" "0xc2132d05d31c914a87c6611c10748aeb04b58e8f" "1000000000000000000"
,method = Left GET
,responseFormat = ResponseFormat.json
})
case result of
Left err -> log $ "Quote failed: " <> AX.printError err
Right response -> log $ "GET /api response: " <> stringify response.body
app :: App
app = do
get "/" $ send "<a href='http://localhost:8080/quotes'><button>Get Quotes</button></a>"
get "/quotes" $ send "This is where your quotes should go"
main :: Effect Server
main = do
listenHttp app 8080 \_ ->
log $ "Listening on " <> show 8080
VsCode 突出显示以 Left err -> log
开头的行作为问题的根源,当我将鼠标悬停在该错误上时,我得到以下附加信息:
printError :: Error → String
Could not match type
Effect
with type
Aff
while trying to match type Effect Unit
with type Aff t0
while checking that expression (apply log) ((append "Quote failed: ") (printError err))
has type Aff t0
in value declaration sendQuoteRequest
where t0 is an unknown type
PureScript(TypesDoNotUnify)
我希望了解的不仅是如何修复这个错误,而是更多地了解如何解释 Purescript 给我的错误。如果您的代码中出现此错误,您会采取什么步骤来解决?
错误消息中的第一个单词是:
Could not match type Effect
with type Aff
然后它澄清了一点:
while trying to match type Effect Unit
with type Aff t0
这是绝大多数类型错误的样子。这意味着编译器一直在跟踪你的程序,弄清楚哪些位有哪些类型,最后发现一个位从一条推理结果是 Effect Unit
类型,但从另一行推理结果是 Effect Unit
类型 Aff t0
(对于某些未知类型 t0
)。两行推理都正确,但结果不匹配,所以编译器不知道下一步该做什么。
你问的那些推理是什么?好吧,编译器并没有告诉你,这有一个很好的理由:在大多数实际情况下,它对你来说无论如何都没有任何意义。从编译器的角度来看,推理线中的所有位都同样重要,但是它们太多了,无法一次打印出来,而且它不知道如何选择对你最有意义的那些。
但它确实为您提供了另一条有价值的信息 - 结果表明该位具有两种不匹配的类型:
while checking that expression (apply log) ((append "Quote failed: ") (printError err))
has type Aff t0
在这里我认为编译器可以更好地打印出表达式。它最终会到达那里,但现在它只需要最小的努力就可以打印出来。但不要害怕!我们仍然可以解码它。
看到那边的 apply
了吗?那是什么?你没有把它写在你的代码中,那么它是从哪里来的?
好吧,您确实在代码中写了它:美元运算符 $
是 apply
(see docs) 的别名。同样,运算符 <>
是 append
.
的别名
知道了,我们可以将这段代码解码成它的原始形式:
(apply log) ((append "Quote failed: ") (printError err))
(($) log) (((<>) "Quote failed: ") (printError err))
($) log ((<>) "Quote failed: " (printError err))
log $ ("Quote failed: " <> (printError err))
log $ "Quote failed: " <> printError err
嘿,看!这是您在第 25 行写的位!
好的,到目前为止我们知道什么?我们知道编译器已经确定 log $ ...
表达式一方面必须具有类型 Effect Unit
并且 必须具有类型 Aff t0
另一方面。
让我们看看这段代码是什么。看:它是从 Effect.Console
(see docs) 调用 log
函数。它的 return 类型是 Effect Unit
。啊哈!所以 这就是 编译器认为类型应该是 Effect Unit
的原因!因为它是调用 log
函数的结果!
好的,很好,但是 Aff t0
呢?好吧,让我们看看该表达式的结果最终去向:作为 launchAff_
(see docs) 的参数。 launchAff_
采用什么类型的参数?惊喜 - Aff a
!
至此,谜团解开了:launchAff_
需要一个类型为 Aff a
的参数,但您却给它一个类型为 Effect Unit
的值。难怪编译器会抱怨!
至于如何修复。
第一个愚蠢的方法就是去 Pursuit 上搜索。我们有一个 Effect Unit
类型的值,我们需要将其转换为 Aff a
。那可能吗?好吧,让我们search for a function of type Effect Unit -> Aff a
。看哪:有这样一个函数,它叫做liftEffect
。所以我们可以使用它:
Left err -> liftEffect $ log $ "Quote failed: " <> AX.printError err
轰!完成。
但是,如果是我,我通常会这样想:我不可能是第一个遇到这种情况的人。想要从 Aff
上下文中打印到控制台不是很正常吗?不应该已经有一个应用程序吗?
所以我可能会去search for a suitable type, for example String -> Aff Unit
. And look: there is actually a log
function that fits. It does the same thing, but it works not specifically in Effect
, like the one you're using, but in any monad that has a MonadEffect
instance. Is Aff
such a monad? Well, let's search for Aff
and look:是的,是的。它确实有一个 MonadEffect
.
的实例
所以现在,知道了所有这些,我可能只是将我的导入从 Effect.Console (log)
更改为 Effect.Class.Console (log)
,然后我不需要进行任何其他更改:新的 log
函数将只在 Aff
.
中工作
我正在使用 Purescript 构建一个加密货币套利机器人,主要是为了了解函数式编程范例。作为主要的 JS 程序员,我发现 Purescript 的错误消息很难解释。目前,在尝试使用 Affjax 库发出 http 请求时,我 运行 陷入 'TypesDoNotUnify' 错误。我的代码如下所示:
import Affjax.ResponseFormat (ResponseFormat(..))
import Affjax.ResponseFormat as ResponseFormat
import Data.Argonaut.Core (Json, fromString, stringify)
import Data.Either (Either(..))
import Data.HTTP.Method (Method(..))
import Effect (Effect)
import Effect.Aff (Aff, launchAff_)
import Effect.Console (log)
import Node.Express.App (App, listenHttp, get)
import Node.Express.Response (send)
import Node.HTTP (Server)
import Node.HTTP.Client (method)
makeQuoteRequest :: String -> String -> String -> String
makeQuoteRequest fromAddress toAddress amount = "https://api.1inch.exchange/v3.0/137/quote?fromTokenAddress=" <> fromAddress <> "&toTokenAddress=" <> toAddress <> "&amount=" <> amount
sendQuoteRequest :: Either Error Unit
sendQuoteRequest = launchAff_ do
result <- AX.request(AX.defaultRequest {
url = makeQuoteRequest "0xd6df932a45c0f255f85145f286ea0b292b21c90b" "0xc2132d05d31c914a87c6611c10748aeb04b58e8f" "1000000000000000000"
,method = Left GET
,responseFormat = ResponseFormat.json
})
case result of
Left err -> log $ "Quote failed: " <> AX.printError err
Right response -> log $ "GET /api response: " <> stringify response.body
app :: App
app = do
get "/" $ send "<a href='http://localhost:8080/quotes'><button>Get Quotes</button></a>"
get "/quotes" $ send "This is where your quotes should go"
main :: Effect Server
main = do
listenHttp app 8080 \_ ->
log $ "Listening on " <> show 8080
VsCode 突出显示以 Left err -> log
开头的行作为问题的根源,当我将鼠标悬停在该错误上时,我得到以下附加信息:
printError :: Error → String Could not match type
Effect
with type
Aff
while trying to match type Effect Unit with type Aff t0 while checking that expression (apply log) ((append "Quote failed: ") (printError err)) has type Aff t0 in value declaration sendQuoteRequest
where t0 is an unknown type PureScript(TypesDoNotUnify)
我希望了解的不仅是如何修复这个错误,而是更多地了解如何解释 Purescript 给我的错误。如果您的代码中出现此错误,您会采取什么步骤来解决?
错误消息中的第一个单词是:
Could not match type
Effect
with typeAff
然后它澄清了一点:
while trying to match type
Effect Unit
with typeAff t0
这是绝大多数类型错误的样子。这意味着编译器一直在跟踪你的程序,弄清楚哪些位有哪些类型,最后发现一个位从一条推理结果是 Effect Unit
类型,但从另一行推理结果是 Effect Unit
类型 Aff t0
(对于某些未知类型 t0
)。两行推理都正确,但结果不匹配,所以编译器不知道下一步该做什么。
你问的那些推理是什么?好吧,编译器并没有告诉你,这有一个很好的理由:在大多数实际情况下,它对你来说无论如何都没有任何意义。从编译器的角度来看,推理线中的所有位都同样重要,但是它们太多了,无法一次打印出来,而且它不知道如何选择对你最有意义的那些。
但它确实为您提供了另一条有价值的信息 - 结果表明该位具有两种不匹配的类型:
while checking that expression
(apply log) ((append "Quote failed: ") (printError err))
has typeAff t0
在这里我认为编译器可以更好地打印出表达式。它最终会到达那里,但现在它只需要最小的努力就可以打印出来。但不要害怕!我们仍然可以解码它。
看到那边的 apply
了吗?那是什么?你没有把它写在你的代码中,那么它是从哪里来的?
好吧,您确实在代码中写了它:美元运算符 $
是 apply
(see docs) 的别名。同样,运算符 <>
是 append
.
知道了,我们可以将这段代码解码成它的原始形式:
(apply log) ((append "Quote failed: ") (printError err))
(($) log) (((<>) "Quote failed: ") (printError err))
($) log ((<>) "Quote failed: " (printError err))
log $ ("Quote failed: " <> (printError err))
log $ "Quote failed: " <> printError err
嘿,看!这是您在第 25 行写的位!
好的,到目前为止我们知道什么?我们知道编译器已经确定 log $ ...
表达式一方面必须具有类型 Effect Unit
并且 必须具有类型 Aff t0
另一方面。
让我们看看这段代码是什么。看:它是从 Effect.Console
(see docs) 调用 log
函数。它的 return 类型是 Effect Unit
。啊哈!所以 这就是 编译器认为类型应该是 Effect Unit
的原因!因为它是调用 log
函数的结果!
好的,很好,但是 Aff t0
呢?好吧,让我们看看该表达式的结果最终去向:作为 launchAff_
(see docs) 的参数。 launchAff_
采用什么类型的参数?惊喜 - Aff a
!
至此,谜团解开了:launchAff_
需要一个类型为 Aff a
的参数,但您却给它一个类型为 Effect Unit
的值。难怪编译器会抱怨!
至于如何修复。
第一个愚蠢的方法就是去 Pursuit 上搜索。我们有一个 Effect Unit
类型的值,我们需要将其转换为 Aff a
。那可能吗?好吧,让我们search for a function of type Effect Unit -> Aff a
。看哪:有这样一个函数,它叫做liftEffect
。所以我们可以使用它:
Left err -> liftEffect $ log $ "Quote failed: " <> AX.printError err
轰!完成。
但是,如果是我,我通常会这样想:我不可能是第一个遇到这种情况的人。想要从 Aff
上下文中打印到控制台不是很正常吗?不应该已经有一个应用程序吗?
所以我可能会去search for a suitable type, for example String -> Aff Unit
. And look: there is actually a log
function that fits. It does the same thing, but it works not specifically in Effect
, like the one you're using, but in any monad that has a MonadEffect
instance. Is Aff
such a monad? Well, let's search for Aff
and look:是的,是的。它确实有一个 MonadEffect
.
所以现在,知道了所有这些,我可能只是将我的导入从 Effect.Console (log)
更改为 Effect.Class.Console (log)
,然后我不需要进行任何其他更改:新的 log
函数将只在 Aff
.