I have read quite a few ressources on Unit of Measures (including the good Microsoft Doc and f# for fun and profit) but I still cannot make my code work.
[<Measure>] type USD
[<Measure>] type JPY
[<Measure>] type EUR
type FxUnit<[<Measure>] 'u,[<Measure>] 'v> = {UnitRate : float<'u/'v>}
let getFx1 u =
match u with
| "USD" -> {UnitRate = 1.0<USD/USD>}
| "EUR" -> {UnitRate = 1.0<USD/EUR>}
This way I get an error under:
1.0<USD/EUR>
I have tried thse:
let getFx1 u : (float<'u>) =
let getFx1 u : (float<'u/'v>) =
without much success. It seems that with a match, I can't return some type FxUnit with different UoM.
Any idea how I could fix this?
thanks.
The problem is that you're trying to define a function that takes a string and returns a value of type either FxUnit<USD,USD> or FxUnit<USD,EUR>. A function can't have two different return types, decided at runtime.
You could make FxUnit take two values of a discriminated union instead of measures:
type Currency =
| USD
| JPY
| EUR
type FxUnit = { UnitRate : float; From : Currency; To : Currency }
let getFx1 u =
match u with
| "USD" -> {UnitRate = 1.0; From = USD; To = USD}
| "EUR" -> {UnitRate = 1.0; From = USD; To = EUR}
| _ -> failwithf "Unrecognised units %s" u
Related
I am looking to hard code units into my record fields. Currently I have the ToString() method overriden and used the [StructuredFormatDisplay("{AsString}")] atribute. This has worked except for it is unfortunate that I lose FSharp's pretty printing (mainly the offsetting) on types that this record is nested into. With that said, I am wondering if anyone knows of any tricks to accomplish this.
Essentially I have this type:
type SteelMaterial =
{Fy : float<ksi>;
Fu : float<ksi>;
E : float<ksi>;}
and want it to print out like this:
SteelMaterial = {Fy = 50.0 <ksi>;
Fu = 60.0 <ksi>;
E = 29000.0 <ksi>;}
and like this, when nested :
Section = {Section = "Section 1";
Material = {Fy = 50.0 <ksi>;
Fu = 60.0 <ksi>;
E = 29000.0 <ksi>;};
Transformations = null;}
The reason I am looking to do this is so to document units when producing calculations via F# Formatting (via (*** include-value: mySection **)), Ifsharp or Azure Notebooks.
UPDATE
I didn't originally include my implementation since I didn't think it added clarity to the question. Here it is if anyone is wondering.
[<StructuredFormatDisplay("{AsString}")>]
type SteelMaterial =
{
Fy : float<ksi>
Fu : float<ksi>
E : float<ksi>
}
static member create (Fy, Fu, E) =
{Fy = Fy; Fu = Fu; E = E}
override this.ToString() =
sprintf "Steel Material =\r\n{Fy = %f <ksi>;\r\nFu = %f <ksi>;\r\nE = %f <ksi>;}"
this.Fy this.Fu this.E
member this.AsString = this.ToString()
Looks like I can get what I am looking for by wrapping the unit in a DU and overriding that ToString() method.
[<Measure>] type ksi
[<StructuredFormatDisplay("{AsString}")>]
type Ksi =
| Ksi of float<ksi>
override this.ToString() =
match this with
| Ksi value -> System.String.Format("{0:0.0####} <ksi>", value)
member this.AsString = this.ToString()
member this.ksi =
match this with
| Ksi value -> value
type SteelMaterial =
{Fy : Ksi;
Fu : Ksi;
E : Ksi;}
let mySteelMaterial =
{Fy = Ksi(50.0<ksi>);
Fu = Ksi(60.0<ksi>);
E = Ksi(29000.0<ksi>)}
This has some benefits including being able to pattern match on the Ksi type and printing the units as I was looking for. The downside is the cons that come with wrapping values in a DU, which includes the user needing to type Ksi(10.0<ksi>) which is not too succinct, and then needing to access the value via the ".ksi".
Given the following parametric type
type SomeDU2<'a,'b> =
| One of 'a
| Two of 'a * 'b
I have to functions that check if the given param is the respective union case without regard to params
let checkOne x =
match x with
| One _ -> true
| _ -> false
let checkTwo x =
match x with
| Two _ -> true
| _ -> false
This works pretty nice and as expected
let oi = checkOne (One 1)
let os = checkOne (One "1")
let tis = checkTwo (Two (1, "1"))
let tsi = checkTwo (Two ("1", 1))
I can switch the types as I like.
Now However I like to combine those two functions into one creation function
let makeUC () = (checkOne, checkTwo)
and then instantiate like this
let (o,t) = makeUC ()
only it gives me this error message now
Value restriction. The value 'o' has been inferred to have generic type
val o : (SomeDU2<'_a,'_b> -> bool)
Either make the arguments to 'o' explicit or, if you do not intend for it to be generic, add a type annotation.
val o : (SomeDU2<obj,obj> -> bool)
Actually I dont want that - nor do I need that.
Probably its a instance of missing higher kinded types in F#
Is there a way around this?
Edit
Actually me question wasnt complety as per #johns comment below.
Obviously I can do the following
let ro1 = o ((One 1) : SomeDU2<int,int>)
let rt1 = t (Two (1,2))
which then will backwards infer o and t to be of type SomeDU2<int,int> -> bool (I find this backwards inference very strange thou). The problem then is that o wont allow for the below anymore.
let ro2 = o ((One "1") : SomeDU2<string,int>)
So I'd have to instantiate a specific o instance for every combination of generic parameters of SomeDU2.
You would run into the value restriction even without the tuple:
let o = (fun () -> checkOne)()
If you need the results of invoking a function to be applicable to values of any type, then one solution would be to create instances of a nominal type with a generic method:
type DU2Checker =
abstract Check : SomeDU2<'a,'b> -> bool
let checkOne = {
new DU2Checker with
member this.Check(x) =
match x with
| One _ -> true
| _ -> false }
let checkTwo = {
new DU2Checker with
member this.Check(x) =
match x with
| Two _ -> true
| _ -> false }
let makeUC() = checkOne, checkTwo
let o,t = makeUC()
let false = o.Check(Two(3,4))
I have a need to use statically resolved type parameters in a situation similar to the below:
[<Struct>]
type Wrapper<'T> =
val raw:'T
new(v:'T) = {raw = v}
type Value =
| Float of float
| Int of int
| String of string
with
member this.ToWrapper() :'T =
match this with
| Float f -> Wrapper<float>(f) // type is inferred as float
| Int i -> Wrapper<int>(i) // error
| String s -> Wrapper<string>(s) // error
How do I define and use a ToWrapper function (or set thereof) that can map a 'Value' type to any type within the Generic Wrapper<'T> where I know 'T will be either float | int | string?
The Wrapper<'T> type needs to be a Struct so interfaces aren't an option - as suggested in some other posts related to this.
It's not clear to me what are you trying to achieve. Are you trying to restrict the wrapped types to Int, String and Float?
1) If so you can check at runtime like this:
[<Struct>]
type Wrapper<'T> =
val raw:'T
new(v:'T) = {raw = v}
let wrap x =
match box x with
| :? float -> ()
| :? int -> ()
| :? string -> ()
| _ -> failwith "invalid type"
Wrapper x
let a = wrap 90
let b = wrap "hi"
let c = wrap true // fails at runtime
2) If you want to restrict at compile-time an easy way is to add static members as constructors:
[<Struct>]
type Wrapper<'T> =
val raw:'T
private new(v:'T) = {raw = v}
with
static member wrap (x:float) = Wrapper x
static member wrap (x:int) = Wrapper x
static member wrap (x:string) = Wrapper x
let a = Wrapper<_>.wrap 90
let b = Wrapper<_>.wrap "hi"
let c = Wrapper<_>.wrap true // doesn't compile
3) Or may be, using the DU inside the wrapper makes more sense for you:
type Value =
| Float of float
| Int of int
| String of string
[<Struct>]
type Wrapper =
val raw:Value
new(v:Value) = {raw = v}
Of all solutions 3) is the only one that really restricts your wrapper. Solutions 1) and 2) restrict the way you construct it.
From 2) you can use some tricks with statically resolved type parameters in order to come up with an inline function (not a method) that will wrap only on those types. Still that will not constraint the type itself, but since the constructor is private the code that consumes your type will be forced through your restricted constructors.
Statically resolved type parameters work with functions or methods but not on types, since they are a compile time F# feature, not a .NET type system feature.
You can't do this, because Wrapper<float> isn't the same type as Wrapper<int> (which also isn't the same type as Wrapper<string>). What would the return type of ToWrapper be? It can't be all three at once.
I am working on a function that pattern matches some of my user-defined types in f# and converts them to strings. Part of the code looks like the following:
let gsToString (gs : general_structure) : string =
match gs with
| Date(Scattered(Eom(Ascending))) -> "Date(Scattered(Eom(Ascending)))"
| Date(Scattered(Eom(SameDate(dt)))) -> "Date(Scattered(Eom(SameDate(" + dt.ToString() + "))))"
| Number(AllNegative(Int1(Neither))) -> "Number(AllNegative(Int1(Neither)))"
| Number(AllNegative(Int1(SameInt(si)))) -> "Number(AllNegative(Int1(SameFloat(" + si.ToString() + "))))"
There are many other types being matched in this function, but these should be enough to convey the issue. Additionally, the types causing problems are:
| SameDate of System.DateTime
| SameFloat of float
Obviously, It is pretty trivial to do the first pattern matching function that converts my general_structure types to strings. However, a problem arises in my next function (which needs to be called later on in the code), where I need to reconvert the string representation back to a general_structure. The problem areas look like the following:
let stringToGS (str : string) : general_structure =
match str with
| "Date(Scattered(Eom(Ascending)))" -> Date(Scattered(Eom(Ascending)))
| "Date(Scattered(Eom(SameDate(dt))))"-> Date(Scattered(Eom(SameDate(System.DateTime.Parse dt))))
| "Number(AllNegative(Int1(Neither)))" -> Number(AllNegative(Int1(Neither)))
| "Number(AllPositive(Float1(SameFloat(sf))))" -> Number(AllPositive(Float1(SameFloat((float) sf))))
Although the first and the third cases in the stringToGS function work just fine, I am unable to find a way to convert the others back to their original form. If there any way to take a string inside of a pattern matching statement (in this case it would be dt and fs) and somehow parse only that portion of the pattern in order to return a different value (in this case I am trying to make them System.DateTimes and Floats, respectively) and return then to their original forms of:
Date(Scattered(Eom(SameDate(dt))))
Number(AllPositive(Float1(SameFloat(sf))))
? I would appreciate any help.
EDIT:
I was able to resolve the problem by doing something like the following with if statements for the cases that were causing problems:
if str.Contains("Scattered(Eom(SameDate")
then
let p1 = str.IndexOf(")")
let p2 = str.LastIndexOf("(")
let dt1 = str.Remove(p1)
let dt2 = dt1.Substring(p2 + 1)
let date = System.DateTime.Parse dt2
Date(Scattered(Eom(SameDate(date))))
Then, I could just do the normal pattern matching on all of the types that did not contain nested data.
You could also use active patterns, if there is a limited amount of classes and you don't want to use a serialization library:
open System
let (|RegexMatch|_|) pattern input =
let matches = System.Text.RegularExpressions.Regex.Matches(input, pattern)
if matches.Count = 1 then Some matches.[0].Groups.[1].Value
else None
type GeneralStructure =
| NoPayload
| DatePayload of DateTime
| StringPayload of string option
let toString = function
| NoPayload -> "NoPayload"
| DatePayload dt -> sprintf "DatePayload(%d)" <| dt.ToBinary()
| StringPayload None -> "StringPayload(None)"
| StringPayload (Some s) -> sprintf "StringPayload(Some(%s))" s
let fromString = function
| "NoPayload" -> NoPayload
| "StringPayload(None)" -> StringPayload None
| RegexMatch #"DatePayload\((.*)\)" dt -> DatePayload <| DateTime.FromBinary(Int64.Parse dt)
| RegexMatch #"StringPayload\(Some\((.*)\)\)" msg -> StringPayload <| Some msg
| o -> failwithf "Unknown %s %s" typeof<GeneralStructure>.Name o
let serialized = StringPayload <| Some "Foo" |> toString
let deserialized = fromString serialized
let serialized' = DatePayload DateTime.UtcNow |> toString
let deserialized' = fromString serialized'
// val serialized : string = "StringPayload(Some(Foo))"
// val deserialized : GeneralStructure = StringPayload (Some "Foo")
// val serialized' : string = "DatePayload(5247430828937321388)"
// val deserialized' : GeneralStructure = DatePayload 06.08.2015 18:04:10
Note that the regex is not foolproof, I made that up just to fit these cases.
I want to get the equivalent of Enum.GetName for an F# discriminated union member. Calling ToString() gives me TypeName+MemberName, which isn't exactly what I want. I could substring it, of course, but is it safe? Or perhaps there's a better way?
You need to use the classes in the Microsoft.FSharp.Reflection namespace so:
open Microsoft.FSharp.Reflection
///Returns the case name of the object with union type 'ty.
let GetUnionCaseName (x:'a) =
match FSharpValue.GetUnionFields(x, typeof<'a>) with
| case, _ -> case.Name
///Returns the case names of union type 'ty.
let GetUnionCaseNames <'ty> () =
FSharpType.GetUnionCases(typeof<'ty>) |> Array.map (fun info -> info.Name)
// Example
type Beverage =
| Coffee
| Tea
let t = Tea
> val t : Beverage = Tea
GetUnionCaseName(t)
> val it : string = "Tea"
GetUnionCaseNames<Beverage>()
> val it : string array = [|"Coffee"; "Tea"|]
#DanielAsher's answer works, but to make it more elegant (and fast? because of the lack of reflection for one of the methods), I would do it this way:
type Beverage =
| Coffee
| Tea
static member ToStrings() =
Microsoft.FSharp.Reflection.FSharpType.GetUnionCases(typeof<Beverage>)
|> Array.map (fun info -> info.Name)
override self.ToString() =
sprintf "%A" self
(Inspired by this and this.)
This answer supplies additional information and solutions to the top answer.
I just now had a case where the top answer did not work. The problem was that the value was behind an interface, and then I would sometimes get the case name (Coffee or Tea), but mostly only the type name (Beverage). I don't understand why. I'm on .NET 5.0.
I changed the function to this, and then it worked as expected on my interfaced DU, always giving me the case name.
open FSharp.Reflection
let GetUnionCaseName (x: obj) =
match FSharpValue.GetUnionFields(x, x.GetType()) with
| case, _ -> case.Name
I am aware that this is similar to other answers here, but this is not a member function, and so I guess should work on any DU, whether behind interfaces or not. I haven't tested what happens if used on a non-DU type.
type IMessage = interface end
type Beverage = Coffee | Tea
type Car =
| Tesla of model:string
| Ford
interface IMessage
type MySingleCase = MySingleCase of string
type SingleCase2 = SingleCase2 of string interface IMessage
let m1: Beverage = Coffee
let m2: IMessage = (Tesla "Model 3") :> IMessage
let m3 = MySingleCase "x"
let m4 = SingleCase2 "x" :> IMessage
printfn "%s" (GetUnionCaseName m1) // Coffee
printfn "%s" (GetUnionCaseName m2) // Tesla
printfn "%s" (GetUnionCaseName m3) // MySingleCase
printfn "%s" (GetUnionCaseName m4) // SingleCase2
I would like to propose something even more concise:
open Microsoft.FSharp.Reflection
type Coffee = { Country: string; Intensity: int }
type Beverage =
| Tea
| Coffee of Coffee
member x.GetName() =
match FSharpValue.GetUnionFields(x, x.GetType()) with
| (case, _) -> case.Name
When union case is simple, GetName() may bring the same as ToString():
> let tea = Tea
val tea : Beverage = Tea
> tea.GetName()
val it : string = "Tea"
> tea.ToString()
val it : string = "Tea"
However, if union case is fancier, there will be a difference:.
> let coffee = Coffee ({ Country = "Kenya"; Intensity = 42 })
val coffee : Beverage = Coffee {Country = "Kenya"; Intensity = 42;}
> coffee.GetName()
val it : string = "Coffee"
> coffee.ToString()
val it : string = "Coffee {Country = "Kenya"; Intensity = 42;}"