I'm using the Forecast.io C# weather library, which provides several classes for weather forecasts over different time periods (e.g., hourly, daily, "right now"), which all contain similar data.
I'm trying to write functions that will accept any of these classes, but I'm getting some compile errors (below) that I don't understand:
For the FS0001 errors, Why is the getTemp function not satisfied with an object that matches the Forecast union type? Why does it expect a different parameter type in each case?
For the FS0019 error, what constructor is it referring to? Why is it expecting me to supply an argument to this constructor?
Here is some example code:
open ForecastIO
type Forecast =
| Currently of ForecastIO.Currently
| HourForecast of ForecastIO.HourForecast
let getTemp forecast =
match forecast with
| Currently -> forecast.temperature
| HourForecast -> forecast.temperature
|> float
let forecastForDate date =
let request = new ForecastIORequest("api_key", 35.780556f, -78.638889f, date, Unit.us);
request.Get ()
let test () =
let baseDate = System.DateTime.Parse "2014-06-12 22:00:00"
let forecast = forecastForDate baseDate
forecast
|> (fun r -> r.currently)
|> getTemp
|> printfn "%f"
forecast
|> (fun r -> r.hourly.data.[0])
|> getTemp
|> printfn "%f"
test ()
And here is my compiler output:
/tmp/forecast.io/test2.fs(9,15): error FS0019: This constructor is applied to 0 argument(s) but expects 1
/tmp/forecast.io/test2.fs(23,12): error FS0001: Type mismatch. Expecting a
Currently -> 'a
but given a
Forecast -> float
The type 'Currently' does not match the type 'Forecast'
/tmp/forecast.io/test2.fs(28,12): error FS0001: Type mismatch. Expecting a
HourForecast -> 'a
but given a
Forecast -> float
The type 'HourForecast' does not match the type 'Forecast'
There are a few different issues. One is as #mydogisbox mentions - if you DU cases have fields, you are required to consider them in your pattern matching, either ignoring them with _ or capturing into some identifier.
The main issue, though, is that you are not creating an instance of your DU anywhere. The .currently property of your forecast object might be of type ForecastIO.Currently, and .hourly.data.[0] of type ForecastIO.HourForecast, but that doesn't mean you can take those values and treat them like cases from your Forecast DU. You need to explicitly construct the case you want:
forecast
|> (fun r -> Currently(r.currently))
|> getTemp
|> printfn "%f"
forecast
|> (fun r -> HourForecast(r.hourly.data.[0]))
|> getTemp
|> printfn "%f"
If you don't want to define a DU and instead really just want to pattern match against the type of the input, you can do something like what #Jan suggests, and not even have to define a DU:
let getTemp forecast =
match forecast with
| :? ForecastIO.Currently as c -> c.temperature
| :? ForecastIO.HourForecast as h -> h.temperature
|> float
When decomposing DUs you need to specify the name of the constructor parameters (i.e. the parameters needed to construct that DU case). In this case you don't want to use them so you can just blank them out like this:
let getTemp forecast =
match forecast with
| Currently _ -> forecast.temperature
| HourForecast _ -> forecast.temperature
|> float
Works the following better?
let getTemp forecast =
match forecast with
| :? Currently as c -> c.temperature
| :? HourForecast as h -> h.temperature
|> float
Related
I have the following Discriminated Union (DU) declaration:
type Book =
| Dictionary of string[]
| Novel of int[]
| Comics of bool[]
An example:
let x = Dictionary [|"a"; "b"|]
How can I extract the length of the array inside without doing pattern matching and without caring about the data type of the array (in this case: string, int, bool). Note: I have no control over the DU declaration; as a result, I can't write new member method within Book, like getArrayLength()
Of course, we can do it in some way as followed:
match x with
| Dictionary (x: _[]) -> x |> Array.length
| Novel (x: _[]) -> x |> Array.length
| Comics (x: _[]) -> x |> Array.length
But typing x |> Array.length a lot is incovenient. This is a simple example, but we can think of a general problem:
type Animal =
| Dog of DogClass
| Cat of CatClass
| Cow of CowClass
...
... and DogClass, CatClass, etc. may share something. We want to get that shared thing. E.g. those classes inherit from AnimalClass, within which there is countLegs() method. Suppsed there are many animals, pattern matching for all of them while the code block after -> is almost the same. I love the principle DRY (Don't Repeat Yourself).
Is there any convenient way to tackle such problem?
==
EDITED 21.10.2019
I was also looking for some syntax like:
let numEles =
match x with
| _ (arr: _[]) -> x |> Array.Length
| _ -> failwith "No identifiers with fields as Array."
let numLegs =
match anAnimall with
| _ (animal: ?> Animal) -> animal.countLegs()
| _ -> failwith "Can't count legs because of not being an animal."
I think this still follows the spirit of matching, but seem like this approach is not supported.
Realistically, there's no getting around pattern matching here. DUs were, in a way, built for it. Since you don't control the type, you can always add a type extension:
type Book with
member this.Length =
match this with
| Dictionary d -> d.Length
| Novel n -> n.Length
| Comics c -> c.Length
let x = Dictionary [|"a"; "b"|]
printfn "%d" x.Length // Prints 2
Though it's also equally valid to define a Book module with a length function on it if you prefer that:
module Book =
let length b =
match b with
| Dictionary d -> d.Length
| Novel n -> n.Length
| Comics c -> c.Length
let x = Dictionary [|"a"; "b"|]
printfn "%d" (x |> Book.length) // prints 2
But you'll need to write a pattern match expression on the Book type at least once. The fact that every case is made up of data that all has the same property doesn't really help the fact that you need to still identify every case individually.
I m trying to filter a mixed data type for a specific type, say float (ideally this would be dynamic though)
here my example:
let testobj = [8.0 , 1.0, "bla" ; 8.0 , 1.0, "bla"]
let testfun data = data |> List.filter (fun a ->
match a.GetType() with
| float -> a
| _ -> 0.0)
now this should return [8.0 , 1.0, 0.0 ; 8.0 , 1.0, 0.0] for testobj but I m gettting an error that the function is of type bool
This isn't what you want to do.
Seriously.
F# wants lists to be homogeneous and your list is not homogeneous. float and string don't share a common base class so you're not going to get a list from it.
What F# wants you to do is to use a discriminated union for this. So if you have this type:
type Composite =
| Num of float
| Str of string
you can define your list like this:
let data = [ Num(8.0); Num(1.0); Str("bla"); Num(8.0); Num(1.0); Str("bla") ]
and from there you can pattern match on the types and your function looks like this:
let testfun d = d |> List.map (fun a ->
match a with
| Num x -> a
| _ -> Num(0.0) )
data|> testfun |> printfn "%A"
And the output will be:
[Num 8.0; Num 1.0; Num 0.0; Num 8.0 ; Num 1.0 ; Num 0.0;]
If you want floats in the end and not Composites, do this:
let testfun1 d = d |> List.map (fun a ->
match a with
| Num x -> x
| _ -> 0.0 )
which sheds the composite type. And everything (and I mean everything) in that code is type strong and type-safe.
From a real-world maintenance point of view, I would eschew the _ case in the matches and instead use all my types, reasoning that if I extend Composite to include another type I would want the compiler to scream at me and look at each function that uses it rather than silently assuming that 0.0 or Num(0.0) is really what I wanted.
For example, if I added integers to that type, this would do exactly the wrong thing if I wanted to sum the contents of a list of composites.
Given that you're stuck/hell-bent on a weakly-typed data set, then you want something like this:
let testfun2 d = d |> Array.map (fun (a:Object) ->
match a with
| :? float as x -> x
| _ -> 0.0
)
let data:Object[] = [|8.0; 1.0; "bla"; 8.0; 1.0; "bla"|]
data |> testfun2 |> printfn "%A"
which will print what you expect. Note that I'm using proper Array syntax and not list syntax.
However this is feeling really wonky for F#. See how I have to adorn a and d with types? In my previous code, the language can figure it all out. If I don't adorn either, I get compiler errors because we're really going against the grain of the type system.
If I were you, I would be inclined to do something like this first:
let recast d = d |> Array.map (fun (a:Object) ->
match a with
| :? float as x -> Num x
| :? string as x -> Str x
| _ -> raise (ArgumentException("that was unexpected: " + a.GetType().Name))
)
which turns this into an Array of Composite which is now type strong. If you tack on |> Array.toList after the Array.map, you get a list (if you want that).
I need a function that could take an arbitrary number of arguments, each could be either of type 'T or seq<'T>. Inside the function I need to process it as a single seq<'T> with all inputs combined in the same order as they sere supplied.
The obvious way was to have something like:
module Test =
let flatten ([<ParamArray>] args) =
let flat = seq {
for a in args do
match box a with
| :? int as x -> yield x
| :? seq<int> as sq ->
for s in sq do
yield s
| _ -> failwith "wrong input type"
}
flat // this should be seq<int>
but I cannot make it work in FSI even with the simplest case
let fl = Test.flatten 1;;
----------------------^
...: error FS0001: The type 'int' is not compatible with the type 'seq<'a>'
What is wrong here and how to get it work as needed? Probably this could be done in some completely different way?
From msdn :
In F#, parameter arrays can only be defined in methods. They cannot be
used in standalone functions or functions that are defined in
modules.
So instead of a module, declare a type with a static method.
open System
type Test() =
static member flatten ([<ParamArray>] args: obj[]) =
let flat = seq {
for a in args do
match box a with
| :? int as x -> yield x
| :? seq<int> as sq ->
for s in sq do
yield s
| _ -> failwith "wrong input type"
}
flat
If you have other let bindings you can still declare a module with the same name.
Also note that in the second guard of the match you can avoid the for loop by doing:
| :? seq<int> as sq -> yield! sq
And box is not required.
I'd like to check that a value is of a particular case of a discriminated union, without having to also check any included data. My motivation is to only test one thing with each unit test.
An example is as follows (the last two lines give compilation errors):
module MyState
open NUnit.Framework
open FsUnit
type MyState =
| StateOne of int
| StateTwo of int
let increment state =
match state with
| StateOne n when n = 10 -> StateTwo 0
| StateOne n -> StateOne (n + 1)
| StateTwo n -> StateTwo (n + 1)
[<Test>]
let ``incrementing StateOne 10 produces a StateTwo`` ()=
let state = StateOne 10
(increment state) |> should equal (StateTwo 0) // works fine
(increment state) |> should equal (StateTwo _) // I would like to write this...
(increment state) |> should be instanceOfType<StateTwo> // ...or this
Can this be done in FsUnit?
I'm aware of this answer but would prefer not to have to write matching functions for each case (in my real code there are far more than two).
If you don't mind using reflections, the isUnionCase function from this answer could be handy:
increment state
|> isUnionCase <# StateTwo #>
|> should equal true
Note that it's a bit verbose because you need a function call before comparing values.
A similar but lighter approach could be comparison of tags:
// Copy from https://stackoverflow.com/a/3365084
let getTag (a:'a) =
let (uc,_) = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(a, typeof<'a>)
uc.Name
increment state
|> getTag
|> should equal "StateTwo"
Beware that this is not type-safe and you can easily misspell a union case name.
What I would do is to create a similar DUs for comparison purpose:
type MyStateCase =
| StateOneCase
| StateTwoCase
let categorize = function
| StateOne _ -> StateOneCase
| StateTwo _ -> StateTwoCase
In this way, you define categorize once and use it multiple times.
increment state
|> categorize
|> should equal StateTwoCase
It appears FSUnit doesn't (or can't, I'm not sure) directly support this use case.
The next best thing I've found is to declare a TestResult type like the following and use a match to reduce the result to this type.
type TestResult =
| Pass
| Fail of obj
Here is the reducing match
let testResult =
match result with
| OptionA(_) -> Pass
| other -> Fail(other)
Now you can just use should equal to ensure the correct result.
testResult |> should equal Pass
The benefits of this solution are strong typing but more importantly in the failure case you can see what the invalid result was.
It doesn't look very elegant, but you can extract type from a value of state:
let instanceOfState (state: 'a) =
instanceOfType<'a>
And then use it in the test:
(increment state) |> should be (instanceOfState <| StateTwo 88)
EDIT
Yes, unfortunately the type is always MyState. Looks like pattern matching or ugly reflection are inevitable.
What if FsUnit already supports an assertion against a specific union case, albeit one restricted to values of the type Microsoft.FSharp.Core.Choice<_,...,_>?
Let's leverage this with a multi-case active pattern, which uses Reflection to check against the union case name.
open System.Reflection
open Microsoft.FSharp.Reflection
let (|Pass|Fail|) name (x : obj) =
let t = x.GetType()
if FSharpType.IsUnion t &&
t.InvokeMember("Is" + name,
BindingFlags.GetProperty, null, x, null )
|> unbox then Pass
else Fail x
Should be working now:
increment state
|> (|Pass|Fail|) "StateTwo"
|> should be (choice 1)
Suppose I have the following code:
type Vehicle =
| Car of string * int
| Bike of string
let xs = [ Car("family", 8); Bike("racing"); Car("sports", 2); Bike("chopper") ]
I can filter above list using incomplete pattern matching in an imperative for loop like:
> for Car(kind, _) in xs do
> printfn "found %s" kind;;
found family
found sports
val it : unit = ()
but it will cause a:warning FS0025: Incomplete pattern matches on this expression. For example, the value 'Bike (_)' may indicate a case not covered by the pattern(s). Unmatched elements will be ignored.
As the ignoring of unmatched elements is my intention, is there a possibility to get rid of this warning?
And is there a way to make this work with list-comprehensions without causing a MatchFailureException? e.g. something like that:
> [for Car(_, seats) in xs -> seats] |> List.sum;;
val it : int = 10
Two years ago, your code was valid and it was the standard way to do it. Then, the language has been cleaned up and the design decision was to favour the explicit syntax. For this reason, I think it's not a good idea to ignore the warning.
The standard replacement for your code is:
for x in xs do
match x with
| Car(kind, _) -> printfn "found %s" kind
| _ -> ()
(you could also use high-order functions has in pad sample)
For the other one, List.sumBy would fit well:
xs |> List.sumBy (function Car(_, seats) -> seats | _ -> 0)
If you prefer to stick with comprehensions, this is the explicit syntax:
[for x in xs do
match x with
| Car(_, seats) -> yield seats
| _ -> ()
] |> List.sum
You can silence any warning via the #nowarn directive or --nowarn: compiler option (pass the warning number, here 25 as in FS0025).
But more generally, no, the best thing is to explicitly filter, as in the other answer (e.g. with choose).
To explicitly state that you want to ignore unmatched cases, you can use List.choose and return None for those unmatched elements. Your codes could be written in a more idomatic way as follows:
let _ = xs |> List.choose (function | Car(kind, _) -> Some kind
| _ -> None)
|> List.iter (printfn "found %s")
let sum = xs |> List.choose (function | Car(_, seats)-> Some seats
| _ -> None)
|> List.sum