Take the 2-minute tour ×

I often need to declare a type which contains a map or a list, for instance:

type my_type_1 = my_type_0 IntMap.t
type my_type_2 = my_type_0 List

Also I have seen another style of declaration which encapsulates map or list in a record, for instance:

type my_type_1 =
  | Bot_1
  | Nb_1 of my_type_0 IntMap.t
type my_type_2 =
  | Bot_2
  | Nb_2 of my_type_0 List

My question is, whether there are some cases where the second style is necessary and better than the first style?

Thank you very much!

share|improve this question
add comment

3 Answers

up vote 1 down vote accepted

The two types you give are not equivalent, because of the Bot constructor added in the second case. This means that the two my_type_1 do not have the same semantics. Incidentally, the construction Bot | Foo of 'a is already provided by the standard type 'a option, with constructors Some and None, so the type my_type_1 of your second sample is equivalent to a my_type_1 option in the first one.

Whether to use an option type or your own constructors names is up to you. In general, I would advise to you an option type if the semantics of your type coincides with the option idea of failure, being absent, or being undefined. Given your name Bot, I assume this is probably what you're doing, but defining your own constructor names is also ok and can be clearer in some circumstances. The matter has been discussed in depth in this blog post from ezyang.

Now, assuming your two types definition were equivalent (that is, in absence of the Bot) constructor, what's the purpose of adding an algebraic datatype layer, a new constructor, instead of using a simple type alias ? Well, it has the effect of making your type distinct from the representation type. For example, if you define type 'a stack = Stack of 'a list, 'a stack and 'a list cannot be confused for each other, and the compiler will raise an error if you do. So that can be used to enforce a (light) type separation, with the constructor acting as a type annotation:

let empty = Stack []
let length (Stack li) = List.length li

I'd say it's mostly a matter of taste, but I would recommend using an algebraic datatype instead of an alias when you want to be sure that there can be no mistake with the original type. The downside is that you have to wrap the operations of the original datatype, as I did in my length function above.

share|improve this answer
add comment

Those are not different styles, but different types: the first type declarations are an abbreviation for a specialized instance (for mytype_0) of the polymorphic List, or IntMap.

The second set of definitions present a "constructed" type, for which Bot_1 (and Bot_2) provide values. Those "alternatives" can be used, for example, to create functions of type T -> my_type_1 which return Bot_1 in a special case where the computation doesn't allow to return a list, in a similar way of what an option type permits. This is impossible with the first set of definitions (who must always provide the required list payload).

share|improve this answer
Thanks for your comment –  SoftTimur Jul 18 '11 at 11:46
add comment

The second one isn't a "record" (which is a different thing). It creates an algebraic data type. I'm not sure how to explain it but if you've used Haskell or Standard ML you'll know. It's basically a tagged union. A my_type_1 is either a Bot_1 (which carries no data) or a Nb_1 (which carries a my_type_0 IntMap.t as data).

The first one is simply a type synonym (like a typedef in C).

share|improve this answer
add comment

Your Answer


