Dependent Types via an F# Type Provider
Let's look at the case of a Product Description from some hypothetical domain. Typically you'd make a decision on what constitutes a valid Product Description, then write some static validation functions that are utilized when checking a string input that is meant to be used as a Product Description. Thereafter, the string would just be a string, and we'd rely on labels (local bindings and property names) to distinguish it as a Product Description.
This library allows us to encode those rules about what constitutes a valid Product Description into a type.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: |
|
Creating and Using Instances
There are two ways to create instances of our dependent types: constructors and static factory methods.
The Factory methods are preferred, as these are total functions that produce an Option
type.
The constructors will throw an exception if the input does not conform.
1: 2: 3: 4: 5: 6: |
|
Type Erasure
All the provided types in this library are erased to their underlying primitive types. Therefore there is very little (if any) runtime overhead for using these dependent types, aside from the necessary validation of inputs.
This may seem strange for string
, since that is a sealed type, but this isn't inheritance. This
type provider is a program used by the compiler to create dependent types that constrain the range
of the type they are erased to; if you are familiar with units of measure, it is semantically very
similar.
1: 2: |
|
Bounded Numbers
Commonly we use int
in our code out of convenience, where a different primitive might be more
efficient; the range of the value might fit into a much smaller type. Nevertheless, the real problem
here is that we should be able to define richer semantics in our domain without having to sacrifice
performance.
This library supports erased bounded types for all numeric primitives. Let's use a couple of these to create some new types for our contrived example.
1: 2: 3: 4: 5: |
|
Validation
The bounds for the Bounded____
types are available as static properites.
1:
|
|
Full name: Tutorial.ProductDescription
Full name: Tutorial.ProductName
Full name: Tutorial.OneChar
{Name: ProductName;
Description: ProductDescription;}
Full name: Tutorial.Product
Full name: Tutorial.newProduct
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
Full name: Microsoft.FSharp.Core.option<_>
Full name: Tutorial.p
Full name: Tutorial.productName
{Product: Product;
UnitPrice: ProductPrice;
Quantity: AllowedQuantity;}
Full name: Tutorial.LineItem
LineItem.Product: Product
--------------------
type Product =
{Name: ProductName;
Description: ProductDescription;}
Full name: Tutorial.Product
Full name: Tutorial.ProductPrice
Full name: Tutorial.AllowedQuantity