13
votes

What exactly is the difference between F#'s type augmentation and type extension, and do we really need both?

Are there situations where one is better than the other, and vice-versa?

I'm asking because I recently had a lecture in F# where the lecturer talked about both, and afterwards commented that he couldn't see the reason why both were included in the F# language.

Update:

Ok, so Vladislav Zorov links to a page with examples of using type augmentation both when defining your own types, and extending (or augmenting?) an external type.

pad links to an MSDN page where they call it intrinsic and optional type extension.

Both seem to illustrate the same thing. Can someone come with a concrete example of type extension and another concrete example of type augmentation perhaps, in order to explicitly clarify what the two things are exactly?

2
You seem to have us mixed up :) I'm linking to Tomas Petricek's blog, together with Jon Skeet and Yin Zhu they wrote this - manning.com/petricek , which is IMHO one of the greatest, most detailed books on F# ever written.Vladislav Zorov

2 Answers

11
votes

The following bits from MSDN's Type Extensions page are relevant (emphasis mine):

There are two forms of type extensions that have slightly different syntax and behavior. An intrinsic extension is an extension that appears in the same namespace or module, in the same source file, and in the same assembly (DLL or executable file) as the type being extended. An optional extension is an extension that appears outside the original module, namespace, or assembly of the type being extended. Intrinsic extensions appear on the type when the type is examined by reflection, but optional extensions do not. Optional extensions must be in modules, and they are only in scope when the module that contains the extension is open.

The purpose of optional extension is clear. It helps you inject new functionalities to types not belonging to your assemblies. For examples, FSharpx uses it to create various helpers for parsing primitive types:

open System

type Boolean with
    static member parse x =
        match bool.TryParse(x) with
        | true,v -> Some v
        | _ -> None

Why do you need intrinsic extension then? The answer is its convenience. I find it useful to break down type definitions to multiple sections with clear purposes.

In many F# libraries, I saw the use of the following pattern: type definition -> utility functions -> intrinsic extension. In this way, you can define sophisticated utility functions on your types, make them available in modules and still can use them directly in your member definitions. You can look at Complex type in F# PowerPack to see the pattern.

EDIT:

To be honest, I often use type extension and type augmentation interchangeably. The thing that matters is whether they are intrinsic or optional.

5
votes

They are different things. Type augmentations, when defined in the same namespace, module and source file, actually become part of the type when compiled. Type extensions (a.k.a. type augmentations for types outside of the module and source file) are implemented with .NET extension methods.

They both use the same syntax, the only difference is whether the type you mention is in the same namespace and assembly, i.e. you're augmenting your own code and the additional methods can be added to your type before compilation.

Source: http://tomasp.net/blog/fsharp-iii-oop.aspx

Edit:

This is a terminology mix-up, they are both referring to the same thing - intrinsic extensions are type augmentations of the first kind (i.e. same namespace and assembly), optional extensions are type augmentations of the second kind (i.e. 3rd party assembly, in the blog post this is the List<T> augmentation example).

I assume when your lecturer is talking about type augmentations, he's referring to intrinsic extensions, i.e. first kind type augmentations, and when he's talking about type extensions, he's talking about optional extensions, or second kind type augmentations.