* Phantom types
@ 2010-05-17 14:59 Thomas Braibant
2010-05-17 15:14 ` [Caml-list] Phantom types [NC] Rabih CHAAR
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Thomas Braibant @ 2010-05-17 14:59 UTC (permalink / raw)
To: caml-list
Hi list,
Following on the thread "Subtyping structurally-equivalent records, or
something like it?", I made some experimentations with phantom types.
Unfortunately, I turns out that I do not understand the results I got.
Could someone on the list explain why the first piece of code is well
typed, while the second one is not ?
type p1
type p2
type 'a t = float
let x : p1 t = 0.0
let id : p2 t -> p2 t = fun x -> x
let _ = id x (* type checks with type p2 t*)
type 'a t = {l: float}
let x : p1 t = {l = 0.0}
let id : p2 t -> p2 t = fun x -> x
let _ = id x (* ill typed *)
Any thoughts ?
thomas
2010/5/1 Stéphane Lescuyer <stephane.lescuyer@inria.fr>:
> 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
>
> _______________________________________________
> 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
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Phantom types [NC]
2010-05-17 14:59 Phantom types Thomas Braibant
@ 2010-05-17 15:14 ` Rabih CHAAR
2010-05-17 15:19 ` Philippe Veber
2010-05-17 16:00 ` Phantom types Dawid Toton
2010-05-17 16:37 ` [Caml-list] " Goswin von Brederlow
2 siblings, 1 reply; 10+ messages in thread
From: Rabih CHAAR @ 2010-05-17 15:14 UTC (permalink / raw)
To: Thomas Braibant; +Cc: caml-list, caml-list-bounces
[-- Attachment #1: Type: text/plain, Size: 9486 bytes --]
if you define the intermediate type
type s= {l:float}
followed by
type 'a t = s
everything goes well
I am unable to give you an explanation about this (the need of the
intermediay type s). I hope someone can shed some light on this.
Sincerely
Thomas Braibant <thomas.braibant@gmail.com>
Sent by: caml-list-bounces@yquem.inria.fr
17/05/10 04:59 PM
To
caml-list@yquem.inria.fr
cc
Subject
[Caml-list] Phantom types
Hi list,
Following on the thread "Subtyping structurally-equivalent records, or
something like it?", I made some experimentations with phantom types.
Unfortunately, I turns out that I do not understand the results I got.
Could someone on the list explain why the first piece of code is well
typed, while the second one is not ?
type p1
type p2
type 'a t = float
let x : p1 t = 0.0
let id : p2 t -> p2 t = fun x -> x
let _ = id x (* type checks with type p2 t*)
type 'a t = {l: float}
let x : p1 t = {l = 0.0}
let id : p2 t -> p2 t = fun x -> x
let _ = id x (* ill typed *)
Any thoughts ?
thomas
2010/5/1 Stéphane Lescuyer <stephane.lescuyer@inria.fr>:
> 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
>
> _______________________________________________
> 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
>
_______________________________________________
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
*************************************************************************
This message and any attachments (the "message") are confidential, intended solely for the addressee(s), and may contain legally privileged information.
Any unauthorised use or dissemination is prohibited. E-mails are susceptible to alteration.
Neither SOCIETE GENERALE nor any of its subsidiaries or affiliates shall be liable for the message if altered, changed or
falsified.
************
Ce message et toutes les pieces jointes (ci-apres le "message") sont confidentiels et susceptibles de contenir des informations couvertes
par le secret professionnel.
Ce message est etabli a l'intention exclusive de ses destinataires. Toute utilisation ou diffusion non autorisee est interdite.
Tout message electronique est susceptible d'alteration.
La SOCIETE GENERALE et ses filiales declinent toute responsabilite au titre de ce message s'il a ete altere, deforme ou falsifie.
*************************************************************************
[-- Attachment #2: Type: text/html, Size: 12177 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Phantom types [NC]
2010-05-17 15:14 ` [Caml-list] Phantom types [NC] Rabih CHAAR
@ 2010-05-17 15:19 ` Philippe Veber
0 siblings, 0 replies; 10+ messages in thread
From: Philippe Veber @ 2010-05-17 15:19 UTC (permalink / raw)
To: Rabih CHAAR; +Cc: Thomas Braibant, caml-list, caml-list-bounces
[-- Attachment #1: Type: text/plain, Size: 10518 bytes --]
It seems that the expressions typecheck when t is a type abbreviation and
not a type definition. I don't know about the actual typing rules but this
would be reasonable, I guess.
Philippe.
2010/5/17 Rabih CHAAR <rabih.chaar@lyxor.com>
>
> if you define the intermediate type
> type s= {l:float}
> followed by
> type 'a t = s
> everything goes well
>
> I am unable to give you an explanation about this (the need of the
> intermediay type s). I hope someone can shed some light on this.
>
> Sincerely
>
>
>
> *Thomas Braibant <thomas.braibant@gmail.com>*
> Sent by: caml-list-bounces@yquem.inria.fr
>
> 17/05/10 04:59 PM
> To
> caml-list@yquem.inria.fr
> cc
> Subject
> [Caml-list] Phantom types
>
>
>
>
> Hi list,
>
> Following on the thread "Subtyping structurally-equivalent records, or
> something like it?", I made some experimentations with phantom types.
> Unfortunately, I turns out that I do not understand the results I got.
>
> Could someone on the list explain why the first piece of code is well
> typed, while the second one is not ?
>
>
> type p1
> type p2
>
> type 'a t = float
> let x : p1 t = 0.0
> let id : p2 t -> p2 t = fun x -> x
> let _ = id x (* type checks with type p2 t*)
>
> type 'a t = {l: float}
> let x : p1 t = {l = 0.0}
> let id : p2 t -> p2 t = fun x -> x
> let _ = id x (* ill typed *)
>
> Any thoughts ?
>
> thomas
>
>
>
> 2010/5/1 Stéphane Lescuyer <stephane.lescuyer@inria.fr>:
> > 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
> >
> > _______________________________________________
> > 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
> >
>
> _______________________________________________
> 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
>
> *************************************************************************
> This message and any attachments (the "message") are confidential, intended solely for the addressee(s), and may contain legally privileged information.
> Any unauthorised use or dissemination is prohibited. E-mails are susceptible to alteration.
> Neither SOCIETE GENERALE nor any of its subsidiaries or affiliates shall be liable for the message if altered, changed or
> falsified.
> ************
> Ce message et toutes les pieces jointes (ci-apres le "message") sont confidentiels et susceptibles de contenir des informations couvertes
> par le secret professionnel.
> Ce message est etabli a l'intention exclusive de ses destinataires. Toute utilisation ou diffusion non autorisee est interdite.
> Tout message electronique est susceptible d'alteration.
> La SOCIETE GENERALE et ses filiales declinent toute responsabilite au titre de ce message s'il a ete altere, deforme ou falsifie.
> *************************************************************************
>
>
> _______________________________________________
> 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
>
>
[-- Attachment #2: Type: text/html, Size: 14815 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Phantom types
2010-05-17 14:59 Phantom types Thomas Braibant
2010-05-17 15:14 ` [Caml-list] Phantom types [NC] Rabih CHAAR
@ 2010-05-17 16:00 ` Dawid Toton
2010-05-17 16:37 ` [Caml-list] " Goswin von Brederlow
2 siblings, 0 replies; 10+ messages in thread
From: Dawid Toton @ 2010-05-17 16:00 UTC (permalink / raw)
To: caml-list
> type 'a t = {l: float}
>
> Any thoughts ?
I think the crucial question is when new record types are born. Here is
my opinion:
The "=" sign in the above type mapping definition is what I would call
"delayed binding". "Early binding" would be equivalent to
type tmp = {lab : float}
type 'a s = tmp
(evaluate the right-hand side first, then define the mapping).
The "early binding" creates only one record type, so lab becomes
ordinary record label.
In the given example of the "delayed binding" the t becomes a machine
producing new record types.
Hence, the identifier l is not an ordinary record label. It is shared by
whole family of record types. We can see it this way:
# type 'a t = { la : float } ;;
type 'a t = { la : float; }
# {la = 0.};;
- : 'a t = {la = 0.}
So OCaml interpreter doesn't know the exact type of the last expression,
but it is clever enough to give it a generalized type.
We can use la to construct records of incompatible types:
# type 'a t = { la : float } ;;
type 'a t = { la : float; }
# let yy = ({la = 0.} : int t) ;;
val yy : int t = {la = 0.}
# let xx = ({la = 0.} : string t);;
val xx : string t = {la = 0.}
# xx = yy;;
Error: This expression has type int t but an expression was expected of type
string t
I suppose my jargon may be not mainstream, apologies.
Dawid
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Phantom types
2010-05-17 14:59 Phantom types Thomas Braibant
2010-05-17 15:14 ` [Caml-list] Phantom types [NC] Rabih CHAAR
2010-05-17 16:00 ` Phantom types Dawid Toton
@ 2010-05-17 16:37 ` Goswin von Brederlow
2 siblings, 0 replies; 10+ messages in thread
From: Goswin von Brederlow @ 2010-05-17 16:37 UTC (permalink / raw)
To: Thomas Braibant; +Cc: caml-list
Thomas Braibant <thomas.braibant@gmail.com> writes:
> Hi list,
>
> Following on the thread "Subtyping structurally-equivalent records, or
> something like it?", I made some experimentations with phantom types.
> Unfortunately, I turns out that I do not understand the results I got.
>
> Could someone on the list explain why the first piece of code is well
> typed, while the second one is not ?
>
>
> type p1
> type p2
>
> type 'a t = float
> let x : p1 t = 0.0
> let id : p2 t -> p2 t = fun x -> x
> let _ = id x (* type checks with type p2 t*)
This is actualy a problem, at least for me. Because that is a type error
you usualy want to specifically catch through the use of phantom types.
But ocaml knows that 'a t = float and all floats are compatible. So it
accepts all 'a t as the same.
To make the phantom type checking work for you you need to move the
definition of your phantom type into a submodule and make the type
abstract through its signature. Any functions converting from one 'a t
to 'b t also need to be in there. To avoid having to use the submodule
name all the time you can use something like
module M : sig type 'a t = private float val make : float -> 'a t end = struct
type 'a t = float
let make f = f
end
include M
# let x : p1 t = make 0.0;;
val x : p1 t = 0.
# let id : p2 t -> p2 t = fun x -> x;;
val id : p2 t -> p2 t = <fun>
# let _ = id x;;
Error: This expression has type p1 t = p1 M.t
but an expression was expected of type p2 t = p2 M.t
The "private" tells the type system that nobody (outside the module) is
to create a value of that type. Only inside the module, where the type
isn't private can you create one.
> type 'a t = {l: float}
> let x : p1 t = {l = 0.0}
> let id : p2 t -> p2 t = fun x -> x
> let _ = id x (* ill typed *)
Why it works correctly here is explained nicely in the other mailss in
this thread.
> Any thoughts ?
>
> thomas
MfG
Goswin
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Phantom types
2004-08-23 15:16 ` Jon Harrop
@ 2004-08-27 9:03 ` Jacques GARRIGUE
0 siblings, 0 replies; 10+ messages in thread
From: Jacques GARRIGUE @ 2004-08-27 9:03 UTC (permalink / raw)
To: jon; +Cc: caml-list
From: Jon Harrop <jon@jdh30.plus.com>
> I should add, using:
>
> val add_even_even : [> `Even ] t -> [> `Even ] t -> [> `Even ] t
> val add_even_odd : [> `Even ] t -> [> `Odd ] t -> [> `Odd ] t
>
> allows:
>
> # PhantomInt.add_even_odd i j;;
> val k : _[> `Odd ] PhantomInt.t = <abstr>
This part is OK.
> which can then be misused:
>
> # PhantomInt.add_even_even i k;;
> - : [ `Even ] PhantomInt.t = <abstr>
But this one is wrong.
> I think this is because [> `Even] means any superset of [ `Even ] whereas [<
> `Even], which was probably intended, means any subset of [ `Even ]. Indeed,
> the latter appears to work.
Indeed, one must be careful about variance.
So the right signature would be:
type +'a t
type any = [`Even|`Odd]
val add_even_even : [ `Even ] t -> [ `Even ] t -> [> `Even ] t
val add_even_odd : [ `Even ] t -> [ `Odd ] t -> [> `Odd ] t
val add_unknown_unknown : any t -> any t -> any t
(Note the + indicating covariance)
Then you have
# let k = PhantomInt.add_even_odd i j;;
val k : [> `Odd ] PhantomInt.t = <abstr>
(polymorphic result!)
# PhantomInt.add_even_even i k;;
** type error
# PhantomInt.add_unknown_unknown i k;;
- : any t = <abstr>
Jacques Garrigue
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Phantom types
2004-08-23 9:49 ` Jon Harrop
2004-08-23 15:16 ` Jon Harrop
@ 2004-08-25 21:03 ` brogoff
1 sibling, 0 replies; 10+ messages in thread
From: brogoff @ 2004-08-25 21:03 UTC (permalink / raw)
To: Jon Harrop; +Cc: caml-list
On Mon, 23 Aug 2004, Jon Harrop wrote:
> On Saturday 31 July 2004 17:31, Markus Mottl wrote:
> > module type PHANTOM_INT = sig
> > type 'p t
> > ...
>
> Right, I've had a bit more of a chance to play with phantom types now, and I'm
> quite confused. :-)
>
> As far as I can tell, there were a few errors in Markus' original (I may well
> be wrong, of course), so here is my altered version:
[...snip...]
Yes, those look like errors, and your fixes look good.
> So I've changed the types to be [ `Even ] instead of [> `Even ] and the "make"
> functions to be "int -> ...". This appear to work as desired:
that's what you want in this example.
> Now, there are some subtle peculiarities of these which I don't understand.
> Firstly, the type checking of the phantom types only seems to work if the
> type is made abstract in the module signature. I can't think why this should
> make a difference though. For example, changing "type 'p t" to "type 'p t =
> int" in "PhantomInt : sig" then allows:
>
> # PhantomInt.add_even_even i j;;
> - : [ `Even ] PhantomInt.t = 5
>
> which is clearly undesirable.
This looks like a bug in the OCaml type checker. It also occurs if you replace
the polymorphic variants with type even = Even and odd = Odd. It remains if
you replace the phantom type int by int64 or even int array, but goes away when
you replace the phantom type by type 'p t = { data = int } or
type 'p t = Int int. So the built in types appear to be being treated
differently.
> Secondly, specifying the types as Markus did (e.g. [> `Even]), which I think
> should have been correct, leads to some kind of monomorphic type:
>
> # PhantomInt.add_even_odd i j;;
> - : _[> `Odd ] PhantomInt.t = <abstr>
A [> or [< means there's an implicit type variable.
-- Brian
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Phantom types
2004-08-23 9:49 ` Jon Harrop
@ 2004-08-23 15:16 ` Jon Harrop
2004-08-27 9:03 ` Jacques GARRIGUE
2004-08-25 21:03 ` brogoff
1 sibling, 1 reply; 10+ messages in thread
From: Jon Harrop @ 2004-08-23 15:16 UTC (permalink / raw)
To: caml-list
On Monday 23 August 2004 10:49, Jon Harrop wrote:
> Secondly, specifying the types as Markus did (e.g. [> `Even])...
I should add, using:
val add_even_even : [> `Even ] t -> [> `Even ] t -> [> `Even ] t
val add_even_odd : [> `Even ] t -> [> `Odd ] t -> [> `Odd ] t
allows:
# PhantomInt.add_even_odd i j;;
val k : _[> `Odd ] PhantomInt.t = <abstr>
which can then be misused:
# PhantomInt.add_even_even i k;;
- : [ `Even ] PhantomInt.t = <abstr>
I think this is because [> `Even] means any superset of [ `Even ] whereas [<
`Even], which was probably intended, means any subset of [ `Even ]. Indeed,
the latter appears to work.
Cheers,
Jon.
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Phantom types
2004-07-31 16:31 ` [Caml-list] Phantom types Markus Mottl
@ 2004-08-23 9:49 ` Jon Harrop
2004-08-23 15:16 ` Jon Harrop
2004-08-25 21:03 ` brogoff
0 siblings, 2 replies; 10+ messages in thread
From: Jon Harrop @ 2004-08-23 9:49 UTC (permalink / raw)
To: caml-list
On Saturday 31 July 2004 17:31, Markus Mottl wrote:
> module type PHANTOM_INT = sig
> type 'p t
> ...
Right, I've had a bit more of a chance to play with phantom types now, and I'm
quite confused. :-)
As far as I can tell, there were a few errors in Markus' original (I may well
be wrong, of course), so here is my altered version:
module PhantomInt : sig
type 'p t
val add : 'p t -> 'p t -> 'p t
val add_even_even : [ `Even ] t -> [ `Even ] t -> [ `Even ] t
val add_even_odd : [ `Even ] t -> [ `Odd ] t -> [ `Odd ] t
val add_odd_even : [ `Odd ] t -> [ `Even ] t -> [ `Odd ] t
val add_odd_odd : [ `Odd ] t -> [ `Odd ] t -> [ `Even ] t
val neg : 'a t -> 'a t
val make_even : int -> [ `Even ] t
val make_odd : int -> [ `Odd ] t
end = struct
type 'p t = int
let add = (+)
let add_even_even = add
let add_even_odd = add
let add_odd_even = add
let add_odd_odd = add
let neg n = -n
let make_even n = if n mod 2 = 0 then n else failwith "not even"
let make_odd n = if n mod 2 <> 0 then n else failwith "not odd"
end;;
So I've changed the types to be [ `Even ] instead of [> `Even ] and the "make"
functions to be "int -> ...". This appear to work as desired:
# let i = PhantomInt.make_even 2;;
val i : [ `Even ] PhantomInt.t = <abstr>
# let j = PhantomInt.make_odd 3;;
val j : [ `Odd ] PhantomInt.t = <abstr>
# PhantomInt.add_even_odd i j;;
- : [ `Odd ] PhantomInt.t = <abstr>
# PhantomInt.add_even_even i j;;
This expression has type [ `Odd ] PhantomInt.t but is here used with type
[ `Even ] PhantomInt.t
These two variant types have no intersection
Now, there are some subtle peculiarities of these which I don't understand.
Firstly, the type checking of the phantom types only seems to work if the
type is made abstract in the module signature. I can't think why this should
make a difference though. For example, changing "type 'p t" to "type 'p t =
int" in "PhantomInt : sig" then allows:
# PhantomInt.add_even_even i j;;
- : [ `Even ] PhantomInt.t = 5
which is clearly undesirable.
Secondly, specifying the types as Markus did (e.g. [> `Even]), which I think
should have been correct, leads to some kind of monomorphic type:
# PhantomInt.add_even_odd i j;;
- : _[> `Odd ] PhantomInt.t = <abstr>
Note the "_" preceding the "[> `Odd]". I'm not sure what the implications of
this are.
Can someone explain these to me, please?
Cheers,
Jon.
PS: I'm using 3.08.0, in case that makes a difference.
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Caml-list] Phantom types
2004-07-31 13:44 ` Jon Harrop
@ 2004-07-31 16:31 ` Markus Mottl
2004-08-23 9:49 ` Jon Harrop
0 siblings, 1 reply; 10+ messages in thread
From: Markus Mottl @ 2004-07-31 16:31 UTC (permalink / raw)
To: Jon Harrop; +Cc: caml-list
On Sat, 31 Jul 2004, Jon Harrop wrote:
> On Saturday 31 July 2004 11:34, Markus Mottl wrote:
> > ...
> > val incr : (int, [> `W ]) t -> unit
> > val decr : (int, [> `W ]) t -> unit
>
> Should these both be [`R|`W]?
Well, in this case it doesn't really matter. The upper version would
also accept only writable references in addition to readable ones.
But they must be at least writable. Even other attributes would be
accepted as long as `W is one of them.
> > The phantom variable is 'rw. When creating references, it can be any
> > of `R (for reading) and `T (for writing).
>
> Do you mean `W for writing?
Sorry, yeah.
> That's very interesting. So a phantom type is a type which you stick in
> to dupe the type system into doing something for you? Is there a good
> reference on those? I seem to remember a thread about their utility
> in porting the STL to ocaml but that was before I had ever used OCaml...
Indeed, phantom types are extremely neat. Sometimes I think that every
type, including basic ones, should actually have a polymorphic parameter,
which can later be used for constraining it.
E.g.:
---------------------------------------------------------------------------
module type PHANTOM_INT = sig
type 'p t
val add : 'p t -> 'p t -> 'p t
val add_even_even : [> `Even ] t -> [> `Even ] t -> [> `Even ] t
val add_even_odd : [> `Even ] t -> [> `Odd ] t -> [> `Odd ] t
val add_odd_even : [> `Odd ] t -> [> `Even ] t -> [> `Odd ] t
val add_odd_odd : [> `Odd ] t -> [> `Odd ] t -> [> `Even ] t
val make_even : 'a t -> [ `Even ] t
val make_odd : 'a t -> [ `Odd ] t
end
module PhantomInt : PHANTOM_INT = struct
type 'p t = int
let add = (+)
let add_even_even = add
let add_even_odd = add
let add_odd_even = add
let add_odd_odd = add
let make_even n = if n mod 2 = 0 then n else failwith "not even"
let make_odd n = if n mod 2 <> 0 then n else failwith "not odd"
end
module type PHANTOM_LIST = sig
type ('a, 'p) t
val rev : ('a, 'p) t -> ('a, 'p) t
val rev_sorted_up : ('a, [> `SortedUp ]) t -> ('a, [> `SortedDown ]) t
val rev_sorted_down : ('a, [> `SortedDown ]) t -> ('a, [> `SortedUp ]) t
end
module PhantomList : PHANTOM_LIST = struct
type ('a, 'p) t = 'a list
let rev = List.rev
let rev_sorted_up = rev
let rev_sorted_down = rev
end
---------------------------------------------------------------------------
You get the idea. Phantom types not only allow you to capture
constraints, which are proved by the compiler, they are also perfectly
cheap computationally, because you don't have to check things at runtime
all the time.
E.g. after having successfully applied "make_even" to an integer, the
compiler guarantees that it won't be misused, i.e. other functions only
need to require this constraint in their type signature, and need not
check it at runtime anymore.
I wonder in how far generics could make these type checking tricks even
more powerful!
> And this const-alternative is useful when dealing with large records which
> have mostly constant but some mutable entries because handling such records
> invokes a lot of copying? But, say, arrays are passed by reference so this
> wouldn't provide much of a performance advantage, is that right?
> Incidentally, does anyone have a functional array implementation (which
> doesn't suck ;-)?
If you mean O(1) with constants as low as imperative arrays: no ;-)
Regards,
Markus
--
Markus Mottl http://www.oefai.at/~markus markus@oefai.at
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2010-05-17 16:37 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-05-17 14:59 Phantom types Thomas Braibant
2010-05-17 15:14 ` [Caml-list] Phantom types [NC] Rabih CHAAR
2010-05-17 15:19 ` Philippe Veber
2010-05-17 16:00 ` Phantom types Dawid Toton
2010-05-17 16:37 ` [Caml-list] " Goswin von Brederlow
-- strict thread matches above, loose matches on Subject: below --
2004-07-31 8:56 [Caml-list] const equivalent for mutable types? Christopher A. Gorski
2004-07-31 10:34 ` Markus Mottl
2004-07-31 13:44 ` Jon Harrop
2004-07-31 16:31 ` [Caml-list] Phantom types Markus Mottl
2004-08-23 9:49 ` Jon Harrop
2004-08-23 15:16 ` Jon Harrop
2004-08-27 9:03 ` Jacques GARRIGUE
2004-08-25 21:03 ` brogoff
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox