I am parsing http responses from my server (Phoenix 1.3) on my Elm 0.18 frontend.
The response looks like this:
error: BadStatus { status = { code = 422, message = "Unprocessable Entity" }, headers = Dict.fromList [("cache-control","max-age=0, private, must-revalidate"),("content-type","application/json; charset=utf-8")], url = "http://localhost:4000/api/v1/sessions", body = "{\"error\":\"No user could be found\"}" }
I would like to extract the three-digit HTTP code as a String....in this case, "422".
What is the best way to parse this in Elm? I am using a very hacky method and I'd like to know what tools are best applied here.
errorCode : String -> String
errorCode =
error
|> Debug.log "error"
|> toString
|> String.split "code = "
|> List.drop 1
|> String.join ""
|> String.split ","
|> List.take 1
|> String.join ""
|> Debug.log "Error"
It looks like you have an Error from the elm-lang/http package. The string you quoted is just how Errors are rendered as strings in the console / debugger, I believe -- I don't think there's any parsing to be done, as such.
In other words, I think the function you want doesn't operate on Strings at all:
errorCode : Error -> Maybe String
errorCode err =
case err of
BadStatus response ->
Just response.status.code
_ ->
Nothing
(The elm compiler should of course tell you whether your String -> String or my Error -> Maybe String signature is correct.)
Related
I need to request data from several URLs and then use the results.
I am using plain Fable 3 with the Fable-Promise and Fable-Fetch libraries.
I have worked out how to fetch from multiple URLs and combine the results into a single Promise that I can then use to update the UI (the multiple results need to be drawn only once).
But if one of the fetch errors then the whole thing falls over. Ideally I'd like to use tryFetch and then propagate the Result<TermData, None | Exception> but nothing I do seems to compile.
How exactly do I use tryFetch and then unwrap the result with a second let! in the CE? (The comments explain more)
module App
open Browser.Dom
open App
open System.Collections.Generic
open System.Text.RegularExpressions
open Fetch
open System
type TermData =
abstract counts : int []
abstract scores : int []
abstract term : string
abstract allWords : bool
type QueryTerm =
{ mutable Term: string
mutable AllWords: bool }
let loadSingleSeries (term: QueryTerm) =
promise {
let url =
$"/api/plot/{term.Term}?allWords={term.AllWords}"
// Works but doesn't handle errors.
let! plotData = fetch url [] // type of plotData: Response
// let plotDataResult = tryFetch url []
// This ideally becomes Promise<Result<TermData, None>>
// let unwrapped = match plotDataResult with
// | Ok res -> Ok (res.json<TermData>()) // type: Promise<TermData>
// | Error err -> ??? // tried Error (Promise.create(fun resolve reject -> resolve None)) among others
let! result = plotData.json<TermData>() // type of result: TermData
return result
}
let dataArrays =
parsed // type Dictionary<int, TermData>
|> Seq.map (fun term -> loadSingleSeries term.Value)
|> Promise.Parallel
|> Promise.map (fun allData -> console.log(allData))
// Here we will handle None when we have it
I don't have much Fable experience, but if I understand your question correctly, I think the following should work:
let loadSingleSeries (term: QueryTerm) =
promise {
let url =
$"/api/plot/{term.Term}?allWords={term.AllWords}"
let! plotDataResult = tryFetch url []
match plotDataResult with
| Ok resp ->
let! termData = resp.json<TermData>()
return Ok termData
| Error ex ->
return Error ex
}
The idea here is that if you get an error, you simply propagate that error in the new Result value. This returns a Promise<Result<TermData, Exception>>, as you requested.
Update: Fixed return type using a second let!.
I haven't run this code but looking at the docs it looks like you need to use Promise.catch
let loadSingleSeries (term: QueryTerm) =
promise {
let url =
$"/api/plot/{term.Term}?allWords={term.AllWords}"
let! plotDataResult =
fetch url []
|> Promise.map Ok // Wraps the "happy path" in a Result.Ok
|> Promise.catch (fun err ->
//handle the error
Error err)
return
match plotDataResult with
| Ok res -> ...
| Error err -> ...
}
I ended up having to use the pipeline rather than CE approach for this as follows:
let loadSingleSeries (term: QueryTerm) =
let url =
$"/api/plot/{term.Term}?allWords={term.AllWords}"
let resultPromise =
fetch url []
|> Promise.bind (fun response ->
let arr = response.json<TermData> ()
arr)
|> Promise.map (Ok)
|> Promise.catch (Error)
resultPromise
The key was using Promise.bind to convert the first promise to get the Response to the promise of Promise<TermData>. The map and catch then convert to a Promise<Result<TermData, exn>>.
I'm trying to write a generic json decode function in fable. It seems to compile in FSharp but I get the error message for this code:
[using the Thoth.Json library and the Fetch library from Fable.PowerPack]
let autoDecoder<'a> (json:string) (value:obj) : Result<'a, Thoth.Json.Decode.DecoderError> =
let tryDecode = Thoth.Json.Decode.Auto.fromString<'a>(json)
let getDecoderError (str:string) : Thoth.Json.Decode.DecoderError = ( "Auto decode Error", Thoth.Json.Decode.FailMessage str)
Result.mapError getDecoderError tryDecode
error FABLE: Cannot get type info of generic parameter, please inline or inject a type resolver
I'm not sure how to fix this and haven't been able to find anything on google.
I want to be able to call the function like this in my update function in Fable Elmish:
let update (msg:Msg) (model:Model) =
match msg with
..
| OrStart ->
let getData() = Fetch.fetchAs<ResultResponse> "https://randomuser.me/api/" json.autoDecoder<ResultResponse> http.getHeaders
model, Cmd.ofPromise getData () LoadedTypedData FetchError
How can I get fable to compile the autoDecoder<'a> function while keeping it generic?
Thanks
I think Fable is telling you to use inline like this:
let inline autoDecoder<'a> (json:string) (value:obj) : Result<'a, Thoth.Json.Decode.DecoderError> =
let tryDecode = Thoth.Json.Decode.Auto.fromString<'a>(json)
let getDecoderError (str:string) : Thoth.Json.Decode.DecoderError = ( "Auto decode Error", Thoth.Json.Decode.FailMessage str)
Result.mapError getDecoderError tryDecode
That is because generic functions just like inline functions need to be instantiated for each call.
BTW, the value parameter is not being used.
You can also streamline the code like this:
let inline autoDecoder<'a> (json:string) : Result<'a, Thoth.Json.Decode.DecoderError> =
Thoth.Json.Decode.Auto.fromString<'a> json
|> Result.mapError (fun (str:string) -> "Auto decode Error", Thoth.Json.Decode.FailMessage str)
I'm new to fable and I wasn't able to get it working, the fable compiler does not allow the auto decode without a specified type - fails here:
Thoth.Json.Decode.Auto.fromString<'a>(str, true)
But for anyone struggling with the fetch api in fable, this can be done with not too much boilerplate code. I couldn't get the promise to be generic, but a type specific implementation like getCustomers is quite succinct and I ended up doing something like this:
type Msg =
| Start
| LoadedCustomerData of Result<QueryDataForJson, string>
..
let getCustomers () = promise {
let! response = Fetch.fetch "http://localhost:5000/spa/api/customers" http.getHeaders
let! text = response.text()
return Thoth.Json.Decode.Auto.fromString<QueryDataForJson>(text, true)
}
..
let update (msg:Msg) (model:Model) =
match msg with
| Start ->
model, Cmd.ofPromise getCustomers () LoadedCustomerData FetchError
| LoadedCustomerData resp ->
match resp with
| Ok qdj -> { model with gridData= queryDataFromJson qdj; message= "Loaded Customer Data" }, Cmd.none
| Error str -> { model with message = str }, Cmd.none
Previously had a very compact and comprehensive answer for my question.
I had it working for my custom type but now due to some reason I had to change it to string type which is now causing type mismatch errors.
module AsyncResult =
let bind (binder : 'a -> Async<Result<'b, 'c>>) (asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
async {
let! result = asyncFun
match result with
| Error e -> return Error e
| Ok x -> return! binder x
}
let compose (f : 'a -> Async<Result<'b, 'e>>) (g : 'b -> Async<Result<'c, 'e>>) = fun x -> bind g (f x)
let (>>=) a f = bind f a
let (>=>) f g = compose f g
Railway Oriented functions
let create (json: string) : Async<Result<string, Error>> =
let url = "http://api.example.com"
let request = WebRequest.CreateHttp(Uri url)
request.Method <- "GET"
async {
try
// http call
return Ok "result"
with :? WebException as e ->
return Error {Code = 500; Message = "Internal Server Error"}
}
test
type mismatch error for the AsyncResult.bind line
let chain = create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
match chain "initial data" |> Async.RunSynchronously with
| Ok data -> Assert.IsTrue(true)
| Error error -> Assert.IsTrue(false)
Error details:
EntityTests.fs(101, 25): [FS0001] Type mismatch. Expecting a '(string -> string -> Async<Result<string,Error>>) -> 'a' but given a 'Async<Result<'b,'c>> -> Async<Result<'d,'c>>' The type 'string -> string -> Async<Result<string,Error>>' does not match the type 'Async<Result<'a,'b>>'.
EntityTests.fs(101, 25): [FS0001] Type mismatch. Expecting a '(string -> string -> Async<Result<string,Error>>) -> 'a' but given a 'Async<Result<string,'b>> -> Async<Result<string,'b>>' The type 'string -> string -> Async<Result<string,Error>>' does not match the type 'Async<Result<string,'a>>'.
Edit
Curried or partial application
In context of above example, is it the problem with curried functions? for instance if create function has this signature.
let create (token: string) (json: string) : Async<Result<string, Error>> =
and then later build chain with curried function
let chain = create "token" >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
Edit 2
Is there a problem with following case?
signature
let create (token: Token) (entityName: string) (entityType: string) (publicationId: string) : Async<Result<string, Error>> =
test
let chain = create token >> AsyncResult.bind ( fun (result: string) -> async {return Ok "more results"} )
match chain "test" "article" "pubid" |> Async.RunSynchronously with
Update: At the front of the answer, even, since your edit 2 changes everything.
In your edit 2, you have finally revealed your actual code, and your problem is very simple: you're misunderstanding how the types work in a curried F# function.
When your create function looked like let create (json: string) = ..., it was a function of one parameter. It took a string, and returned a result type (in this case, Async<Result<string, Error>>). So the function signature was string -> Async<Result<string, Error>>.
But the create function you've just shown us is a different type entirely. It takes four parameters (one Token and three strings), not one. That means its signature is:
Token -> string -> string -> string -> Async<Result<string, Error>>
Remember how currying works: any function of multiple parameters can be thought of as a series of functions of one parameter, which return the "next" function in that chain. E.g., let add3 a b c = a + b + c is of type int -> int -> int -> int; this means that add3 1 returns a function that's equivalent to let add2 b c = 1 + b + c. And so on.
Now, keeping currying in mind, look at your function type. When you pass a single Token value to it as you do in your example (where it's called as create token, you get a function of type:
string -> string -> string -> Async<Result<string, Error>>
This is a function that takes a string, which returns another function that takes a string, which returns a third function which takes a string and returns an Async<Result<whatever>>. Now compare that to the type of the binder parameter in your bind function:
(binder : 'a -> Async<Result<'b, 'c>>)
Here, 'a is string, so is 'b, and 'c is Error. So when the generic bind function is applied to your specific case, it's looking for a function of type string -> Async<Result<'b, 'c>>. But you're giving it a function of type string -> string -> string -> Async<Result<string, Error>>. Those two function types are not the same!
That's the fundamental cause of your type error. You're trying to apply a function that returns a function that returns function that returns a result of type X to a design pattern (the bind design pattern) that expects a function that returns a result of type X. What you need is the design pattern called apply. I have to leave quite soon so I don't have time to write you an explanation of how to use apply, but fortunately Scott Wlaschin has already written a good one. It covers a lot, not just "apply", but you'll find the details about apply in there as well. And that's the cause of your problem: you used bind when you needed to use apply.
Original answer follows:
I don't yet know for a fact what's causing your problem, but I have a suspicion. But first, I want to comment that the parameter names for your AsyncResult.bind are wrong. Here's what you wrote:
let bind (binder : 'a -> Async<Result<'b, 'c>>)
(asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
(I moved the second parameter in line with the first parameter so it wouldn't scroll on Stack Overflow's smallish column size, but that would compile correctly if the types were right: since the two parameters are lined up vertically, F# would know that they are both belonging to the same "parent", in this case a function.)
Look at your second parameter. You've named it asyncFun, but there's no arrow in its type description. That's not a function, it's a value. A function would look like something -> somethingElse. You should name it something like asyncValue, not asyncFun. By naming it asyncFun, you're setting yourself up for confusion later.
Now for the answer to the question you asked. I think your problem is this line, where you've fallen afoul of the F# "offside rule":
let chain = create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
Note the position of the >> operator, which is to the left of its first operand. Yes, the F# syntax appears to allow that in most situations, but I suspect that if you simply change that function definition to the following, your code will work:
let chain =
create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
Or, better yet because it's good style to make the |> (and >>) operators line up with their first operand:
let chain =
create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
If you look carefully at the rules that Scott Wlaschin lays out in https://fsharpforfunandprofit.com/posts/fsharp-syntax/, you'll note that his examples where he shows exceptions to the "offside rule", he writes them like this:
let f g h = g // defines a new line at col 15
>> h // ">>" allowed to be outside the line
Note how the >> character is still to the right of the = in the function definition. I don't know exactly what the F# spec says about the combination of function definitions and the offside rule (Scott Wlaschin is great, but he's not the spec so he could be wrong, and I don't have time to look up the spec right now), but I've seen it do funny things that I didn't quite expect when I wrote functions with part of the function definition on the same line as the function, and the rest on the next line.
E.g., I once wrote something like this, which didn't work:
let f a = if a = 0 then
printfn "Zero"
else
printfn "Non-zero"
But then I changed it to this, which did work:
let f a =
if a = 0 then
printfn "Zero"
else
printfn "Non-zero"
I notice that in Snapshot's answer, he made your chain function be defined on a single line, and that worked for him. So I suspect that that's your problem.
Rule of thumb: If your function has anything after the = on the same line, make the function all on one line. If your function is going to be two lines, put nothing after the =. E.g.:
let f a b = a + b // This is fine
let g c d =
c * d // This is also fine
let h x y = x
+ y // This is asking for trouble
I would suspect that the error stems from a minor change in indentation since adding a single space to an FSharp program changes its meaning, the FSharp compiler than quickly reports phantom errors because it interprets the input differently. I just pasted it in and added bogus classes and removed some spaces and now it is working just fine.
module AsyncResult =
[<StructuralEquality; StructuralComparison>]
type Result<'T,'TError> =
| Ok of ResultValue:'T
| Error of ErrorValue:'TError
let bind (binder : 'a -> Async<Result<'b, 'c>>) (asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
async {
let! result = asyncFun
match result with
| Error e -> return Error e
| Ok x -> return! binder x
}
let compose (f : 'a -> Async<Result<'b, 'e>>) (g : 'b -> Async<Result<'c, 'e>>) = fun x -> bind g (f x)
let (>>=) a f = bind f a
let (>=>) f g = compose f g
open AsyncResult
open System.Net
type Assert =
static member IsTrue (conditional:bool) = System.Diagnostics.Debug.Assert(conditional)
type Error = {Code:int; Message:string}
[<EntryPoint>]
let main args =
let create (json: string) : Async<Result<string, Error>> =
let url = "http://api.example.com"
let request = WebRequest.CreateHttp(Uri url)
request.Method <- "GET"
async {
try
// http call
return Ok "result"
with :? WebException as e ->
return Error {Code = 500; Message = "Internal Server Error"}
}
let chain = create >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
match chain "initial data" |> Async.RunSynchronously with
| Ok data -> Assert.IsTrue(true)
| Error error -> Assert.IsTrue(false)
0
How do I retrieve a value from a generic?
Specifically, I am attempting the following:
// Test
let result = Validate goodInput;;
// How to access record??
let request = getRequest result
Here's the code:
type Result<'TSuccess,'TFailure> =
| Success of 'TSuccess
| Failure of 'TFailure
let bind nextFunction lastFunctionResult =
match lastFunctionResult with
| Success input -> nextFunction input
| Failure f -> Failure f
type Request = {name:string; email:string}
let validate1 input =
if input.name = "" then Failure "Name must not be blank"
else Success input
let validate2 input =
if input.name.Length > 50 then Failure "Name must not be longer than 50 chars"
else Success input
let validate3 input =
if input.email = "" then Failure "Email must not be blank"
else Success input;;
let Validate =
validate1
>> bind validate2
>> bind validate3;;
// Setup
let goodInput = {name="Alice"; email="abc#abc.com"}
let badInput = {name=""; email="abc#abc.com"};;
// I have no clue how to do this...
let getRequest = function
| "Alice", "abc#abc.com" -> {name="Scott"; email="xyz#xyz.com"}
| _ -> {name="none"; email="none"}
// Test
let result = Validate goodInput;;
// How to access record??
let request = getRequest result
printfn "%A" result
You mean how do you extract the record out of your result type? Through pattern matching, that's what you're already doing in bind.
let getRequest result =
match result with
| Success input -> input
| Failure msg -> failwithf "Invalid input: %s" msg
let result = Validate goodInput
let record = getRequest result
This will return the record or throw an exception. Up to you how you handle the success and failure cases once you have your Result - that could be throwing an exception, or turning it into option, or logging the message and returning a default etc.
This seems to be a frequently asked question: How do I get the value out of a monadic value? The correct answer, I believe, is Mu.
The monadic value is the value.
It's like asking, how do I get the value out of a list of integers, like [1;3;3;7]?
You don't; the list is the value.
Perhaps, then, you'd argue that lists aren't Discriminated Unions; they have no mutually exclusive cases, like the above Result<'TSuccess,'TFailure>. Consider, instead, a tree:
type Tree<'a> = Node of Tree<'a> list | Leaf of 'a
This is another Discriminated Union. Examples include:
let t1 = Leaf 42
let t2 = Node [Node []; Node[Leaf 1; Leaf 3]; Node[Leaf 3; Leaf 7]]
How do you get the value out of a tree? You don't; the tree is the value.
Like 'a option in F#, the above Result<'TSuccess,'TFailure> type (really, it's the Either monad) is deceptive, because it seems like there should only be one value: the success. The failure we don't like to think about (just like we don't like to think about None).
The type, however, doesn't work like that. The failure case is just as important as the success case. The Either monad is often used to model error handling, and the entire point of it is to have a type-safe way to deal with errors, instead of exceptions, which are nothing more than specialised, non-deterministic GOTO blocks.
This is the reason the Result<'TSuccess,'TFailure> type comes with bind, map, and lots of other goodies.
A monadic type is what Scott Wlaschin calls an 'elevated world'. While you work with the type, you're not supposed to pull data out of that world. Rather, you're supposed to elevate data and functions up to that world.
Going back to the above code, imagine that given a valid Request value, you'd like to send an email to that address. Therefore, you write the following (impure) function:
let send { name = name; email = email } =
// Send email using name and email
This function has the type Request -> unit. Notice that it's not elevated into the Either world. Still, you want to send the email if the request was valid, so you elevate the send method up to the Either world:
let map f = bind (fun x -> Success (f x))
let run = validate1 >> bind validate2 >> bind validate3 >> map send
The run function has the type Request -> Result<unit,string>, so used with goodInput and badInput, the results are the following:
> run goodInput;;
val it : Result<unit,string> = Success unit
> run badInput;;
val it : Result<unit,string> = Failure "Name must not be blank"
And then you probably ask: and how do I get the value out of that?
The answer to that question depends entirely on what you want to do with the value, but, imagine that you want to report the result of run back to the user. Displaying something to the user often involves some text, and you can easily convert a result to a string:
let reportOnRun = function
| Success () -> "Email was sent."
| Failure msg -> msg
This function has the type Result<unit,string> -> string, so you can use it to report on any result:
> run goodInput |> reportOnRun;;
val it : string = "Email was sent."
> run badInput |> reportOnRun;;
val it : string = "Name must not be blank"
In all cases, you get back a string that you can display to the user.
I have the following code which will return a seq of DownloadLink for these Urls that can be parsed.
type DownloadLink = { Url: string; Period: DateTime }
nodes |> Seq.map (fun n ->
let url = n.Attributes.["href"].Value
match url with
| Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
{ Url = url; Period = period }
| _ ->
printfn "Cannot parse %s" url // Error
)
However, I got the following error at the printfn. What's right way to implement it? Should I make it a list option first and then filter out these None items?
Error 1 Type mismatch. Expecting a
string -> DownloadLink
but given a
string -> unit
The type 'DownloadLink' does not match the type 'unit'
The basic problem is that if you have something like
match x with
|true -> A
|false -> B
the type of A and B must be the same.
There is actually a build in function that combines the map and filter using Some that you had though of - use Seq.choose like so
nodes |> Seq.choose (fun n ->
let url = n.Attributes.["href"].Value
match url with
| Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
Some ({ Url = url; Period = period })
| _ ->
printfn "Cannot parse %s" url // Error
None
)
Aside from Seq.choose, you can also nicely solve the problem using sequence expressions - where you can use yield to return result in one branch, but do not have to produce a value in another branch:
seq { for n in nodes do
let url = n.Attributes.["href"].Value
match url with
| Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
yield { Url = url; Period = period }
| _ ->
printfn "Cannot parse %s" url }
Aside, I would not recommend doing a side effect (printing) as part of your processing code. If you want to report errors, it might be better to return an option (or define a type which is either Success or Error of string) so that the error reporting is separated from processing.