All programming languages have types. Some languages have more fleshed-out type systems than others.
Not all languages have the concept of a unit of measure.
My goals in this talk:
At its core, a type is a restriction limiting you to certain values.
The int
type lets you use valid integers (typically within 4 bytes), so "Bob" will never be a proper integer but 3 is.
With C#, we are used to defining types.
Unless we don't want to.
With F#, we are used to not defining types.
Unless we want to.
The easiest way to avoid null pointer references? Avoid null
!
F# has the Option
type, which may be either Some {object}
or None
. Therefore, it always has a value and cannot be null
.
Product types are the combination of multiple values. The key product types we'll look at are tuples and record types.
Tuples exist in F# and C# both, and with the System.ValueTuple
library, C# gets many of the in-built F# advantages around tuples.
We write a tuple as a comma-separated list, like (1, 2, 3, "Dog")
but the complier interprets it as (int * int * int * string)
.
A tuple is a thing as much as it is a collection of things.
If you are calling a C# (or VB.Net) function from F#, you may only pass in one thing. If the BCL method has multiple required inputs, you must pass in a tuple.
Record types are product types with labels for each input. The "development feel" of a record type is similar to that of a struct
over a class, as record types do not have associated methods, accessors, or mutators.
C# 9 has introduced record types.
In contrast to product types, which are the product of multiple inputs, sum types are the sum of multiple inputs.
Another way to think of this is, product types chain together AND
operations, while sum types chain OR
operations.
In F#, the sum type is also known as a discriminated union. We define a thing as one of the valid set.
Sum types replace if-else
logic and try-catch
blocks. Suppose we have a web request.
Unit is the representation of a category with a single element.
Values go here to die.
Unit guarantees that our expressions always return a value. This is part of the definition of an expression. The unit type informs us that we don't care about the value, per se, but it is still a value.
C# does not have a unit type and so void versus non-void methods need to remain separate. This is why we have both Func
and Action
.
Sometimes the built-in types are too lax. For example, we can represent a set of prime numbers as an array of integers, but that won't help us avoid slipping in a 4.
This is where custom types come into play. In C#, you can build a class which behaves like a custom type would.
Additional rules narrow the range of acceptable values beyond what built-in types can handle.
Ensure at compile time that any value of this custom type is guaranteed to follow your rules.
Prevent sending in the wrong parameter.
Ever done this before?
That's because x and y are both integers, so the compiler can't protect us.
But this is a lot harder to mess up:
Hard to create a class for every possible field, however.
Units of measure are not available in C#. These allow the F# compiler to prevent you from making units-based mistakes, like adding feet and pounds.
With units of measure, we can:
F# extends the native .NET type system. Important things we covered today include:
To learn more, go here:
https://csmore.info/on/types
And for help, contact me:
feasel@catallaxyservices.com | @feaselkl
Catallaxy Services consulting:
https://CSmore.info/on/contact