given the following Discriminated Union:
type A = B of string | C of int
How can I get the constructor B name?
A.B.ToString()
// would return something like:
val it : string = "FSI_0045+it#109-19"
// when I desire
val it : string = "B"
for example with this type it works:
type D = E | F
D.E.ToString();;
val it : string = "E"
I normally get the string name of an instance of the DU with
let stringFromDU (x: 'a) =
match FSharpValue.GetUnionFields(x, typeof<'a>) with
| case, _ -> case.Name
But in this case, I do not have an instance, I just want to serialize the label name.
If you enable the latest language version, e.g. by passing --langversion:preview to FSI or setting
<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>
in your .fsproj, the following will work:
type A = B of int
let n = nameof A.B
Note: with F# 5 this will be supported out of the box :-)
You're using FSharpValue from FSharp.Reflection namespace in your example. Note that there's another class in that library for handling scenarios where you want to work with types only, FSharpType.
let cases = FSharpType.GetUnionCases(typeof<A>)
Outside of unions, it also provides helpers for other operations on F# native types.
Related
I have a DU and I'm overriding the Equals method. Based on the current DU value, I would like to call the base equality method or my custom one. However, it's not letting me access "base". Any idea on how to work around this?
type Test =
| A of string
| B of int64
override this.Equals(other) =
let other' = other :?> Test
match other' with
| A str -> str = "a"
| B i -> base.Equals this other //how do I do this?
First, any F# discriminated union will have obj as base class, so just use obj.Equals.
Second, Equals is a .NET method, not an F# function, so its arguments must be given in a tupled form - i.e. Equals(x,y) instead of Equals x y.
Finally, if you implement a custom Equals, you also need to add [<CustomEquality; NoComparison>]
So:
[<CustomEquality; NoComparison>]
type Test =
| A of string
| B of int64
override this.Equals(other) =
let other' = other :?> Test
match other' with
| A str -> str = "a"
| B i -> obj.Equals(this, other)
I'm using HtmlAgilityPack to parse some Html files. The following code
let html = "<div>...</div>"
let doc = new HtmlDocument()
doc.LoadHtml(html)
let node = doc.DocumentNode
let x = node.SelectSingleNode("//div/#xyz")
|> Option.ofNullable
The last line got the error of
The type HtmlNode doesn't match the type 'Nullable<'a>'
The following code can convert it
let ofNull = function
| null -> None
| x -> Some x
Is there any built-in construct to do it? I don't want my definition of ofnull scatter everywhere in the code/script.
The built-in function you are looking for is Option.ofObj, not Option.ofNullable.
The Option.ofNullable converts a value of type System.Nullable<'a>, which is a .NET type that is used for turning value types into nullable types. C# has langauge support for this and you can use, for example, int? to define type that is similar to option<int> in F#.
The Option.ofObj function takes any .NET obj (actually, a generic type that has a null as a value) and returns Some if it is not null or None if it is null. The types are:
Option.ofNullable : System.Nullable<'a> -> 'a option
Option.ofObj : 'a -> 'a option
I want to define the following record type:
type LogFuncs = {
LogWithLabel : string -> 'a -> unit
LogWithLabelAndReturn : string -> 'a -> 'a }
The intention is that I can define one function of string -> unit and then use that to derive several convenience functions, like so:
let makeLogFuncs outputFunc =
let logWithLabelFunc label value = sprintf "%s: %A" label value |> outputFunc
let logWithLabelAndReturnFunc label value = logWithLabelFunc label value; value
{ LogWithLabel = logWithLabelFunc
LogWithLabelAndReturn = logWithLabelAndReturnFunc }
But, the compiler won't let me do this without specifying an <'a> when making an instance of type LogFuncs, and that's not what I want to do -- I want to be able to call this function on any 'a. (I will also want to provide related functions, hence the use of the record type.)
Is it possible to define a record type with a field containing type parameter that is not also a type parameter of the record type itself?
I don't believe it is possible to do with record types. But I can define a class which provides the mechanism I wanted:
type Logger (outputFunc: string->unit) =
member __.LogWithLabel label value = sprintf "%s: %A" label value |> outputFunc
member x.LogWithLabelAndReturn label value = x.LogWithLabel label value; value
Then I can do:
let log = new Loggery (printfn "%s")
let ``result should equal 5`` = 5 |> log.LogWithLabelAndReturn "Beans"
...and it correctly prints "Beans: 5" and returns 5.
You can make the record itself generic:
type LogFuncs<'a> = {
LogWithLabel : string -> 'a -> unit
LogWithLabelAndReturn : string -> 'a -> 'a }
That also makes the makeLogFuncs generic. It's still usable, but probably not in the way you want:
(makeLogFuncs (printfn "%s")).LogWithLabel "Number" 42
(makeLogFuncs (printfn "%s")).LogWithLabelAndReturn "Number" 42
(makeLogFuncs (printfn "%s")).LogWithLabel "Text" "Foo"
(makeLogFuncs (printfn "%s")).LogWithLabelAndReturn "Text" "Foo"
As the answer provided by Overlord Zurg implies, the OP approach is quite object-oriented, so use objects if you want to design the system in that way.
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 would like to define a type such as:
type blah =
AThing
| AnotherThing
with
static member ofString : string -> blah
override x.ToString () : string
I would like to make sure that the two methods are always guaranteed to be consistent. A good way of doing this would be to construct two maps from the same list of pairs, and then turn them into the obvious implementations of the two methods. Roughly:
let pairs = [Athing, "AThing"; AnotherThing, "AnotherThing"]
let tostr = Map.ofList pairs
let toblah = pairs |> List.map swap |> Map.ofList
I think this code can only be defined in a static member function. The static bit is implied by it needing to be accessible from ofString, which is static. It cannot be defined before the type, since the list relies on it. It cannot be defined afterwards because F# does not allow methods to be just declared and implemented later (in the same way that e.g. C++ would). So that leaves the choice between a static member and a static let. The compiler says that static lets are not allowed in an augmentation. (?)
The code works fine when put into a static member function, however it makes the maps every time they are needed. Needless to say, this is even less efficient than linear search. How do I do it correctly please? Many thanks.
This compiles (with a warning)
type blah =
AThing
| AnotherThing
let pairs = [AThing, "AThing"; AnotherThing, "AnotherThing"]
let tostr = Map.ofList pairs
let toblah = pairs |> List.map (fun (x,y)->y,x) |> Map.ofList
type blah
with
static member ofString s = toblah.[s]
override x.ToString () = tostr.[x]
and demonstrates an augmentation (define the type, do other code, then do type blah with to define more members).
Here's the code that works as required:
type blahFactory() =
static let pairs = printf "array initialized; "; [AThing, "AThing"; AnotherThing, "AnotherThing"]
static member tostr = Map.ofList pairs
static member toblah = pairs |> List.map swap |> Map.ofList
and blah =
| AThing
| AnotherThing
with
static member ofString (string) = blahFactory.toblah.[string]
override x.ToString () = blahFactory.tostr.[x]
I've placed printf instruction to demonstrate the array is initialized just once.
Several thoughts you may consider useful.
First, using DU is overhead for sample provided. If your data is that simple, you may consider enum types.
If you really mean DU, that may be sign of possible problems in the future. Consider this:
type blah =
| AThing of string
| AnotherThing of int * int
Both construction and comparison will be impossible in this case.
Do you have any reasons not to use standard XML serialization?
I don't get your point. What's wrong with:
type blah =
| AThing
| AnotherThing
with
static member ofString = function
| "AThing" -> AThing
| "AnotherThing" -> AnotherThing
| _ -> failwith "Unwellformed string"
override x.ToString () =
match x with
| AThing -> "AThing"
| AnotherThing -> "AnotherThing"
Pattern matching is Θ(1) which is extremely efficient in F#.
I believe you were trying to do the following: for any Discriminated Union have an ability to go from particular DU case to its name and back without burden of hardcoding the relationship for each pair, which have place in both answers by Brian and pad.
If you sacrifice override of ToString() in lieu of the equivalent static member it can be done with the following approach:
open Microsoft.FSharp.Reflection
type Blah =
| AThing
| AnotherThing
[<AutoOpenAttribute>]
module BlahExt =
let cases = FSharpType.GetUnionCases typeof<Blah>
let toBlah = dict [for case in cases do yield (case.Name, FSharpValue.MakeUnion(case, [||]) :?> Blah)]
let fromBlah = dict [for case in cases do yield ((FSharpValue.MakeUnion(case, [||]) :?> Blah), case.Name)]
type Blah
with
static member ofString name =
if toBlah.ContainsKey name then (toBlah.Item name) else failwith "bad string"
static member toString x = fromBlah.Item x
Now printfn "ofString: %A" (Blah.ofString "AThing") shows union case AThing and vice versa printfn "toString: %s" (Blah.toString AThing) shows string AThing.
You can notice that I did not list members of your DU whatsoever, this is achieved once thru reflection and guaranteed to be correct mapping automagically. This approach will work for any number of unit cases - two or two hundreds - without any need to hardcode the particular cases.