caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Subtyping structurally-equivalent records, or something like it?
@ 2010-05-01 16:04 Anthony Tavener
  2010-05-01 16:51 ` [Caml-list] " Stéphane Lescuyer
  0 siblings, 1 reply; 5+ messages in thread
From: Anthony Tavener @ 2010-05-01 16:04 UTC (permalink / raw)
  To: caml-list

[-- Attachment #1: Type: text/plain, Size: 4235 bytes --]

I have this:

  type kinematic = { lin: Vec.t; ang: Vec.t }

Which I've been using to represent a medley of physical attributes (force,
momentum, velocity, etc.).

As the physics code becomes increasingly substantial I'm running into
possible human-error, like passing a momentum where a force is expected, or
a mass instead of inverse-mass (mass is also a record though different, but
inv-mass has the same structure as mass). So I'd like to make distinct
types, such as:

  type position = { r: Vec.t; theta: Vec.t }
  type acceleration = { a: Vec.t; alpha: Vec.t }
  type force = { f: Vec.t; tau: Vec.t }

They are structurally equivalent, and ideally I'd like to be able to treat
these as 'kinematic' too, since that is how I would express the arithmetic
and common functions on these types (add, mul, etc).


I'm sure I've seen posts on this before but I can't find them now (though
what I remember are questions about having distinct 'float' types, such as
for degrees vs radians, rather than records).

I know OCaml doesn't have subtypes for records, which is effectively what
I'm looking for. Though this case is a bit more specialized that that... all
the subtypes and base type are structurally equivalent. Code for one of
these types would technically work on any... but is there a way to inform
the type system of my intentions?


I hope someone has a better option than those I've considered, or that I
have a grave misunderstanding somewhere and one of these options is actually
good:

1. Objects. Subtyping makes these a natural fit, but in this case I don't
need anything else which objects provide, and a certainly don't need the
poor performance or method-calling mixed in with my computational code
(aesthetically... yucky, to me). Again, each type is structurally
equivalent. Just some functions want certain types.

2. Using distinct records for each type, but no 'kinematic' base type, so
all common operations are duplicated for each new type. No performance hit.
But the redundant code is horrible -- makes extensions a pain, and a
potential bug-source.

2b. Same as above, but putting the common code in a functor which we apply
on all the different types. I think this will add some overhead, since the
signature of the types (below) would demand accessor functions for the
record fields, in order to uniformly get the fields from the different types
(again, even though they are structurally equivalent) -- these calls
probably wouldn't get optimized out. But maybe there is a better way to do
this?

  module type KINEMATIC = sig
    type t
    val lin : t -> Vec.t
    val ang : t -> Vec.t
  end

3. Making all the other types different aliases of 'kinematic'; then using
explicit type declarations in function parameters and coercion to
'kinematic' when needed. This makes some ugly code, and the added-typesafety
is almost illusory. This is kind-of like 'typesafe' C code doing typecasting
gymnastics.

4. Adapter functions: 'kinematic_of_force: force -> kinematic', etc. as a
way to use the common set of 'kinematic' functions. This is clunky and comes
with a big performance hit unless these functions became like
type-coercions. If there is a way this can be done with zero runtime cost,
I'd accept the clunkiness. :)

Any thoughts?


I've been using OCaml for a few years now, but this is my first post. I feel
many of you are familiar online personae through reading archives, blogs,
and websites. Thank-you for all the help I've absorbed through those various
channels. And thanks to those making the language I favor for most tasks!

Briefly introducing myself: I've been a professional video-game developer
for 15 years, most recently specializing in AI. I quit my last job almost
two years ago to travel and program (95% in OCaml!), and am developing a
game now. I've seen indications over the years of other game developers
taking the plunge and then parting ways with OCaml, surely back to C++. I
see OCaml as viable and certainly more pleasurable, even with avoiding
mutation. But within a pressure-cooker environment (working for $$ from
someone else) people fall back on what they are most familiar with... also
you can't go too rogue while still being part of a team. :)

-Anthony Tavener

[-- Attachment #2: Type: text/html, Size: 4546 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Caml-list] Subtyping structurally-equivalent records, or  something like it?
  2010-05-01 16:04 Subtyping structurally-equivalent records, or something like it? Anthony Tavener
@ 2010-05-01 16:51 ` Stéphane Lescuyer
  2010-05-02  2:59   ` Anthony Tavener
  0 siblings, 1 reply; 5+ messages in thread
From: Stéphane Lescuyer @ 2010-05-01 16:51 UTC (permalink / raw)
  To: Anthony Tavener; +Cc: caml-list

Hi Anthony,
I think that maybe using phantom types could do the trick : consider
defining empty types for all the different "kinds" of similar
constructs that you have, and then define the kinematic record with a
phantom parameter type.

type position
type acceleration
type force

type 'a kinematic = {lin : Vec.t; ang: Vec.t}

By adding some extra typing annotations, you can then constraint your
functions to accept (or produce) only a given kind of construct, say
for example a position kinematic :

let pos_0 : position kinematic = {lin = Vec.origin; ang = Vec.origin }

let double_force (v : force kinematic) : force kinematic = {lin =
Vec.mult 2. v.lin; ang = v.ang }

double_force pos_0 -> does not type check

You can write generic functions as add, norm, products, etc : they are
just polymorphic with respect to the phantom type parameter. By the
way you ensure that you are not multiplying apples and carrots :
let plus (v : 'a kinematic) (v' : 'a kinematic) : 'a kinematic = ...

Of course, the overhead is that you have to explicitely write some
type annotations, especially for constructors, but the runtime
overhead is exactly 0. And also, one limitation is that you can't use
different projection names for the different cosntructs, since it is
really always the same record type that you are using.

I hope this helps,

Stéphane L.

On Sat, May 1, 2010 at 6:04 PM, Anthony Tavener
<anthony.tavener@gmail.com> wrote:
> I have this:
>
>   type kinematic = { lin: Vec.t; ang: Vec.t }
>
> Which I've been using to represent a medley of physical attributes (force,
> momentum, velocity, etc.).
>
> As the physics code becomes increasingly substantial I'm running into
> possible human-error, like passing a momentum where a force is expected, or
> a mass instead of inverse-mass (mass is also a record though different, but
> inv-mass has the same structure as mass). So I'd like to make distinct
> types, such as:
>
>   type position = { r: Vec.t; theta: Vec.t }
>   type acceleration = { a: Vec.t; alpha: Vec.t }
>   type force = { f: Vec.t; tau: Vec.t }
>
> They are structurally equivalent, and ideally I'd like to be able to treat
> these as 'kinematic' too, since that is how I would express the arithmetic
> and common functions on these types (add, mul, etc).
>
>
> I'm sure I've seen posts on this before but I can't find them now (though
> what I remember are questions about having distinct 'float' types, such as
> for degrees vs radians, rather than records).
>
> I know OCaml doesn't have subtypes for records, which is effectively what
> I'm looking for. Though this case is a bit more specialized that that... all
> the subtypes and base type are structurally equivalent. Code for one of
> these types would technically work on any... but is there a way to inform
> the type system of my intentions?
>
>
> I hope someone has a better option than those I've considered, or that I
> have a grave misunderstanding somewhere and one of these options is actually
> good:
>
> 1. Objects. Subtyping makes these a natural fit, but in this case I don't
> need anything else which objects provide, and a certainly don't need the
> poor performance or method-calling mixed in with my computational code
> (aesthetically... yucky, to me). Again, each type is structurally
> equivalent. Just some functions want certain types.
>
> 2. Using distinct records for each type, but no 'kinematic' base type, so
> all common operations are duplicated for each new type. No performance hit.
> But the redundant code is horrible -- makes extensions a pain, and a
> potential bug-source.
>
> 2b. Same as above, but putting the common code in a functor which we apply
> on all the different types. I think this will add some overhead, since the
> signature of the types (below) would demand accessor functions for the
> record fields, in order to uniformly get the fields from the different types
> (again, even though they are structurally equivalent) -- these calls
> probably wouldn't get optimized out. But maybe there is a better way to do
> this?
>
>   module type KINEMATIC = sig
>     type t
>     val lin : t -> Vec.t
>     val ang : t -> Vec.t
>   end
>
> 3. Making all the other types different aliases of 'kinematic'; then using
> explicit type declarations in function parameters and coercion to
> 'kinematic' when needed. This makes some ugly code, and the added-typesafety
> is almost illusory. This is kind-of like 'typesafe' C code doing typecasting
> gymnastics.
>
> 4. Adapter functions: 'kinematic_of_force: force -> kinematic', etc. as a
> way to use the common set of 'kinematic' functions. This is clunky and comes
> with a big performance hit unless these functions became like
> type-coercions. If there is a way this can be done with zero runtime cost,
> I'd accept the clunkiness. :)
>
> Any thoughts?
>
>
> I've been using OCaml for a few years now, but this is my first post. I feel
> many of you are familiar online personae through reading archives, blogs,
> and websites. Thank-you for all the help I've absorbed through those various
> channels. And thanks to those making the language I favor for most tasks!
>
> Briefly introducing myself: I've been a professional video-game developer
> for 15 years, most recently specializing in AI. I quit my last job almost
> two years ago to travel and program (95% in OCaml!), and am developing a
> game now. I've seen indications over the years of other game developers
> taking the plunge and then parting ways with OCaml, surely back to C++. I
> see OCaml as viable and certainly more pleasurable, even with avoiding
> mutation. But within a pressure-cooker environment (working for $$ from
> someone else) people fall back on what they are most familiar with... also
> you can't go too rogue while still being part of a team. :)
>
> -Anthony Tavener
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>
>



-- 
I'm the kind of guy that until it happens, I won't worry about it. -
R.H. RoY05, MVP06


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Caml-list] Subtyping structurally-equivalent records, or  something like it?
  2010-05-01 16:51 ` [Caml-list] " Stéphane Lescuyer
@ 2010-05-02  2:59   ` Anthony Tavener
  2010-05-07  9:42     ` AUGER
  0 siblings, 1 reply; 5+ messages in thread
From: Anthony Tavener @ 2010-05-02  2:59 UTC (permalink / raw)
  To: Stéphane Lescuyer; +Cc: caml-list

[-- Attachment #1: Type: text/plain, Size: 936 bytes --]

Wow! Thanks Stéphane... that was a little piece of magic I was
hoping for. It's a bit verbose, but at least it doesn't affect
performance and it allows all the control over types I need.

I now see I didn't really grok phantom types whenever they were
mentioned. A bit of "in one ear and out the other". Now I have
better grasp on yet another feature of the language.


I know I read that one before, Dario... that title about NASA
is familiar! Too bad I didn't remember it was a solution to the
problem I was now having. Thank-you for that link -- an
excellent description of the problem and a nice solution.


Ah, great stuff... that looks like something to consider too,
Sylvain! There's plenty of material on phantom types but I never
made the connection that they were what I was looking for.


Thank-you everyone for the help and clear explanations! I'm
going to go play with this now. :)

Anthony Tavener

[-- Attachment #2: Type: text/html, Size: 1030 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Caml-list] Subtyping structurally-equivalent records, or something like it?
  2010-05-02  2:59   ` Anthony Tavener
@ 2010-05-07  9:42     ` AUGER
  0 siblings, 0 replies; 5+ messages in thread
From: AUGER @ 2010-05-07  9:42 UTC (permalink / raw)
  To: Anthony Tavener; +Cc: caml-list

Le Sun, 02 May 2010 04:59:48 +0200, Anthony Tavener  
<anthony.tavener@gmail.com> a écrit:

> Wow! Thanks Stéphane... that was a little piece of magic I was
> hoping for. It's a bit verbose, but at least it doesn't affect
> performance and it allows all the control over types I need.
>
> I now see I didn't really grok phantom types whenever they were
> mentioned. A bit of "in one ear and out the other". Now I have
> better grasp on yet another feature of the language.
>
>
> I know I read that one before, Dario... that title about NASA
> is familiar! Too bad I didn't remember it was a solution to the
> problem I was now having. Thank-you for that link -- an
> excellent description of the problem and a nice solution.
>
>
> Ah, great stuff... that looks like something to consider too,
> Sylvain! There's plenty of material on phantom types but I never
> made the connection that they were what I was looking for.
>
>
> Thank-you everyone for the help and clear explanations! I'm
> going to go play with this now. :)
>
> Anthony Tavener


Just one last thing about this thread:
It is not really related to type checking,
but more on a way to convince the programmer that it uses rightfully a  
force and not a position;
it is the use of labels. Imagine you have this function:

(** [next_position p v dt] is the next position of an object on position  
[p] with
     a speed [v] in a time [dt] *)
val next_position: kinematic -> kinematic -> float -> kinematic

as far as you have the documentation next to you, you won't make mistake  
between the two `kinematic';
but if you don't have it, then you may not recall if you have to write  
`next_position v p dt' or `next_position p v dt'

but now, imagine you have:

(** [next_position p v dt] is the next position of an object on position  
[p] with
     a speed [v] in a time [dt] *)
val next_position: pos:kinematic -> speed:kinematic -> laps:float ->  
kinematic

then you just have to use the right labels to make it work:
`next_position ~speed:v ~pos:p ~laps:dt'.

As I said, this is not a type checking solution, but it can make the  
program clearer and allows the user to forget parameters order (but he  
needs to memorize label names)

Just beware with higher order:

# let g ~x ~y = x - y;;
val g : x:int -> y:int -> int = <fun>
# let app (f: y:int -> x:int -> int) a b = f ~x:a ~y:b;;
val app : (y:int -> x:int -> int) -> int -> int -> int = <fun>
# app g;;
Error: This expression has type x:int -> y:int -> int
        but an expression was expected of type y:int -> x:int -> int

-- 
Cédric AUGER

Univ Paris-Sud, Laboratoire LRI, UMR 8623, F-91405, Orsay


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Caml-list] Subtyping structurally-equivalent records, or something like it?
@ 2010-05-01 19:55 Dario Teixeira
  0 siblings, 0 replies; 5+ messages in thread
From: Dario Teixeira @ 2010-05-01 19:55 UTC (permalink / raw)
  To: caml-list, Anthony Tavener

Hi,

>  type kinematic = { lin: Vec.t; ang: Vec.t }
>
> Which I've been using to represent a medley of physical attributes (force, > momentum, velocity, etc.).

I second Stéphane's suggestion of using phantom types; moreover,
I recommend you read an article that discusses them to some detail
and covers their use for precisely this sort of problem:
http://camltastic.blogspot.com/2008/05/phantom-types.html

Cheers,
Dario Teixeira






^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2010-05-07  9:42 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-05-01 16:04 Subtyping structurally-equivalent records, or something like it? Anthony Tavener
2010-05-01 16:51 ` [Caml-list] " Stéphane Lescuyer
2010-05-02  2:59   ` Anthony Tavener
2010-05-07  9:42     ` AUGER
2010-05-01 19:55 Dario Teixeira

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).