Grant Olson wrote:
> This is really a design question more than anything. I'm goofing around
> with some geometry equations involving planes. I'm defining the planes as a
> point on the plane and a normal to the plane. So I got the bright idea that
> points and vectors should be different types, even though they basically
> have the same structure and same operations performed on them:
>
> type point = {x:float;y:float;z:float}
> type vector = Vector of point
> type plane = Plane of point * vector
>
> It turns out this is cumbersome. I basically keep on unwrapping the normal
> to perform operations. Is this a good way to do things? Should I just have
> a point be synonymous with a vector and rely on context? Should I define the
> plane as 'type plane {p:point;n:point}' or 'type plane {p:point;n:vector}'
> Are there any other approaches anyone would suggest?
>
> I'm just trying to get this right early on so I don't have to reHacktor
> later.
>
> Any input is appreciated,
>
> Grant
>
>
Your problem is the usual balance between on one side safety
(bug-catching), and on the other side ease-of-program-writing (length of
code, versatility) and (if you care) execution time. So it depends how
much bugs you expect to avoid by doing it the clean way
For a small
throwaway program and if you know well enough what you're doing,
distinguishing vectors for points may well be overtyping. For a longer
program, a library, or if you feel cleanliness can spare you
hard-to-spot bugs, the cleaner way may be the way.
Now if you go for the clean way:
probably the cleanest way to go, without loss of (execution time)
efficiency, is to have a single type internally, and differentiate a the
interface level:
(* untested code and not complete anyway, may contain typos *)
module Geometry : sig
(* or put this part in geometry.mli *)
type coords3 = {x:float; y:float; z:float}
type point (* abstract type : the implementation of point is
hidden *)
type vector (* abstract type : the implementation is hidden *)
val add_vp : vector -> point -> point
val add_vv : vector -> vector -> vector
val coords_v : vector -> coords
val vector : coords -> vector
...
end = struct
(* or put this part in geometry.ml *)
type coords3 = {x:float; y:float; z:float}
type point = coords3
type vector = coords3
let add_cc p q = {x=p.x +. q.x; etc}
let add_vp = add_cc
let add_vv = add_cc
let coords_v x = x (* there are wizard ways to optimise this
do-nothing function, but this will do for a beginning *)
...
end;
open Geometry
let v = vector {x=1.0; y=2.0; z=3.0} (*ok*)
let p : point = v (* type error *)
Also, in any case I would definitely favour
type plane = {p: point; n: vector}
or
type plane = {p: point; normal: vector}
over
type plane = Plane of point * vector
(or the even worse Plane of coords3 * coords3)
as because the interpretation of the parameters of a plane is
non-trivial, it is better to give them names.
Frédéric
.