alex said:
This is telling us something (about the type system), but not enough to
have a meaningful discussion.
The OCaml type system is deliberately restrictive. Out of 10,000 lines of
code in my latest project, this restrictiveness is advantageous (by
eliminating many errors) in all but one place. In this place, I have use an
object to weaken the type system just enough to let me do what I want.
Suppose you want to render
Spheres, ellipsoids, cones, planes, parallelepipeds, prisms, etc.
There are obviously complex RELATIONS among these, and you might want to
re-use the code due to these relations instead of just cutting and
pasting.
Absolutely. Then you factor your code into many functions. Code reuse is
then achieved by calling one function from many places. For example, if
you're after brevity, you might replace all sphere code with calls to
ellipse.
Your example is be best written in OCaml without using any OO at all.
What would your scene _object_ look like?
type aux = Sphere of sphere | Ell of ell | Cone of cone | Plane of ...
class scene = ?
My scene _object_ looks nothing like that. It looks like this:
class scene :
?fills:Smoke.Fill.t Store.t ->
?styles:Smoke.Style.geometry Store.t ->
?geometries:Smoke.ContourGeometry.t Store.t ->
basic_node -> object ('a)
method add_fill : Smoke.Fill.t -> Smoke.Fill.t Store.key * 'a
method add_geometry : Smoke.ContourGeometry.t ->
Smoke.ContourGeometry.t Store.key * 'a
method add_style : Smoke.Style.geometry ->
Smoke.Style.geometry Store.key * 'a
method append : basic_node -> int * 'a
method get_bound : Smoke.Bound.t
method get_bound_of : basic_node -> Smoke.Bound.t
method bound_of : iterator -> Smoke.Bound.t
method get_fill : Smoke.Fill.t Store.key -> Smoke.Fill.t
method get_fills : Smoke.Fill.t Store.t
method get_geometry : Smoke.ContourGeometry.t Store.key ->
Smoke.ContourGeometry.t
method get_geometries : Smoke.ContourGeometry.t Store.t
method get_root : basic_node
method get_style : Smoke.Style.geometry Store.key ->
Smoke.Style.geometry
method get_styles : Smoke.Style.geometry Store.t
method push_back : basic_node -> 'a
method push_front : basic_node -> 'a
method render : Smoke.RenderData.t -> unit
method replace_fill : Smoke.Fill.t Store.key -> Smoke.Fill.t -> 'a
method replace_geometry : Smoke.ContourGeometry.t Store.key ->
Smoke.ContourGeometry.t -> 'a
method replace_root : basic_node -> 'a
method replace_style : Smoke.Style.geometry Store.key ->
Smoke.Style.geometry -> 'a
end
It has nothing to do with shapes, geometries and so forth. It is only to do
with the way I chose to structure the whole library. Specifically, it pulls
everything together in a way that can then be used by all of the parts of
my library independently, without having to know about each other. Thus, it
evades a big mutual dependency.
You want to talk about the variant type which implements a scene. I have
chosen to use a variant type (much better than a class hierarchy):
type ('leaf, 'loner, 'group) generic_node =
GenericLeaf of 'leaf
| GenericLoner of 'loner * ('leaf, 'loner, 'group) generic_node
| GenericGroup of 'group * ('leaf, 'loner, 'group) generic_node list
Note that it is both generic and extensible. The functions which act over
this type are also both polymorphic and extensible. I have several levels
of progressively more specialised scenes and functions, allowing the user
to choose how much functionality they want to implement themselves.
This functionality can actually be achieved (albeit unsafely) in C++ and
Java but the code required is so convoluted that nobody would ever think to
write it. In OCaml, the best approach is often the most natural and mose
concise.