[英]How do you understand error messages in Purescript?
I'm working on building a crypto arbitrage bot in Purescript, mostly to learn about the functional programming paradigm.我正在使用 Purescript 构建一个加密货币套利机器人,主要是为了了解函数式编程范例。 As a JS programmer primarily, I find Purescript's error messages very difficult to interpret.
作为主要的 JS 程序员,我发现 Purescript 的错误消息很难解释。 Currently, while trying to use the Affjax library to make an http request I'm running into a 'TypesDoNotUnify' error.
目前,在尝试使用 Affjax 库发出 http 请求时,我遇到了“TypesDoNotUnify”错误。 My code looks like this:
我的代码如下所示:
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 highlights the line beginning with Left err -> log
as the source of the problem, and when I hover over that the error I get the following additional info: VsCode 突出显示以
Left err -> log
开头的行作为问题的根源,当我 hover 超过该错误时,我得到以下附加信息:
printError:: Error → String Could not match type
printError:: 错误 → 字符串无法匹配类型
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
在尝试将类型 Effect Unit 与类型 Aff t0 匹配时同时检查表达式(应用日志)((附加“Quote failed:”)(printError err))在值声明 sendQuoteRequest 中具有类型 Aff t0
where t0 is an unknown type PureScript(TypesDoNotUnify)
其中 t0 是未知类型 PureScript(TypesDoNotUnify)
What I'm hoping to understand is not only how to fix this error, but to learn a little more about how to interpret the errors that Purescript gives me.我希望了解的不仅是如何修复这个错误,而是更多地了解如何解释 Purescript 给我的错误。 What steps would you take to work through this error if it came up in your code?
如果您的代码中出现此错误,您将采取什么步骤来解决它?
The first words in the error message are:错误消息中的第一句话是:
Could not match type
Effect
with typeAff
无法将类型
Effect
与类型Aff
匹配
And then it clarifies a bit:然后它澄清了一点:
while trying to match type
Effect Unit
with typeAff t0
在尝试将类型
Effect Unit
与类型Aff t0
匹配时
This is how the vast majority of type errors will look.这就是绝大多数类型错误的样子。 It means the compiler was following your program along, figuring out which bits have which types, and finally found a bit which from one line of reasoning turned out to have type
Effect Unit
, but from another line of reasoning turned out to have type Aff t0
(for some as of yet unknown type t0
).这意味着编译器一直在跟踪您的程序,找出哪些位具有哪些类型,并最终找到一个位,从一行推理中得出的类型为
Effect Unit
,但从另一行推理中得出的类型为Aff t0
(对于一些未知的类型t0
)。 Both lines of reasoning are correct, but their results don't match, so the compiler doesn't know what to do next.两行推理都是正确的,但它们的结果不匹配,因此编译器不知道下一步该做什么。
Which were those lines of reasoning you ask?你问的那些推理是什么? Well, the compiler isn't telling you, and there is a good-ish reason for that: in most real cases it wouldn't make any sense to you anyway.
好吧,编译器并没有告诉你,这有一个很好的理由:在大多数实际情况下,它对你来说无论如何都没有任何意义。 From the compiler's point of view all bits in the line of reasoning are equally important, but there are too many of them to print all at once, and it doesn't know how to pick the ones that would make most sense to you.
从编译器的角度来看,推理线中的所有位都同样重要,但是它们太多了,无法一次打印出来,而且它不知道如何选择对您最有意义的位。
But it does give you another valuable piece of information - which bit turned out to have two mismatching types:但它确实为您提供了另一条有价值的信息——该位结果有两种不匹配的类型:
while checking that expression
(apply log) ((append "Quote failed: ") (printError err))
has typeAff t0
同时检查表达式
(apply log) ((append "Quote failed: ") (printError err))
的类型为Aff t0
Here I think the compiler could do a bit of a better job of printing out the expression.在这里,我认为编译器可以更好地打印出表达式。 It will get there eventually, but for now it just prints with minimal effort.
它最终会到达那里,但现在它只需要最小的努力就可以打印出来。 But no fear.
但不要害怕。 We can still decode it.
我们仍然可以解码它。
See that apply
over there?看到那边
apply
了吗? What's that?那是什么? You didn't write that in your code, so where did it come from?
你没有把它写在你的代码中,那么它是从哪里来的呢?
Well, you kinda did write it in your code: the dollar operator $
is an alias for apply
( see docs ).好吧,您确实在代码中写了它:美元运算符
$
是apply
的别名( 请参阅文档)。 And similarly, the operator <>
is an alias for append
.同样,运算符
<>
是append
的别名。
Knowing that, we can decode this bit of code into its original form:知道了这一点,我们就可以将这段代码解码成它的原始形式:
(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
Hey, look!你看! This is the bit you wrote on line 25!
这是您在第 25 行写的位!
Ok, so what do we know so far?好的,那么到目前为止我们知道什么? We know that the compiler has determined that the
log $...
expression must have the type Effect Unit
on one hand and the type Aff t0
on the other hand.我们知道编译器已经确定
log $...
表达式一方面必须具有Effect Unit
类型,另一方面必须具有Aff t0
类型。
Let's see what this bit of code is.让我们看看这段代码是什么。 Look: it's a call to the
log
function from Effect.Console
( see docs ).看:这是从
Effect.Console
调用log
function( 参见文档)。 Its return type is Effect Unit
.它的返回类型是
Effect Unit
。 Aha!啊哈! So that's why the compiler thinks the type should be
Effect Unit
!所以这就是编译器认为类型应该是
Effect Unit
的原因! Because it's the result of a call to the log
function!因为它是调用
log
函数的结果!
Ok, good, but what about Aff t0
?好的,很好,但是
Aff t0
呢? Well, let's see where the result of that expression is ultimately going: as a parameter to launchAff_
( see docs ).好吧,让我们看看该表达式的结果最终会去哪里:作为
launchAff_
的参数( 请参阅文档)。 And what type of parameter does launchAff_
take? launchAff_
采用什么类型的参数? Surprise - it's Aff a
!惊喜 - 这是
Aff a
!
So there you go, mystery solved: launchAff_
expects a parameter of type Aff a
, but you're giving it a value of type Effect Unit
.所以你 go,谜团解决了:
launchAff_
需要一个Aff a
类型的参数,但你给它一个Effect Unit
类型的值。 No wonder the compiler complains!难怪编译器会抱怨!
As for how to fix it.至于怎么修。
The first dumb way to do it is to just go and search on Pursuit.第一个愚蠢的方法是拨打 go 并搜索 Pursuit。 We have a value of type
Effect Unit
, and we need to convert it to Aff a
.我们有一个
Effect Unit
类型的值,我们需要将其转换为Aff a
。 Is that possible?那可能吗? Well, let's search for a function of type
Effect Unit -> Aff a
.好吧,让我们 搜索
Effect Unit -> Aff a
类型的 function 。 And behold: there is such function, it's called liftEffect
.看哪:有这样一个 function,它叫做
liftEffect
。 So we can just use that:所以我们可以使用它:
Left err -> liftEffect $ log $ "Quote failed: " <> AX.printError err
Boom.繁荣。 Done.
完毕。
But then, if it was me, I would normally think this way: I can't be the first person this happened to.但是,如果是我,我通常会这样想:我不可能是第一个发生这种情况的人。 Shouldn't it be a normal occurrence to want to print to console from within an
Aff
context?想要从
Aff
上下文中打印到控制台不是很正常吗? Should't there be already an app for that?不应该已经有一个应用程序吗?
So I might go and search for a suitable type, for example String -> Aff Unit
.所以我可能会 go 并搜索合适的类型,例如
String -> Aff Unit
。 And look: there is actually a log
function that fits .看看:实际上有一个
log
function 适合。 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.它做同样的事情,但它不像你正在使用的那样专门在
Effect
中工作,而是在任何具有MonadEffect
实例的 monad 中工作。 Is Aff
such a monad? Aff
是这样的单子吗? Well, let's search for Aff
and look : yes, it is.好吧,让我们搜索
Aff
并查看:是的,是的。 It does have an instance of MonadEffect
.它确实有一个
MonadEffect
实例。
So now, knowing all of that, I might just change my import from Effect.Console (log)
to Effect.Class.Console (log)
, and then I don't need to make any other changes: the new log
function will just work in Aff
.所以现在,知道了所有这些,我可能只是将导入从
Effect.Console (log)
更改为Effect.Class.Console (log)
,然后我不需要进行任何其他更改:新log
function 将只是在Aff
工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.