Given:
C = case A of
undefined ->
"";
Value ->
Value
end
How would I express this as a single line?
Besides the obvious (putting all of that code on one line), you could make a helper function like this:
with_default(undefined, D) -> D;
with_default(X, _) -> X.
with_default(X) -> with_default(X, "").
Related
I would like to encode a Maybe String to string if it has a concrete value, or null if it's Nothing.
At the moment, I use a helper function encodeOptionalString myStr to get the desired effect. I was wondering if there's a more Elm-like way of doing this. I really like the API of elm-json-decode-pipeline that allows me to write Decode.nullable Decode.string for decoding.
encodeOptionalString : Maybe String -> Encode.Value
encodeOptionalString s =
case s of
Just s_ ->
Encode.string s_
Nothing ->
Encode.null
You could generalize this into an encodeNullable function yourself:
encodeNullable : (value -> Encode.Value) -> Maybe value -> Encode.Value
encodeNullable valueEncoder maybeValue =
case maybeValue of
Just value ->
valueEncoder value
Nothing ->
Encode.null
Or if you want a slightly shorter ad hoc expression:
maybeString
|> Maybe.map Encode.string
|> Maybe.withDefault Encode.null
The package elm-community/json-extra has exactly the method you desire.
maybe : (a -> Value) -> Maybe a -> Value
Encode a Maybe value. If the value is Nothing it will be encoded as null
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
I'm currently writing a web-based vocabulary trainer in Elm. This requires sorting a list of words by a custom comparator.
The type I want to sort is:
type alias Word =
{ id: Int
, sourceWord: String
, targetWord: String
, numTries: Int
, numCorrect: Int
, createdAt: Maybe Date -- might be empty, therefore wrapped in Maybe
, lastAskedAt: Maybe Date -- might be empty, therefore wrapped in Maybe
}
type alias WordList = List (Word)
My rules for comparison are (in descending order of importance):
number of correct guesses (asc)
number overall guesses (desc)
when word was last asked (asc)
when word was added (desc)
The best approach I could come up with is this:
compareWords: Word -> Word -> Basics.Order
compareWords w1 w2 =
let
dateToComparable d = Date.Format.format "%Y-%m-%d" d
orderNumCorrect = compare w1.numCorrect w2.numCorrect
orderNumTries = compare w2.numTries w1.numTries -- switch ordering to sort descending
orderLastAskedAt = case (w1.lastAskedAt, w2.lastAskedAt) of
(Just a1, Just a2) -> compare (dateToComparable a1) (dateToComparable a2)
(Nothing, Just _) -> Basics.LT
(Just _, Nothing) -> Basics.GT
(Nothing, Nothing) -> Basics.EQ
orderCreatedAt = case (w2.createdAt, w1.createdAt) of -- switch ordering to sort descending
(Just a1, Just a2) -> compare (dateToComparable a1) (dateToComparable a2)
(Nothing, Just _) -> Basics.LT
(Just _, Nothing) -> Basics.GT
(Nothing, Nothing) -> Basics.EQ
in
case orderNumCorrect of
Basics.EQ -> case orderNumTries of
Basics.EQ -> case orderLastAskedAt of
Basics.EQ -> orderCreatedAt
_ -> orderLastAskedAt
_ -> orderNumTries
_ -> orderNumCorrect
which I don't like for a number of reasons:
it's ugly as hell
it requires me to use Date.Format.format (from mgold/elm-date-format) to compare Date values (since Date apparently is not comparable)
Is there a more elegant / Elm-ish way to achieve what I want?
Update + solution
As #"Zimm i48" suggested in their most excellent answer, here's a much shorter version that uses the elm-ordering package:
dateToComparable : Maybe Date -> Time
dateToComparable =
Maybe.map Date.toTime >> Maybe.withDefault 0
compareWords : Ordering Word
compareWords =
Ordering.byField .numCorrect
|> Ordering.breakTiesWith (Ordering.byField (.numTries >> negate))
|> Ordering.breakTiesWith (Ordering.byField (.lastAskedAt >> dateToComparable))
|> Ordering.breakTiesWith
(Ordering.byField (.createdAt >> dateToComparable >> negate))
A more Elm-ish way of doing this kind of things is compositionally, thanks to the |> operator.
The elm-ordering library provides the primitives that you need to do this kind of things, especially the Ordering.byField and Ordering.breakTiesWith functions.
As for the dates, my advice would be to use Date.toTime (the resulting values are comparable).
Bonus: full implementation of your ordering function available for testing here: https://runelm.io/c/xoz. You can see it's much simpler and more readable than yours...
In F# can you pattern match on a function signature. I want to decorate a number of functions with a function that measures the execution of the function and calls out to statsd. The current function I have is:
let WrapFunctionWithPrefix(metrics:Metric.Client.IRecorder, functionToWrap, prefix) =
let metricsIdentifier = (sprintf "%s.%s" prefix Environment.MachineName)
using (metrics.StartTimer(metricsIdentifier)) ( fun metrics -> functionToWrap)
As you can see above, the prefix will vary, and in our application this will vary per function definition. So rather than having to pass in the measure prefix every time I want to do something like the following:
let WrapFunction metrics afunc =
match afunc with
| :? (int -> int) -> WrapFunctionWithPrefix(metrics, afunc, "My function 1")
| :? (string -> string) -> WrapFunctionWithPrefix(metrics, afunc, "My function 2")
| _ -> failwith "Unknown function def"
Is there any way of pattern matching based on the function signature in F#?
Any help appreciated.
Billy
Would it be possible to declare the cases as a DU?
type MyFunctions =
| Intish of int -> int
| Stringish of string -> string
let WrapFunction metrics afunc =
match box afunc with
| :? (int -> int) -> WrapFunctionWithPrefix(metrics, afunc, "My function 1")
| :? (string -> string) -> WrapFunctionWithPrefix(metrics, afunc, "My function 2")
| _ -> failwith "Unknown function def"
will work for your pattern match. You normally end up having to box unknown types before trying to cast them, as :? doesn't like being used on value types.
I'm not totally sure how your using statement will interact with the function you return though. I think it will dispose metrics and return the function immediately, which is probably not what you want.
get_currency() ->
URL = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.xchange%20where%20pair%20in%20(%22GBPEUR%22)&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys",
{Result, Info} = httpc:request(URL),
case Result of
error ->
{Result, Info};
ok ->
{{_Protocol, Code, _CodeStr}, _Attrs, WebData} = Info,
WebData
end.
extract_text(Content) ->
Item = hd(Content),
case element(1, Item) of
xmlText -> Item#xmlText.value;
_ -> ""
end.
analyze_info(WebData) ->
ToFind = [rate],
Parsed = element(1, xmerl_scan:string(WebData)),
Children = Parsed#xmlElement.content,
ElementList = [{El#xmlElement.name, extract_text(El#xmlElement.content)} || El <- Children, element(1, El) == xmlElement],
lists:map(fun(Item) -> lists:keyfind(Item, 1, ElementList) end, ToFind).
the above is the code im using to try to extract the contents of the tag from the url http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.xchange%20where%20pair%20in%20(%22GBPEUR%22)&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys.
here is what i do in the shell.
inets:start().
XML = scrapetest:get_currency().
scrapetest:analyze_info(XML).
and the return i get is simply "false". Im not sure what im doing wrong.
Just add some logs to your code.
Eg. adding io:format("~p~n", [ElementList]), - will show you that ElementList contains only result tag, and you should go one level deeper in your list comprehension to get tag named rate
This is common advice.
In your case, seems that better decision is recursive find function (if you want to write some code)
or use some batteries, like xmerl_xpath
Just example for another analyze_info :
analyze_info(WebData) ->
Parsed = element(1, xmerl_scan:string(WebData)),
xmerl_xpath:string("//Rate/text()", Parsed).
This will return:
[{xmlText,[{'Rate',2},{rate,1},{results,1},{query,1}],
1,[],"1.1813",text}]