From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@yquem.inria.fr Delivered-To: caml-list@yquem.inria.fr Received: from mail3-relais-sop.national.inria.fr (mail3-relais-sop.national.inria.fr [192.134.164.104]) by yquem.inria.fr (Postfix) with ESMTP id C40DBBC57 for ; Sat, 1 May 2010 18:04:22 +0200 (CEST) X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AgECAM/t20tKfVK0mGdsb2JhbACSZolsVggVAQEBAQEICQwHESKqU4F/hRguiE4BAQMFhQ0Egzw X-IronPort-AV: E=Sophos;i="4.52,310,1270418400"; d="scan'208";a="49693816" Received: from mail-wy0-f180.google.com ([74.125.82.180]) by mail3-smtp-sop.national.inria.fr with ESMTP; 01 May 2010 18:04:22 +0200 Received: by wyb29 with SMTP id 29so762041wyb.39 for ; Sat, 01 May 2010 09:04:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:received:date:message-id :subject:from:to:content-type; bh=v50ohFKYKaF0J1Mndj3in9Sugedp/kunTb6gGluDZQA=; b=js9h8KAe5r/n76VhzUs/Mck8Wi/R+3ReF6yT59sh5cXYWuK+yLeXFfpEBS4KUFthJm l2dt9tdACBcMqi8LJuMlq+bG4iu52iLJ5ZmeeWw7tgiX9gyKMbfa4tOdEj87QhEgWKmp FMfYJvZgDHGJi9d9D3VcCMU4F5N1TlBYIbib0= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:date:message-id:subject:from:to:content-type; b=rK1Zisp3wwNIf9P+ZcnWxgRjQtMFVegWDeZaXdUZbXaDeyDoOg9W2Y5ng33ugzI0Y4 L+GtYYxUKYRJyCfeQKHAaeY7NEccRNl2FWTbLCu7G02kOpqTXlKFc3aSorEBde18yk39 ciF8FxoNgCD8kNPI2dUQyBAVPEHvFboMTOAjk= MIME-Version: 1.0 Received: by 10.216.90.70 with SMTP id d48mr4735223wef.199.1272729861685; Sat, 01 May 2010 09:04:21 -0700 (PDT) Received: by 10.216.90.66 with HTTP; Sat, 1 May 2010 09:04:21 -0700 (PDT) Date: Sat, 1 May 2010 23:04:21 +0700 Message-ID: Subject: Subtyping structurally-equivalent records, or something like it? From: Anthony Tavener To: caml-list@yquem.inria.fr Content-Type: multipart/alternative; boundary=0016e6d566404d003f04858a8393 X-Spam: no; 0.00; subtyping:01 acceleration:01 structurally:01 ocaml:01 subtypes:01 subtypes:01 structurally:01 subtyping:01 functor:01 accessor:01 uniformly:01 sig:01 val:01 val:01 runtime:01 --0016e6d566404d003f04858a8393 Content-Type: text/plain; charset=ISO-8859-1 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 --0016e6d566404d003f04858a8393 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable I have this:

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

As the physics code becomes increas= ingly substantial I'm running into possible human-error, like passing a= momentum where a force is expected, or a mass instead of inverse-mass (mas= s 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:

=A0 type position =3D { r: Vec.t; theta: Vec.t }
=A0 type accelerati= on =3D { a: Vec.t; alpha: Vec.t }
=A0 type force =3D { f: Vec.t; tau: Ve= c.t }

They are structurally equivalent, and ideally I'd like to = be able to treat these as 'kinematic' too, since that is how I woul= d 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 fin= d 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 effectivel= y 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 f= or one of these types would technically work on any... but is there a way t= o 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, b= ut 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 w= ith 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' bas= e type, so all common operations are duplicated for each new type. No perfo= rmance 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 ap= ply 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 type= s (again, even though they are structurally equivalent) -- these calls prob= ably wouldn't get optimized out. But maybe there is a better way to do = this?

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

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

4. Adapter functions: 'kinematic_of_force: force -> kinematic= 9;, etc. as a way to use the common set of 'kinematic' functions. T= his is clunky and comes with a big performance hit unless these functions b= ecame like type-coercions. If there is a way this can be done with zero run= time 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 makin= g the language I favor for most tasks!

Briefly introducing myself: I've been a professional video-game dev= eloper for 15 years, most recently specializing in AI. I quit my last job a= lmost two years ago to travel and program (95% in OCaml!), and am developin= g a game now. I've seen indications over the years of other game develo= pers 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 som= eone 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
--0016e6d566404d003f04858a8393--