------------------------------------------------------------------------------

TODO (REALLY)

Better clean up & share code at the three call sites of [bulk].

------------------------------------------------------------------------------

TODO (PERHAPS)

Document hexpr_polymorphic. Make VisitorsHashcons available as a library.

If there is an error, then the warnings are never seen,
  because they are placed in the generated code.
  Can we fix this?
  e.g. type t = A of (int -> int)[@opaque]

In fully [polymorphic] mode, perhaps one could allow [@@deriving visitors]
  to be used in an .mli file, producing class types.

In [polymorphic] mode, we could annotate every invocation
  of an external visitor method with its expected (polymorphic) type,
  so as to get better type error messages if this method does not have
  the expected type.

Ideally, a visitor method should be parameterized with visit_'a
  only if 'a appears in the type of some component.
It would be good if a type parameter ['a] could be declared never-visited,
  so the method or function [visit_'a] would be unneeded.
  Could be useful for phantom type parameters, GADTs, 'bn, etc.
  The problem is, when a nonlocal type constructor is applied,
  we cannot know which parameters are phantom.
  Unless we find a way of declaring that, too?

Think about enabling [polymorphic] and [fold] together.
That would require letting the user specify the result type
  associated with each type constructor.

Implement and document endoreduce?
  Share code by using "Map endo" internally where endo : bool.

Maybe [fold] and [fold2] in VisitorsRuntime should just be aliases
  for [map] and [map2]. The user can use [nude] if that it is not appropriate.

Once we have that, can we deal with GADTs?

In [fold],
  the build_ methods could take not only the results of the recursive calls,
  but also their arguments (for added expressive power). That would be a
  true "recursor" (David Chemouil).

Could we have visitors where a state is explicitly threaded from left to right?
  (David Chemouil.)
For greater generality, maybe we should have monadic visitors.
Currently, the environment monad (a reader monad) is built-in.
Could we let the user choose which monad should be used,
  without breaking compatibility?

Develop a real test suite, with expected output.
  Check for left-to-right traversal order.
  Release the test suite, too?
  Some tests have dependencies on other packages: hashcons, core_bench...
  Run these tests only if these packages are installed, warn otherwise?

Add [opaque] as an option, carrying a list of types.
  That would be lighter than writing [@opaque] at every occurrence.

Include an option [except t] to omit the definition of visitor methods for the type [t].
  That would allow the user to provide their own implementation,
  (possibly inherited / polymorphic),
  without having to satisfy the type constraints imposed by our implementation.
  e.g. could generate a [map] visitor where one type (in a family) is rewritten to something completely different

Detect and reject existential types and GADTs.

Could define a fold visitor where the methods receive the names of the types,
data constructors, and record fields that are being visited. (As in
ppx_tools/genlifter.)

Develop [@deriving zippers] to produce a type of zippers,
  and add an option for the environment to be a zipper
  that is extended at every recursive call. (Yann Régis-Gianas.)
  Parameterize the type of zippers by the type of their root
  and allow the constructor Nil only when the root type and
  the current type coincide. (GADT.)
  So that we get n zipper types out of n source types.

Avoid generating beta-redexes.
  (fun (x, y) -> ...) z should be let (x, y) = z in ...
  See [visit_types].

Re-introduce hoisting of closure allocations of the form [self#visit_foo]?
  If so, share them when they have several occurrences.

Think about generating analyze_ methods
  which perform a fixed point computation (use Fix)
  based only on the type structure
  so as to allow a static analysis of the type structure,
  which could be exploited to optimize runtime traversals.
  One would have to lose precision at parameterized types, or expand them away.
