liveBook · Manning

Data type

As explained in F# in Action: A Revised Edition of Get Programming with F#

A type is a language-level classification that specifies the kind of values an expression can represent and the operations applicable to them.

A data type classifies values and lets the compiler enforce which values and operations fit together. In F#, types are inexpensive to create, so they can model business concepts rather than merely storage shapes. A City and a Country may both contain strings, yet distinct wrapper types can prevent them from being mixed accidentally. section 1.5.2

Types work especially well when an application has rules that must hold before an operation can proceed. Representing a validated value with a dedicated type moves checking to one clear boundary: code that sends email can require a ValidatedEmail instead of repeatedly asking whether an ordinary string is valid. section 14.1.1

type ValidatedEmail =
    private | ValidatedEmail of string                             #1
    member this.Value = match this with (ValidatedEmail e) -> e    #2
    static member Create (unvalidatedEmail:string) =               #3
        if unvalidatedEmail.Contains "@" then
            Ok (ValidatedEmail unvalidatedEmail)
        else
            Error "Invalid email!"
 
let sendEmailSafe message (email:ValidatedEmail) =                 #4
    ...
    ...
    ...
    Ok "Sent message"
 
let sendResult =
    ValidatedEmail.Create "isaac@email.com"
    |> Result.bind (sendEmailSafe "Welcome!")                      #5

ValidatedEmail has a private representation and a Create operation. sendEmailSafe accepts only the validated type, so callers must validate before sending. section 14.1.1

This approach narrows testing to the code that creates the validated value. Once a ValidatedEmail exists, sendEmailSafe can follow its happy path; the type prevents calls using an unvalidated email. Types can therefore encode prerequisites as well as describe data. section 14.1.1

Types also support a data-and-functions design:

  • A function transforms data of one type into data of another type. section 1.5.5
  • Functions such as validation, invoice creation, and email delivery can be composed into a pipeline. section 1.5.5
  • Discriminated unions let the compiler check whether pattern matching handles every possible case. section 14.1.1

Guide type inference when an error spreads

F# infers types from usage. If calculateGroup compares age with the string "test", the compiler infers age as a string; later calls passing integers then fail too. Inspecting the inferred signature, such as string -> string, reveals the assumption. Add a temporary annotation that says age is an int to direct inference and place the error at the mistaken comparison. section 3.2.4

let calculateGroup age =                              #1
    if age < 18 then "Child"
    elif age < 65 then "Adult"
    else "Pensioner"
 
let sayHello someValue =                              #2
    let group =                                       #3
        if someValue < 10.0 then calculateGroup 15
        else calculateGroup 35
    "Hello " + group
 
let result = sayHello 10.5                            #4

Use the small calculateGroup and sayHello script to observe how one changed literal alters an inferred input type and produces errors at call sites. section 3.2.4

Specific types are optional, not mandatory, but using them early makes domain rules visible in code and guards against incorrect data combinations. F# types are also intended to be usable from C#, with some caveats when crossing the language boundary. section 1.5.2 section 11.2

How F# in Action: A Revised Edition of Get Programming with F# covers it

How deep it goes
Explains domain-specific wrappers, validated types, discriminated unions, and practical type-inference diagnosis rather than type-system internals.
Examples
Uses City and Country wrappers, ValidatedEmail email sending, Instrument matching, and calculateGroup inference errors.
What it assumes
Introduces F# type use through application data and functions without assuming prior F# or .NET knowledge.
What you can do after
Apply it to model validated domain values, enforce prerequisites, and guide unexpected F# type inference.
How it connects
Connects types with type inference, discriminated unions, pattern matching, automated tests, function pipelines, and C# interoperability.

Also explained in

Compare how 18 books explain data type →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of F# in Action: A Revised Edition of Get Programming with F#. The section links point to the parts of the book it draws on.