I have the following piece of code:
let test =
Js.Promise.resolve("Hello") |> Js.Promise.then_(_obj => raise(Not_found));
let ker =
switch test {
| exception Not_found => Js.log("not found")
| _ => Js.log("found")
};
The output it produces is:
"found"
Why does the above code not produce the output "not found" and what should I do to make sure the first branch in the switch statement gets executed ?
This mostly has to do with the way JavaScript handles exceptions that get thrown inside promises. They need to be handled using https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch , which is bound to https://bucklescript.github.io/bucklescript/api/Js.Promise.html#VALcatch , because they are 'caught' inside the promise and stay inside the promise context.
Concretely, I would turn your example into this:
let test = Js.Promise.(resolve("Hello") |> then_(_obj => raise(Not_found)));
let ker = Js.Promise.(test
|> then_((_) => "found" |> Js.log |> resolve)
|> catch((_) => "Not found" |> Js.log |> resolve));
Because a promise is a promise, regardless if it's a failed one or not.
In your case, at some point it will be resolved to a failed promise, but it will never raise/throw an error. It will just be a promise whose inner value, at some point will be an error (a non thrown error).
In order to catch the error (or value), you need to use the catch function on your test binding. The catch function does with rejected promises, what the then function does to fulfilled ones.
Snippet:
let handleData = [#bs.open] (
fun
| Invalid_argument(_) => Js.Promise.resolve(0)
| Not_found => Js.Promise.resolve(1)
| Sys_error(_) => Js.Promise.resolve(2)
);
let ker =
test |> Js.Promise.catch(handleData)
Related
First of all - sorry for stupid question.
I got some Json string from DB and want to parse all of them with json4s:
val df = sqlContext.sql("SELECT * FROM analytic.test").repartition(22)
val df_base = df.map(f => {
implicit val formats = DefaultFormats
val jsonString = f(5).toString
val tempJSON = parse(jsonString)
val mainJsonArray = tempJSON \ "events"
(
f(2).toString,
makeEventArray(mainJsonArray)
)
}).cache()
All good, i got Json's, but sometimes in DB occurs some failed Json, that take me to error :
com.fasterxml.jackson.core.JsonParseException: Unexpected end-of-input: was expecting closing '"' for name
First question - How can I evade this row with corrupt Json and continue my program?
I trying surround parse with try\catch, but in this case:
var tempJSON = json4s.JsonAST.JValue
try {
tempJSON = parse(f(5).toString)
} catch {
case e: Exception => println("Error on JSON parser. " + e )
}
But taking error:
Error:(51, 25) type mismatch;
found: org.json4s.JValue (which expands to) org.json4s.JsonAST.JValue
required: org.json4s.JsonAST.JValue.type tempJSON = parse(f(5).toString)
^
Second question - How to declare tempJson right?
Or I must validate Json before parse? How?
You can use Try for it:
val tempJSON = Try(parse(f(5).toString))
So now, you can match it:
tempJSON match {
case Success(yourValue) => ???
case Failure(exception) => println(exception.getMessage)
}
Or, if you don't need the exception, you would convert it to Option:
tempJSON.toOption
You'll get None or Some(value).
I don't know json4s but it probably exists, like in Play Json, a validate function returning something like a JsError or a JsSuccess.
Otherwise, an other way to go is to return an Option[JValue] (if you don't want to deal with exceptions), i.e. :
def function: Option[JValue] = {
try {
Option(parse(f(5).toString))
} catch {
case e: Exception => None
}
}
If you want to catch all parsing errors and silently skip them, which is risky and ill advised in most cases.
You can simply do:
df.flatMap(f => Try(do something).toOption)
You may however prefer, earlier validation preventing this to begin with, catching a more specific error only, logging errors etc.
I've written a utility class to help with unit testing. One function, fakeRequest(), will process a request, parse the response, validate the JSON, and make sure things went well in general. It then returns a tuple of (response, parsedJSON) to the caller. If things go poorly, it returns a failure.
This all works fine, but I can't figure out how to match on the returned tuple.
What I want:
1. If I get (response, parsedJSON) validate the response;
2. If I get failure, fail the test case.
My fakeRequest() looks like this:
def fakeRequest[A, B](target: () => Call, request: A, response: B)(implicit f1: Format[A], f2: Format[B]) = {
route(FakeRequest(target()).withJsonBody(Json.toJson(request))) match {
case Some(response) => {
contentType(response) must beSome("application/json")
charset(response) must beSome("utf-8")
GPLog(s"fakeRequest: HTTP status ${status(response)}: JSON response: ${contentAsString(response)}")
val parsedJSON = Json.parse(contentAsString(response)).validate[B]
(response, parsedJSON)
}
case _ => failure
}
}
And this is how I'm trying to match on the response. I've tried a couple dozen variations and can't get it to match on a response:
fakeRequest(controllers.routes.GPInviteService.invite, test, GPInviteService.InviteResponse(true, None, None)) match {
case (response, JsSuccess(invite: GPInviteService.InviteResponse, _)) => { // Never matches
status(response) must beEqualTo(UNPROCESSABLE_ENTITY)
invite.inviteSent must beFalse
invite.error.get.code must beEqualTo(GPError.ValidationFailed.code)
}
case e: JsError => failure
...
This invariably does not work. However, and this is what is really confusing me, I can return the response status code, just not the response object itself. E.g., if I change fakeRequest as such:
def fakeRequest[A, B](target: () => Call, request: A, response: B)(implicit f1: Format[A], f2: Format[B]) = {
route(FakeRequest(target()).withJsonBody(Json.toJson(request))) match {
case Some(response) => {
// ...
(status(response), parsedJSON) // return status, not response?
}
case _ => failure
}
}
I can test against the status just fine, and everything works as expected:
fakeRequest(controllers.routes.GPInviteService.invite, test, GPInviteService.InviteResponse(true, None, None)) match {
case (status, JsSuccess(response: GPInviteService.InviteResponse, _)) => { // Matches!
status must beEqualTo(UNPROCESSABLE_ENTITY)
response.inviteSent must beFalse
response.error.get.code must beEqualTo(GPError.ValidationFailed.code)
}
case e: JsError => failure
}
I'd love to understand why I can match a simple status code, but I can't match a more complex object.
FYI, I even tried case (a, b) => println(a) just to see what would happen. It didn't match when returning the response.
I am new to programming and F# is my first language. I am currently still very unfamiliar with .NET APIs.
As a beginner's project, I want to scrape a website. I want to write a function that, given a specific URL, automatically downloads all the HTML contents on that page. However, if the URL is invalid, rather than throwing a System.Net.WebException message, I want to return the Boolean output "False" instead.
Here is the relevant part of my code:
let noSuchURL (url: string) =
let html = downloadHtmlFromUrl url
let regexPattern = #"<title>Page not found</title>"
let matchResult = Regex.IsMatch(html, regexPattern)
matchResult
(I have tested the downloadHtmlFromUrl function in F# interactive, and it works fine.)
I realised that the code above does not return a Boolean value in the event that the address is invalid. Instead, System.Net.WebException is thrown, with the message "System.Net.WebException: The remote server returned an error: (404) Not Found".
What changes can I make to get a Boolean output?
Maybe catch the exception?
let noSuchURL (url: string) =
try
let html = downloadHtmlFromUrl url
let regexPattern = #"<title>Page not found</title>"
let matchResult = Regex.IsMatch(html, regexPattern)
matchResult
with :? System.Net.WebException -> false
One caveat: this program will return false if there is a WebException, no matter for what reason that exception being raised. If you want specifically to return false on 404 responses, you'll have to look closer at the WebException:
let noSuchURL (url: string) =
try
let html = downloadHtmlFromUrl url
let regexPattern = #"<title>Page not found</title>"
let matchResult = Regex.IsMatch(html, regexPattern)
matchResult
with
:? System.Net.WebException as e
when e.Status = WebExceptionStatus.ProtocolError ||
e.Status = WebExceptionStatus.NameResolutionFailure
-> false
For more on exceptions in F#, take a look at https://msdn.microsoft.com/en-us/library/dd233194.aspx.
Sorry, but I am new to Scala. I have read about Futures and Akka, however I still have issue returning a string for my method.
I have a method getAuthString which should return Authentication String(or Token).
I have used spray Jsonsupport and I can print the result
def getToken(url: String, username: String , password: String) = Future[String]{
import MyJsonProtocol._
import spray.httpx.SprayJsonSupport._
val pipeline: HttpRequest => Future[AuthTokenResult[Entry]] = (addCredentials(BasicHttpCredentials(username, password))
~> sendReceive
~> unmarshal[AuthTokenResult[Entry]]
)
val myfutureResponse: Future[AuthTokenResult[Entry]] = pipeline(Get(url))
myfutureResponse onComplete {
case Success(AuthTokenResult(Entry(Content(authString)):: _)) => println(authString)
case Failure(error) => println("An error has occured: " + error.getMessage)
}
this unmarshal the json and print the desired authString. However, printing is no good to me. I know onComplete returns unit. I want to return authString so that I can use it somewhere else with another request. I think I will have to use flatmap or map, but I am not sure how. I need my method to return authString or error.
You don't want to return a String, you want to return a Future[String] - once something is async the only way to make it not async is to block, and that's (usually) a waste, making the whole async-ness pointless.
I'm not sure why you're wrapping the whole thing in a Future either - the trivial bits of computation can happen on their own, there's little value in forcing them onto a separate thread. So you want something like:
def getToken(url: String, ...): Future[String] = {
...
val myFutureResponse: Future[AuthTokenResult[Entry]] = ...
myFutureResponse map {
case AuthTokenResult(Entry(Content(authString))::_) => authString
}
}
So you use map to transform a Future into another Future with a computation. This will "pass through" errors, but you can use something like recover or recoverWith if you want to handle them in a particular way.
Then when you want to use your Future[String] in a Spray route, you can use the onSuccess or onComplete directives:
val myRoute = (path("/somewhere") & parameter("authData") {
authData =>
onSuccess(getToken(authData)) {
authToken =>
complete("Authed as " + authToken)
}
}
This will use the Future in a proper async, reactive way, without blocking.
Swift programming book says,
By returning a tuple with two distinct values, each of a different
type, the function provides more useful information about its outcome
than if it could only return a single value of a single type.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/gb/jEUH0.l
I searched on internet but couldn't find any examples of it.So I tried myself like example below, but if you've got better please let me know.. Thanks in advance.
var statusCode = 404
var statusMessage = "No Site"
let http404 = ( sCode : statusCode , sMessage : statusMessage)
func responseFromWS (foo : Int, bar : String) -> (param1 : Int, param2 : String)
{
statusCode = foo
statusMessage = bar
let httpProtocol = ( statusCode , statusMessage)
return httpProtocol
}
responseFromWS(500, "Internal Server Error")
In other languages (including objective c) you can return one value only (of any type), but in some cases you might need to return more than one value.
The pattern usually applied in those cases is to pass references to variables to the function for all additional return values - a typical case is a reference to a NSError * variable, which the function either sets to nil if no error occurs, or to an instance of NSError in case of error.
Such problem is elegantly solved in swift using multiple return values packed in a tuple.
The way you are using this features seems correct, but what's wrong is defining the statusCode and statusMessage variables outside the function scope:
func responseFromWS (foo : Int, bar : String) -> (code: Int, message: String)
{
let statusCode: Int = foo
let statusMessage: String = bar
return (code: statusCode, message: statusMessage)
// Note: you can also write as follows, because the parameter names are defined in the function signature
// return (statusCode, statusMessage)
}
You can use the return value in different ways. As a tuple:
let response = responseFromWS(500, "Internal Server Error")
// Using named parameters
println(response.code) // Prints 500
println(response.message) // Prints "Internal Server Error"
// using parameters by index
println(response.0) // Prints 500
println(response.1) // Prints "Internal Server Error"
As individual variables:
let (code, message) = responseFromWS(500, "Internal Server Error")
println(code)
println(message)
As a subset of individual variables (if you need only a subset of the returned values):
// Assign message only, discard code
let (_, message) = responseFromWS(500, "Internal Server Error")
println(message)
In addition to the uses mentioned by #Antonio, I have used them to return "pseudo-structs" where a function calculates several values, but the definition of a new struct type would really not be used anywhere else.
An example: when calculating true bearing and distance on the surface of the earth, one may choose to return some kind of polar coordinate struct, but the reverse azimuth (not a trivial relation in true geodesy) is also calculated as a by product. In implementations in other languages I have done this by defining a struct type to return the three doubles - but this struct type is never used except to call this function! Better to say
let (distance, azimuth, reverseAzimuth) = coordinate(vectorTo: otherCoordinate)
than having your future self look up the definition of and then unpack some obscure struct.