Expose Content of a Discriminated Union Case - f#

I am wondering if there is a good way of exposing the content of a DU case to the DU type itself.
For example:
[<Measure>] type inch
module StructuralShape =
type Plate =
{
Length : float<inch>
Thickness : float<inch>
}
type SingleAngle =
{
VerticalLeg : float<inch>
HorizontalLeg : float<inch>
Thickness : float<inch>
}
type StructuralShape =
| Plate of Plate
| SingleAngle of SingleAngle
let area (ss : StructuralShape) =
match ss with
| Plate pl -> pl.Length * pl.Thickness
| SingleAngle sa ->
(sa.HorizontalLeg + sa.VerticalLeg - sa.Thickness) * sa.Thickness
type StructuralShape with
member ss.Area = area ss
module Test =
open StructuralShape
let myShape = Plate {Length = 2.0<inch>; Thickness = 0.25<inch>}
let area = myShape.Area
// Looking to allow the user of the library
// to access the internal shape properties
// without having to use some type of 'get shape' function
let length = myShape.Length // fails
After a re-look at my design, which was inconsistent as Fyodor pointed out, I decided to create an interface for IShape.
[<Measure>] type inch
type IShape = interface end
module StructuralShapes =
type Plate =
{
Length : float<inch>
Thickness : float<inch>
}
interface IShape
type SingleAngle =
{
VerticalLeg : float<inch>
HorizontalLeg : float<inch>
Thickness : float<inch>
}
interface IShape
let area (shape: IShape) =
match shape with
| :? Plate as pl -> pl.Length * pl.Thickness
| :? SingleAngle as sa ->
(sa.HorizontalLeg + sa.VerticalLeg - sa.Thickness)
* sa.Thickness
| _ -> failwith "Shape not suppported."
type IShape with
member this.Area = area this
Note: For simplicity, this is a simplified version of the library.

It is unclear what you want to achieve here. I suspect you may have inconsistent design in your mind and not realize it.
If your StructuralShape type only ever has one case, then why do you need it at all? Just use Plate instead!
If your StructuralShape type will have more cases, then what happens when the actual value doesn't have a Thickness field?
If your StructuralShape type will have more cases, but all of them are required to have a Thickness field, and that field has the same type and the same meaning for all of them, then a better design would be to "lift" the common fields to the common type, and leave only differences under the DU:
type Plate = { Length: float }
type Foo = { Bar: string }
type StructuralShapeInfo = Plate of Plate | Foo of Foo
type StructuralShape = { Info: StructuralShapeInfo; Thickness: float }
(pardon my choice of Info suffix; I realize it's not the best choice, but I can't come up with a better one without knowing your domain and thinking about it for a while; there are three hard problems in programming: naming stuff and off-by-one errors)

You can augment a discriminated union with members, which is useful for convenient access in cases like this:
type StructuralShape =
| Plate of Plate
member this.Thickness = match this with Plate p -> p.Thickness
If you add any new cases to StructuralShape, then you need to handle the case in the code for this property.
This may be a useful thing to know, but Fyodor makes a very good point in his answer about reorganising your data model so this isn't necessary.

Related

How to check if list contains discriminated union case with type?

Given the follwing code:
type Creature =
{ Strength: int
Toughness: int }
type CardType =
| Creature of Creature
| Land
| Instant
type Card =
{ Types: CardType list }
module Card =
let isType t card = List.contains t card.Types
I am able to write
Card.isType Land
When trying to check if card is a creature, i get the following error:
This expression was expected to have type
'CardType'
but here has type
'Creature -> CardType'
Is it even possible to have a "isType" function like this or am I stuck with pattern matching on a separate "isCreature" function instead?
Unless you want to resort to various reflection-based hacks, you are stuck with pattern matching. I would probably define a bit more general function using List.exist rather than List.contains (taking a predicate). Then you can easily define three functions for your specific card types:
module Card =
let isType t card =
List.exists t card.Types
let isCreature =
isType (function Creature _ -> true | _ -> false)
let isLand = isType ((=) Land)
let isInstant = isType ((=) Instant)
For Land and Instant, you can just check if the value equals the specific one you're looking for. For Creature, this requires pattern matching - but can be done quite nicely using function.

Extending records with additional fields

I have a data pipeline where at each step more data fields are required. I would like to do this in a functional way by respecting immutability. I could achieve this with a class by I am wondering if there is an F# way of doing it?
// code that loads initial field information and returns record A
type recordA = {
A: int
}
// code that loads additional field information and returns record AB
type recordAB = {
A: int
B: int
}
// code that loads additional field information and returns record ABC
type recordABC = {
A: int
B: int
C: int
}
As records are sealed I can't just inherit them. How can I avoid having to define a new record with the exact same fields as the previous step and adding the required fields? Preferably I would like to have something like one record that has all required fields and the fields get assigned to their values in each step.
Note that the number of fields added in each step could be more than 1.
I think this can be a good use case for the anonymous records recently introduced in F#.
let a = {| X = 3 |}
let b = {| a with Y = "1"; Z = 4.0|}
let c = {| b with W = 1 |}
printfn "%d, %s, %f, %d" c.X c.Y c.Z c.W
One way to do it in a very FP-style would be to use a DU with a case for each step of the workflow, and the appropriate data for each step in each case:
type WorfklowState =
| StepOne of int
| StepTwo of int * int
| StepThree of int * int * int
Then your entire workflow state, both what step you're currently on and the data produced/consumed by that step, would be modeled in the data type. Of course, you would probably create record types for the data of each case, rather than using progressively larger tuples.
Depending on the application, this may be a (mis-)use case for a dynamic data container.
F# might help by providing user-defined dynamic lookup operators, for which a special syntactic translation occurs.
let (?) (m : Map<_,_>) k = m.Item k
// val ( ? ) : m:Map<'a,'b> -> k:'a -> 'b when 'a : comparison
let (?<-) (m : Map<_,_>) k v = m.Add(k, v)
// val ( ?<- ) : m:Map<'a,'b> -> k:'a -> v:'b -> Map<'a,'b> when 'a : comparison
let m = Map.empty<_,_>
let ma = m?A <- "0"
let mabc = (ma?B <- "1")?C <- "2"
ma?A // val it : string = "0"
mabc?C // val it : string = "2"
You can "inherit" records:
type RecordA =
{
a : int
}
type RecordAB =
{
a : RecordA
b : int
}
type RecordABC =
{
ab : RecordAB
c : int
}
Then you can access all of the elements, though with longer and longer chain as you go deeper and deeper.
However, why don't you just use a list of elements to store the result?
First, I would create a type to handle all possible types that you may have on each step, e.g.:
type Step =
| Int of int
| String of string
// ...
Then you can represent the workflow simply as:
type WorkflowState = list<Step>
and if you want to ensure that you always have at least one element then you can use:
type WorkflowState = Step * list<Step>
However, the records have labels and the structure above does not have them! So, if you do need labels, then you can represent them by a map using either a strong type:
type Label =
| A
| B
// ...
type WorkflowMappedState = Map<Label, Step>
or just a string based one, e.g.
type WorkflowMappedState = Map<string, Step>
The benefits of list or map based approach in comparison to the answers above is that you don't have to know the maximum number of possible steps. What if the number of steps is over 100? Would you want to create a record with 100+ labels? Most likely not! The anonymous records are great, but what if you want to use them outside of module where they were created? I think that that would cause some troubles.
Having said all that, I think that I would go with a list based approach: type WorkflowState = list<Step>. It is very F# way and it is very easy to transform further.

F# Types and Function signatures

I'm new to F# and I'm trying a few thing to get my head around the language.
I have to two types almost identical (Coordinate and Vector). Because of this type inference cannot work properly and I have hard time to specify the correct type on each function.
It somehow undestand that's a Vector here:
type Coordinate = {X:int; Y:int}
type Vector = {X:int; Y:int}
let calculateVector (origin:Coordinate) (destination:Coordinate) = { X=destination.X-origin.X; Y= destination.Y-origin.Y;}
And here when I want a return type of Coordinate, I cannot find how to specify the return it for this function:
let calculateNextCoordinate (coordinate:Coordinate) direction =
match direction with
| "N" -> { X=coordinate.X; Y=coordinate.Y-1 }
| "NE" -> { X=coordinate.X+1; Y=coordinate.Y-1 }
| "E" -> { X=coordinate.X+1; Y=coordinate.Y }
| "SE" -> { X=coordinate.X+1; Y=coordinate.Y+1 }
| "S" -> { X=coordinate.X; Y=coordinate.Y+1 }
| "SW" -> { X=coordinate.X-1; Y=coordinate.Y+1 }
| "W" -> { X=coordinate.X-1; Y=coordinate.Y }
| "NW" -> { X=coordinate.X-1; Y=coordinate.Y-1 }
| _ -> coordinate
I have this error on the default case: This expression was expected to have 'Vector' but here has type 'Coordinate'
i tired to have a look on this website for function signatures but could not something for my problem: https://fsharpforfunandprofit.com/posts/function-signatures/
Questions:
How do you fix this error?
Is it because inference type take by default the last type declared that match the properties (in my example Vector)?
Bonus: Is there a better way to handle this kind of situation in F#?
Thanks in advance
Since records are constructed using their member names:
{ X = 2; Y = 3}
you created a naming conflict between Vector and Coordinate. In F# the compiler always resolves to the latest definition in such cases and therefore, in your example, the compiler will interpret the record { X = ..., Y = ...} as a Vector.
There is a good article on F# records on fsharpforfunandprofit.com, that explains how to handle this easily, which I suggest you read for a good explanation.
But in short, you can prefix either member of the record instance with the record type:
{ Coordinate.X = 2; Y = 3 } // Creates a Coordinate
{ X = 2; Coordinate.Y = 3 } // Creates a Coordinate
and
{ Vector.X = 2; Y = 3 } // creates a vector

Are there any tricks for hard coding units into record fields without losing pretty printing?

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".

F# type definition with expression

Is it possible to express something like this:
type id = int > 0
I know its not possible to do statically, since this would mean F# has dependent types. In C# I'm used to do this sort of thing with code contracts and get a runtime enforcement. I'm looking for something similiar here.
Thanks
EDIT:
Thank you for all the answers which have various pros and cons. At the monent I'm only using a small subset of F#, a subset of the ocaml core that lends itself easily to program proofs. So no classes.
Contrary to what others said, I would suggest not using classes here, if I understood your problem correctly.
Since the value is immutable, we need applying constraint only once. Any wrapper classes would be an overhead and load GC. Instead, a simple function will do the job:
let inline constrained predicate errormessage value =
if not (predicate value)
then invalidArg "value" errormessage
else value
let positive =
constrained (fun x -> x > 0) "Value must be positive"
let int1 = positive 5 // OK
let int2 = positive -3 // ArgumentException
You can do the same for other types:
let mustBeLong =
constrained (fun (x:string) -> x.Length > 3) "String must be long"
let str1 = mustBeLong "foobar" // OK
let str2 = mustBeLong "baz" // ArgumentException
Using the same within a struct:
type Point2D =
struct
val X: int
val Y: int
new(x: int, y: int) = { X = positive x; Y = positive y }
end
let point1 = Point2D(5, 3) // OK
let point2 = Point2D(5, -2) // ArgumentException
Define it as a union type:
type Id = Id of int
and shadow the constructor with another function:
let Id n =
assert(n > 0)
Id n
In F#, you have to resort to classes and check arguments inside constructors. Other types such as discriminated unions, records and structs have implicit constructors which you can't easily alter.
type Id(i: int) =
do if i <= 0 then
invalidArg "i" "the argument has to be a positive integer"
member x.Value = i
Pattern matching doesn't play nicely with classes. You can remedy the problem using active patterns:
let (|Id|) (id: Id) = id.Value
let id = Id(1)
match id with
| Id 1 -> printfn "matched"
| _ -> printfn "unmatched"
You could create a generic class like so:
type verify<'t>(t:'t,cond) =
let mutable tval = t
let _verify v = if not (cond v) then failwith "bad argument"
do _verify tval
member x.get() = tval
member x.set v =
_verify v
tval <- v
then you can use it with
verify(1,fun t -> t>0)
using .set will recheck the condition.

Resources