In F# can I represent a restricted type without defining a class? Lets say I want to represent all the pairs of number where the first divides the second.
in C# I could do:
class PairDivides
{
int a {get;private set;}
int b {get;private set;}
PairDivides(int first, int second)
{
if (a % b != 0)
{
throw new Exception();
}
this.a = first;
this.b = second;
}
}
Now it's impossible to create a PairDivides
instance where b
doesn't divide a
...
Can this be done in F# using only the functional constructs (record, discriminated union, maybe active patterns, etc.)?
I want to be able to create and receive something like these pairs being certain that they are constructed correctly.
Encapsulation in Java is the process by which data (variables) and the code that acts upon them (methods) are integrated as a single unit. By encapsulating a class's variables, other classes cannot access them, and only the methods of the class can access them.
Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.
Create INSTANCE of same class by instantiating class & this INSTANCE should be with private & static modifier. Provide public static method that returns same INSTANCE of class every time. Finally, create private constructor so that no-one create object from outside of class.
You can do this by making the type private. The only downside is that you then need to provide functions to access the data:
module Pairs =
type PairDivides = private { a: int; b: int }
let createPairDivides a b =
match a % b with
| 0 -> Some { PairDivides.a = a ; b = b }
| _ -> None
let print div =
printfn "{%d %d}" div.a div.b
let tryPrint div =
match div with
| Some a -> print a
| None -> printfn "None"
let a = Pairs.createPairDivides 2 2
let b = a.Value
// This is inaccessible: b.a
Pairs.createPairDivides 2 2 |> Pairs.tryPrint
Pairs.createPairDivides 2 3 |> Pairs.tryPrint
By providing a function to create the pair, and functions to use or extract from it as necessary, you completely eliminate the ability to create invalid pairs (you'll get None
back instead of a bad pair) without using exceptions.
The disadvantage is you need to provide mechanisms to extract the values from the pairs, as the type is now inaccessible when used outside of your current module.
That being said, there's nothing wrong with doing this via classes either. You can get the same level of enforcement via creating a class, if you prefer:
type PairDivides private (a,b) =
member __.A = a
member __.B = b
static member Create a b =
match a % b with
| 0 -> Some(PairDivides(a,b))
| _ -> None
PairDivides.Create 2 2 |> printfn "%A"
PairDivides.Create 2 3 |> printfn "%A"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With