DU Member Access Named Fields

How can I access the named fields of a discriminating member of an association?

Example:

type Point = | Point of x : int * y : int let p = Point(3, 1) // how to access x-value or y-value of p here? 
+5
source share
2 answers

In general, joins with named fields work just like any other type of join: you get access to fields through match :

 let x, y = match p with | Point (x1, y1) -> x1, y1 

The F # documentation also mentions how to map only certain named parameters. In your case, this means that you can write:

 let xOnly = match p with | Point (x = x1) -> x1 

If you have one case, see the answer to @pswg. The code in this answer is common to all discriminatory unions. For singleton joins with named fields, you can use the special syntax shown above and write:

 let Point(x = myX) = p 

This binds the value of field x to myX .

PS according to the comments: why can't you read the fields right away by performing px ? You can imagine a discriminated union as a hierarchy of small objects (and use it as such, see the discriminated union documentation : β€œYou can often use a discriminated union as a simpler alternative to the hierarchy of small objects”). Consider the following DU:

 type Shape = | Circle of r: float | Square of float 

You can view Shape as a superclass. Circle and Square are two derived classes, each of which has one float property. Each instance of Circle or Square that you create will be updated to Shape .

At the same time, it becomes clear why you cannot read the fields right away: first you need to establish which of the derived classes you are viewing, only after you have selected the correct subclass, you can read the fields.

This kind of object hierarchy is very closely related to how DUs are processed inside F #: if you look at the DU type in reflection, you will see two nested types that have the same name as your union cases:

 > typeof<Shape>.GetNestedTypes() |> Seq.iter (fun t -> let p = t.GetProperties() let s = p |> Array.map (fun p -> sprintf "%s: %s" p.Name p.PropertyType.Name) |> String.concat "; " printfn "Nested type %s: %i Properties %s" t.Name p.Length s );; Nested type Tags: 0 Properties Nested type Circle: 4 Properties r: Double; Tag: Int32; IsCircle: Boolean; IsSquare: Boolean Nested type Square: 4 Properties Item: Double; Tag: Int32; IsCircle: Boolean; IsSquare: Boolean 

Actual data is stored in the properties of the subclass. For Circle we used named fields, you see the r property. For Square we have the data in the Item property (or Item1 , Item2 , if there were several arguments). The rest is the generated compiler: a numeric Tag field that will be used to quickly recognize subclasses and two bool properties to test subclasses.

The superclass itself has only compiler-generated properties:

 > typeof<Shape>.GetProperties() |> Seq.iter (fun p -> printfn "Property %s" p.Name);; Property Tag Property IsCircle Property IsSquare 
+4
source

For single-disk discriminatory associations like your example, you do not need to use the match-with . You could just do this:

 let (Point (x, y)) = p printf "%i" x // 3 

Or just get x and ignore y :

 let (Point (x, _)) = p printf "%i" x // 3 
+5
source

Source: https://habr.com/ru/post/1265208/


All Articles