Symbol.Map.t is not a type, but a type constructor, basically a mapping from types to types. state Symbol.Map.t means the type obtained by applying the constructor Symbol.Map.t to the type state as argument. In the page you linked, this corresponds to the rule typexpr ::= typexpr typeconstr.
In state Symbol.Map.t, state is to Symbol.Map.t the same as in int list, int is to list. list is a type constructor, int is a type, and int list is a type. A type constructor takes a type (or many) as argument and results in a type. It can be considered a function over types that that takes types as arguments and results in a type.
It makes sense for me to treat state Symbol.Map.t as a legal type obeying the OCaml syntax. But I still get some questions for type constructor.
How can I tell if a type is a generic type (type constructor) or a concrete type? Is type declared in a module without specifying a concrete type (like int, string) generic?
Also, I guess there are some rules for type constructors. It cannot take any type as its argument, right? In this example, why Symbol.Map.t can take state as its argument? I guess its due to [@@deriving compare, hash, sexp], but not quite sure.
I searched type constructor in Real World Ocaml and didn’t find much results, how can I read more about it? Again, thank you for your helps!
By looking at the definition: type constructors have parameters, as in type 'a t or type ('a, 'b) t, while plain types do not have any parameters (eg type t). “Plain” types can be considered zero arity type constructors.
Type constructors themselves can take any type as argument. In your example state could be replaced by any type whatsoever. The [@@deriving ...] annotations are there for the “key” type, but for the “value” type (state in your case) there are no restrictions at all.
Just to round out the explanation a bit more, in OCaml _ t is also valid syntax for generic types i.e. type constructors. It means ‘t takes a type parameter but I don’t care about its name right now’.
And finally, OCaml also allows a limited form of type constraints on generic type parameters, so you can have something like type 'a t = 'a list constraint 'a = int, which as you can imagine, forces always only the int type as the type parameter here. Constraints are a more advanced technique so their use is more limited.