How do I reduce the duplicated modules in this F# code? - f#

I'm working on a new system where I am using strongly typed Id values for my entities. To do that, I've followed a pattern that I've seen recommended before. The code seems very repetitive though and I'd like to simplify it.
type PersonId = private PersonId of string
module PersonId =
let private prefix = "person"
let value (PersonId id) = id
let create (id: string) = PersonId(sprintf "%s_%s" prefix id)
type OrderId = private OrderId of string
module OrderId =
let private prefix = "order"
let value (OrderId id) = id
let create (id: string) = OrderId(sprintf "%s_%s" prefix id)
Is there a way to make this code more generic so I don't have to repeat the module code? I was thinking of making an EntityId discriminated union of the PersonId and OrderId type, but not sure what the EntityId module code might look like. Thanks for the help.

I think you're headed towards something like this:
type EntityId =
private
| PersonId of string
| OrderId of string
module EntityId =
let init makeEntity prefix =
fun id -> makeEntity (sprintf "%s_%s" prefix id)
let value = function
| PersonId id
| OrderId id -> id
The trick here is that the init function is used to make your current create functions, like this:
module PersonId =
let create = EntityId.init PersonId "person"
module OrderId =
let create = EntityId.init OrderId "order"
And then you can use it like this:
let personId = PersonId.create "abc"
let orderId = OrderId.create "xyz"
printfn "%A" personId
printfn "%A" orderId
printfn "%s" <| EntityId.value personId
printfn "%s" <| EntityId.value orderId
Output is:
PersonId person_abc
OrderId order_xyz
person_abc
order_xyz
P.S. If you like currying, you can simplify init to:
let init makeEntity prefix id =
makeEntity (sprintf "%s_%s" prefix id)

Related

Strongly typed ids in F#?

I have two kinds of entity in my application: customers and products. They are each identified at a database level by a UUID.
In my F# code, this can be represented by System.Guid.
For readability, I added some types like this:
open System
type CustomerId = Guid
type ProductId = Guid
However, this does not prevent me from using a ProductId as a CustomerId and vice-versa.
I came up with a wrapper idea to prevent this:
open System
[<Struct>]
type ProductId =
{
Product : Guid
}
[<Struct>]
type CustomerId =
{
Customer : Guid
}
This makes initialization a little more verbose, and perhaps less intuitive:
let productId = { Product = Guid.NewGuid () }
But it adds type-safety:
// let customerId : CustomerId = productId // Type error
I was wondering what other approaches there are.
You can use single-case union types:
open System
[<Struct>]
type ProductId = ProductId of Guid
[<Struct>]
type CustomerId = CustomerId of Guid
let productId = ProductId (Guid.NewGuid())
Normally we add some convenient helper methods/properties directly to the types:
[<Struct>]
type ProductId = private ProductId of Guid with
static member Create () = ProductId (Guid.NewGuid())
member this.Value = let (ProductId i) = this in i
[<Struct>]
type CustomerId = private CustomerId of Guid with
static member Create () = CustomerId (Guid.NewGuid())
member this.Value = let (CustomerId i) = this in i
let productId = ProductId.Create ()
productId.Value |> printfn "%A"
Another approach, which is less common, but worth mentioning is to use so-called phantom types. The idea is that you will have a generic wrapper ID<'T> and then use different types for 'T to represent different types of IDs. Those types are never actually instantiated, which is why they're called phantom types.
[<Struct>]
type ID<'T> = ID of System.Guid
type CustomerID = interface end
type ProductID = interface end
Now you can create ID<CustomerID> and ID<ProductID> values to represent two kinds of IDs:
let newCustomerID () : ID<CustomerID> = ID(System.Guid.NewGuid())
let newProductID () : ID<ProductID> = ID(System.Guid.NewGuid())
The nice thing about this is that you can write functions that work with any ID easily:
let printID (ID g) = printfn "%s" (g.ToString())
For example, I can now create one customer ID, one product ID and print both, but I cannot do equality test on those IDs, because they're types do not match:
let ci = newCustomerID ()
let pi = newProductID ()
printID ci
printID pi
ci = pi // Type mismatch. Expecting a 'ID<CustomerID>' but given a 'ID<ProductID>'
This is a neat trick, but it is a bit more complicated than just using new type for each ID. In particular, you will likely need more type annotations in various places to make this work and the type errors might be less clear, especially when there is generic code involved. However, it's worth mentioning this as an alternative.

JSON serialization for option types

I'm running across an issue when I define a field of option type while serializing for JSON.
Before works (without option)
[<DataContract>]
type Article = {
[<field: DataMemberAttribute(Name="version") >]
version: string
}
After throws error (with option)
[<DataContract>]
type Article = {
[<field: DataMemberAttribute(Name="version") >]
version: string option
}
method threw exception:
System.Runtime.Serialization.SerializationException: Expecting state 'Element'.. Encountered 'Text' with name '', namespace ''.
Related Code
let response = request.GetResponse() :?> HttpWebResponse
use reader = new StreamReader(response.GetResponseStream())
use memoryStream = new MemoryStream(ASCIIEncoding.Default.GetBytes(reader.ReadToEnd()))
let result = (new DataContractJsonSerializer(typeof<Article>)).ReadObject(memoryStream) :?> Article
Here is a definition with a nullable int. String can already be null to which the zillions of Null reference exceptions attest...
open System
[<DataContract>]
type Person2 = {
[<DataMember(Name="Name") >]
entityName: Nullable<int>
[<DataMember(Name="Type") >]
entityType: String
}
However if you plan to fill this with a bunch of nulls maybe you should consider a class. This is really horrible but here's how it looks:
let p1 = { entityName = Nullable(10); entityType = "John"}
let p2 = { entityName = System.Nullable(); entityType = null}
val p1 : Person2 = {entityName = 10;
entityType = "John";}
val p2 : Person2 = {entityName = null;
entityType = null;}

F# understanding discriminated union

I've kind of asked this question earlier so sorry for asking a bit similar question again. But unfortunately im not able to really understand how to design a discriminated unions.
so i have bunch of data structures which look like
type Artist( artistId : int, name : String ) =
do
if name = null then nullArg String.Empty
new(artistId: int) = Artist(artistId)
member x.ArtistId = artistId
member x.Name = name
and Genre() =
let mutable name = String.Empty
let mutable genreId : int = 0
let mutable description = String.Empty
let mutable albums = List.empty
member x.Description
with get() = description and set( value ) = description <- value
member x.Albums
with get() = albums and set ( value ) = albums <- value
and Album() =
let mutable title = String.Empty
let mutable albumId = 0
let mutable genreId = 0
let mutable artistId = 0
let mutable price : decimal = Decimal.Zero
let mutable albumArtUrl = String.Empty
let mutable genre = new Genre()
let mutable artist = new Artist(artistId)
member x.Title
with get() = title and set (value) = title <- value
member x.Genre
with get() = genre and set (value) = genre <- value
member x.AlbumId
with get() = albumId and set ( value ) = albumId <- value
member x.GenreId
with get() = genreId and set ( value ) = genreId <- value
member x.ArtistId
with get() = artistId and set ( value ) = artistId <- value
member x.Price
with get() = price and set ( value ) = price <- value
member x.AlbumArtUrl
with get() = albumArtUrl and set ( value ) = albumArtUrl <- value
member x.Artist
with get() = artist and set ( value ) = artist <- value
enter code here
I tried defining the above as a Discriminated union based on suggestions by some of F# guru's
which i defined like below
type Name = string
type AlbumId = int
type Artist =
| ArtistId of int
| Artist of Name
type Album =
| Title of string
| Price of decimal
| Album of AlbumId * Artist
| AlbumArtUrl of string
type Genre =
| GenreId of int
| Genre of Name * Album list
enter code here
But now i unable to figure out how would i populate my discriminated union similarly i was doing with my simple F# types which are just properties ?.
Can someone help me to explain this ?. I have been reading on discriminated unions but wont say i fully understand them .
Discriminated unions are used to represent types with multiple different cases, which roughly corresponds to class hierarchies in object oriented langauges. For example, a base class Shape with two inherited classes for Circle and Rectangle might be defined like this:
type Shape =
| Rectangle of (float * float) * (float * float) // Carries locations of two corners
| Circle of (float * float) * float // Carries center and diameter
The way you defined your discriminated unions does not really do what you probably intended. Your types Album, Artist and Genre represent just a single concrete type.
You can represent these with either records (which are just like lightweight classes with just properties) or using discriminated unions with a single case, which corresponds to a single class, but has a pretty lightweight syntax, which is the main benefit. For example:
type Name = string
type Price = decimal
type AlbumId = int
type ArtistId = int
type Artist = Artist of ArtistId * Name
type Album = Album of AlbumId * Name * Price * Artist
To construct an artist together with a few albums, you can write:
let pinkFloyd = Artist(1, "Pink Floyd")
let darkSide = Album(1, "The Dark Side of the Moon", 12.0M, pinkFloyd)
let finalCut = Album(2, "The Final Cut", 11.0M, pinkFloyd)
If you then create a genre, that will contain a list of albums and possibly a list of artists, so you could write something like this:
type Genre = Genre of Name * Artist list * Album list
let rock = Genre("Rock", [pinkFloyd], [darkSide; finalCut])
The question now is, how do you actually want to populate the types. What is your data-source? If you're loading data from a database or from a XML file, you're probably want to write a function that takes some part of the data source and returns Artist or Album and after you load all albums and artists, wrap them inside a Genre and return that as a final result.
PS: It is a bit difficult to answer your questions, because you're not really giving a bigger picture of what you're trying to do. If you can give a small, but concrete example (including the loading of data and their use), then someone can help you to look at the problem from a more functional perspective.

Overloading constructor without initialization

I'm writing a generic class that has two constructors: the first one initializes every field, the second (parameter-less) should not initialize anything.
The only way I found to achieve this is calling the main constructor with "empty" arguments, i.e. Guid.Empty and null. Besides not looking good functional style to my untrained eyes, this means that I have to put a a' : null constraint on the second parameter, which I don't want:
type Container<'a when 'a : null>(id : Guid, content : 'a) =
let mutable _id = id
let mutable _content = content
new() = Container<'a>(Guid.Empty, null)
member this.Id
with get() = _id
and set(value) = _id <- value
member this.Content
with get() = _content
and set(value) = _content <- value
I see two ways to solve this:
use something like the default c# keyword instead of null (does such a thing exist in F#?)
use a different syntax to specify constructors and private fields (how?)
What is the best way to implement this class?
The F# analog to default is Unchecked.default<_>. It is also possible to use explicit fields which you don't initialize:
type Container<'a>() =
[<DefaultValue>]
val mutable _id : Guid
[<DefaultValue>]
val mutable _content : 'a
new (id, content) as this =
new Container<'a>() then
this._id <- id
this._content <- content
However, in general, your overall approach is somewhat unidiomatic for F#. Typically you'd use a simple record type (perhaps with a static method to create uninitialized containers, although this seems to have questionable benefit):
type 'a Container = { mutable id : Guid; mutable content : 'a } with
static member CreateEmpty() = { id = Guid.Empty; content = Unchecked.defaultof<_> }
In many situations, you could even use an immutable record type, and then use record update statements to generate new records with updated values:
type 'a Container = { id : Guid; content : 'a }
[<GeneralizableValue>]
let emptyContainer<'a> : 'a Container =
{ id = Guid.Empty;
content = Unchecked.defaultof<_> }
let someOtherContainer = { emptyContainer with content = 12 }
If the type will be used from languages other than F#, the following provides a natural interface in F#, and C#, for example.
type Container<'a>(?id : Guid, ?content : 'a) =
let orDefault value = defaultArg value Unchecked.defaultof<_>
let mutable _id = id |> orDefault
let mutable _content = content |> orDefault
new() = Container(?id = None, ?content = None)
new(id : Guid, content : 'a) = Container<_>(?id = Some id, ?content = Some content)
member this.Id
with get() = _id
and set(value) = _id <- value
member this.Content
with get() = _content
and set(value) = _content <- value
If it will only be used from F#, you can omit the following constructor overloads
new(id : Guid, content : 'a) = Container<_>(?id = Some id, ?content = Some content)
new() = Container()
because the overload accepting optional args handles both these cases equally well in F#.

Using NoRM to access MongoDB from F#

Testing out NoRM https://github.com/atheken/NoRM from F# and trying to find a nice way to use it. Here is the basic C#:
class products
{
public ObjectId _id { get; set; }
public string name { get; set; }
}
using (var c = Mongo.Create("mongodb://127.0.0.1:27017/test"))
{
var col = c.GetCollection<products>();
var res = col.Find();
Console.WriteLine(res.Count().ToString());
}
This works OK but here is how I access it from F#:
type products() =
inherit System.Object()
let mutable id = new ObjectId()
let mutable _name = ""
member x._id with get() = id and set(v) = id <- v
member x.name with get() = _name and set(v) = _name <- v
Is there an easier way to create a class or type to pass to a generic method?
Here is how it is called:
use db = Mongo.Create("mongodb://127.0.0.1:27017/test")
let col = db.GetCollection<products>()
let count = col.Find() |> Seq.length
printfn "%d" count
Have you tried a record type?
type products = {
mutable _id : ObjectId
mutable name : string
}
I don't know if it works, but records are often good when you just need a class that is basically 'a set of fields'.
Just out of curiosity, you can try adding a parameter-less constructor to a record. This is definitely a hack - in fact, it is using a bug in the F# compiler - but it may work:
type Products =
{ mutable _id : ObjectId
mutable name : string }
// Horrible hack: Add member that looks like constructor
member x.``.ctor``() = ()
The member declaration adds a member with a special .NET name that is used for constructors, so .NET thinks it is a constructor. I'd be very careful about using this, but it may work in your scenario, because the member appears as a constructor via Reflection.
If this is the only way to get succinct type declaration that works with libraries like MongoDB, then it will hopefuly motivate the F# team to solve the problem in the future version of the language (e.g. I could easily imagine some special attribute that would force F# compiler to add parameterless constructor).
Here is a pretty light way to define a class close to your C# definition: it has a default constructor but uses public fields instead of getters and setters which might be a problem (I don't know).
type products =
val mutable _id: ObjectId
val mutable name: string
new() = {_id = ObjectId() ; name = ""}
or, if you can use default values for your fields (in this case, all null):
type products() =
[<DefaultValue>] val mutable _id: ObjectId
[<DefaultValue>] val mutable name: string

Resources