Which style is better to declare a type in Ocaml?
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 necessar开发者_JAVA百科y and better than the first style?
Thank you very much!
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.
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).
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).
精彩评论