F# domain modelling with shared properties - f#

I can't figure out how to do the following in idiomatic F# (slightly adjusted for clarity):
type Shared(name, label, multivalue) =
member val Name = name with get, set
member val Label = label with get, set
member val Multivalue = multivalue with get, set
type TextField(name, label, multivalue) =
inherit Shared(name, label, multivalue)
member val Xslt = "" with get, set
type NumberField(name, label, multivalue) =
inherit Shared(name, label, multivalue)
// helper 'create' function
let textfield name label =
TextField (name, label, false)
// helper 'create' function
let numberfield name label =
NumberField (name, label, false)
let makemultivaluefield (field: Shared) =
field.Multivalue <- true
//field <- I would want this to return the parent type. E.g. TextField or NumberField
// Actual code for creating / using above domain (DSL like syntax... that's the goal of this whole exercise)
let myTextField =
textfield "field" "field"
|> makemultivaluefield
let myNumberField =
numberfield "fieldnumber" "field number label"
|> makemultivaluefield
The |> makemultivaluefield call returns a unit now, but I want it to return a TextField or a NumberField
(There are more classes like NumberField and TextField all with the same 3 properties declared in Shared).

The approach with a minimal amount of change compared to your example would be something like this
let makemultivaluefield<'subtype when 'subtype :> Shared> (field: 'subtype) : 'subtype =
field.Multivalue <- true
field
Make the function generic and then restrict it to subtypes of your Shared type. This should work fine for you unless your real life code is way more complex than the example.

Related

Calling F# constructor with properties initialization from secondary constructors

Given
type A() =
member val Prop: int = 0 with get, set
There are multiple ways to create an instance
let a0 = A() // Prop = 0
let a1 = A(Prop = 1)
let a2 = A()
a2.Prop <- 2
Now we want to enhance our class and allow passing the prop value in the constructor, but without losing the parameterless constructor we already have
type A1() =
member val Prop: int = 0 with get, set
new(prop: int) = A1(Prop = prop) //Error
However this is an error
This is not a valid object construction expression. Explicit object
constructors must either call an alternate constructor or initialize
all fields of the object and specify a call to a super class
constructor.
which doesn't seem correct as the new constructor is actually calling an alternate constructor.
There are alternatives/workarounds to achieve the result, for example:
type A2() =
member val Prop: int = 0 with get, set
static member Create(prop: int) = A(Prop = prop)
let a21 = A2.Create(1)
type A3(?prop: int) as this =
do if prop.IsSome then this.Prop <- prop.Value
member val Prop: int = 0 with get, set
let a31 = A3(1)
however the A1 version seems the cleanest and there are no apparent reasons why it cannot be valid (it is very similar to the A2 static member)
Can someone explain why the A1 syntax cannot be valid?
The primary constructor of your object should generally take all the parameters required for constructing a valid object. As a general rule I'd make the secondary constructor parameterless and the primary take all the parameters you care about.
If you must have a parameterless primary constructor but still want to assign properties in secondary constructors you can use the then keyword for side-effectful construction.
type A1() =
member val Prop: int = 0 with get, set
new(prop: int) as this =
A1()
then
this.Prop <- prop
I don't know if it cannot be valid if somebody decides the compiler should handle it, but I thought you might be interested in how we normally solve this case.
type A4(prop: int) =
member val Prop = prop with get, set
new() = A4(0)
let a4a = A4() // 0
let a4b = A4(3) // 3
let a4c = A4(Prop=7) // 7
Following this, it's useful to know how to make private constructors.
type A4 private (prop: int) =
member val Prop = prop with get, set
private new () = A4(0)
This example is of course rather useless code, but it shows where to put the private keyword. As you probably already understand, you can use one or more private constructors, typically with many parameters, as helpers for public constructors that have fewer parameters.

Is it possible to create a record type whose field is a function that takes a generic parameter, without specifying the type when creating the record?

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.

Setting a property of a class when the containing class is constructed

A class Test which upon construction creates an instance of another class and sets a property would look something like this (I suppose):
type Test() as this =
let a = new A()
do this.Init()
member this.Init() =
let a.Size = 10
However, I get a Block following this 'let' is unfinished. Expect an expression.
What is the correct and preferred way of doing this?
If you want to mutate the Size property, you'll have to use the assignment operator:
type Test() as this =
let a = new A()
do this.Init()
member this.Init() =
a.Size <- 10
However, you can write it much more succinctly like this:
type Test() =
let a = A (Size = 10)

Any simpler way to make a lazy property?

I would like to load data only when needed.
At start I used auto property.
type DataFromCsv(path) =
...
member val A1 = MyCsvType.Load(path1)
member val A2 = MyCsvType.Load(path2)
member val A3 = MyCsvType.Load(path3)
member val A4 = MyCsvType.Load(path4)
It is sort of lazy till I instantiate the object let my_data = DataFromCsv(path), at which time all the files are loaded, which is not ideal.
My another attempt is to use lazy for each property.
type DataFromCsv(path) =
let a1 = lazy (MyCsvType.Load(path1))
let a2 = lazy (MyCsvType.Load(path2))
member this.A1 = a1.Force()
member this.A2 = a2.Force()
It works, but I feel it is a bit cumbersome. Furthermore, a1 is only used by member this.A1 but it is visible anywhere in the object.
So, is there a kind of auto lazy property, so that I can do such things more elegantly?

Get the first value of an Enum type in F#

I have a class with an enum property. To use the compact property syntax I have to initialize it with a value from the type, eg.
type Color =
| Blue= 0
| Green= 1
type MyClass() =
member val Col = Color.Blue with get, set // <-- the problem
However, I don't like the hard coded Blue in there, and would prefer to set it to the first item of the enumeration.
I've been able to do it with this:
member val Color = enum<Color>(unbox (Enum.GetValues(typeof<Color>).GetValue(0))) ...
Needless to say, I'm hoping there's a better way to either get the first value of an enum, or initialize the property!
If the first member is significant within your program then it makes sense to make it explicit. It's perfectly fine to have two members with the same value.
type Color =
| Default = 0
| Blue = 0
| Green = 1
type MyClass() =
member val Col = Color.Default with get, set
Since enum equality depends on the underlying value, this works as desired (i.e., prints "blue"):
let c = MyClass()
match c.Col with
| Color.Blue -> printfn "blue"
| _ -> printfn "not blue"
Given the two alternatives you mention, I believe you are better off with Color.Blue. While the numeric values backing the enumeration are entirely arbitrary, the colours at least have some meaning in your application. If you are uncomfortable with setting the value directly because there is extra meaning associated to the default you can always define it separately somewhere else:
let standardUniformColor = Color.Blue
//(...)
type MyClass() = member val Col = standardUniformColor with get, set
You can use the magic enum function like so:
type Color =
| Blue= 0
| Green= 1
type MyClass() =
member val Col : Color = enum 0 with get, set
The only catch is that you need a type annotation somewhere so that the compiler knows which enum to cast to.
NOTE: This assumes that the enum's first value is 0.

Resources