7

Ocaml programmers can use so called 'phantom types' to enforce some constraints using the type system. A nice example can be found at http://ocaml.janestreet.com/?q=node/11.

The syntax type readonly doesn't work in F#. It could be replaced with a pseudo-phantom type defined as type readonly = ReadOnlyDummyValue in order to implement the tricks in the above mentioned blog post.

Is there a better way to define phantom types in F#?

Miron Brezuleanu
  • 2,989
  • 2
  • 22
  • 33

1 Answers1

16

I think that defining type just using type somename won't work in F#. The F# compiler needs to generate some .NET type from the declaration and F# specification doesn't explicitly define what should happen for phantom types.

You can create a concrete type (e.g. with type somename = ReadOnlyDummyValue) in the implementation file (.fs) and hide the internals of the type by adding just type somename to the interface file (.fsi). This way you get quite close to a phantom type - the user outside of the file will not see internals of the type.

Another appealing alternative would be to use interfaces. This sounds logical to me, because empty interface is probably the simplest type you can declare (and it doesn't introduce any dummy identifiers). Empty interface looks like this:

type CanRead = interface end
type CanWrite = interface end

The interesting thing, in this case, is that you can also create inherited interfaces:

type CanReadWrite = 
  inherit CanRead
  inherit CanWrite

Then you can write a function that can take values of type Ref<CanRead, int> but also values of type Ref<CanReadWrite, int> (because these values also support reading):

let foo (arg:Ref<#CanRead, int>) = // ...    

This seems like something that could be useful. I would be actually quite interested whether this can be done in OCaml too (because it relies on the F# support for interfaces and inheritance).

sdgfsdh
  • 33,689
  • 26
  • 132
  • 245
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • The suggestion to use interfaces is great. Thanks! – Miron Brezuleanu Aug 28 '10 at 05:49
  • @Jon: I was expecting that. I just didn't know how and couldn't find any good source of information. Do you have a link to some example (preferably freely available ;-))? – Tomas Petricek Aug 29 '10 at 15:42
  • @Tomas: We used phantom types in the Citrix code base to distinguish between different kinds of UUID, catching any errors at compile time. The only difference is that you can use structural types in OCaml to achieve the same effect without having to define any types by hand. – J D Aug 29 '10 at 16:35
  • @Tomas: Here is a simple example: http://camltastic.blogspot.com/2008/05/phantom-types.html – J D Aug 29 '10 at 16:37
  • @Jon: Thanks for the link - it works quite nicely thanks to structural types. I guess interfaces in F# are at least to some point closer to the original OCaml version. – Tomas Petricek Aug 29 '10 at 17:09
  • 1
    A type-safe SQL query helper module that uses exactly this idea: https://gist.github.com/yawaramin/552c25a23549f15556d54954e39f946d – Yawar Feb 23 '17 at 04:04