caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Create a constraint between variant type and data list
@ 2010-09-03 17:16 Sylvain Le Gall
  2010-09-03 17:38 ` [Caml-list] " bluestorm
                   ` (5 more replies)
  0 siblings, 6 replies; 12+ messages in thread
From: Sylvain Le Gall @ 2010-09-03 17:16 UTC (permalink / raw)
  To: caml-list

Hello all,

I would like to somehow enforce that a variant type is associated with
an entry in a data list. 

For example, 

I would like to define:

type license = GPL | LGPL 

and 

let data = [ GPL, "GNU Public license"; 
             LGPL, "GNU Lesser General Public license" ]


I would like to enforce that all variants of license are in the
association list.

I have tried to use polymorphic variants, but don't see how to enforce
this constraint.

The point, is that if I add a new variant to license (e.g. BSD3), the
compiler output an error because this new variant is not in data list.

Any ideas ? If you need to use another type expression rather than
variant, please do so, as long as I am able to link the license type
and data list.

Thanks all,
Sylvain Le Gall


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

* Re: [Caml-list] Create a constraint between variant type and data list
  2010-09-03 17:16 Create a constraint between variant type and data list Sylvain Le Gall
@ 2010-09-03 17:38 ` bluestorm
  2010-09-03 21:28   ` Sylvain Le Gall
  2010-09-03 18:51 ` [Caml-list] " Martin Jambon
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 12+ messages in thread
From: bluestorm @ 2010-09-03 17:38 UTC (permalink / raw)
  To: Sylvain Le Gall; +Cc: caml-list

Hi,

I do not have a direct solution to your problem.

If you want to associate a data with each case, you can use a pattern
matching, wich will do the exhaustiveness check :

let to_string : license -> _ = function
| `GPL -> "GPL"
| `LGPL -> "LGPL"

You will be warned if you add (or remove) some licenses and forget to
change the function.


You may, however, want to have a list of all licenses values, instead
of a case-handling on each value. I don't know how you could have the
compiler check completeness for you, but if you really want that
check, there is an extra-linguistic method : declare your value
without giving it a type annotation, so that the compiler will infer
the covered case, then use a script outside the program, calling
"ocamlc -i" to check the inferred signature, and comparing it to you
datatype declaration.

Finally, I have a third solution based on code generation : given my
first solution (turning the association list into a function), what
you need is only a list of all the constructors (and you can build
your assoc list with List.map (fun x -> x, assoc_function x)). This
can easily be generated from the datatype declaration using direct
camlp4, or Markus Mottl's type-conv (
http://www.ocaml.info/home/ocaml_sources.html#toc11 ).


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

* Re: [Caml-list] Create a constraint between variant type and data list
  2010-09-03 17:16 Create a constraint between variant type and data list Sylvain Le Gall
  2010-09-03 17:38 ` [Caml-list] " bluestorm
@ 2010-09-03 18:51 ` Martin Jambon
  2010-09-03 19:39   ` Ashish Agarwal
  2010-09-03 21:13 ` Maxence Guesdon
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 12+ messages in thread
From: Martin Jambon @ 2010-09-03 18:51 UTC (permalink / raw)
  To: Sylvain Le Gall; +Cc: caml-list

Sylvain Le Gall wrote:
> Hello all,
> 
> I would like to somehow enforce that a variant type is associated with
> an entry in a data list. 
> 
> For example, 
> 
> I would like to define:
> 
> type license = GPL | LGPL 
> 
> and 
> 
> let data = [ GPL, "GNU Public license"; 
>              LGPL, "GNU Lesser General Public license" ]
> 
> 
> I would like to enforce that all variants of license are in the
> association list.
> 
> I have tried to use polymorphic variants, but don't see how to enforce
> this constraint.
> 
> The point, is that if I add a new variant to license (e.g. BSD3), the
> compiler output an error because this new variant is not in data list.
> 
> Any ideas ? If you need to use another type expression rather than
> variant, please do so, as long as I am able to link the license type
> and data list.

I don't see a solution other than meta-programming or runtime checks.

Here is a simple code generator that would do the job:

(* license_gen.ml *)
open Printf

let print_licenses l =
  printf "type license =";
  List.iter (fun (k, v) -> printf " | %s" k) l;
  printf "\n";
  printf "let licences = [\n";
  List.iter (fun (k, v) -> printf "  %s, %S;\n" k v) l;
  printf "]\n"

let () =
  print_licenses [
    "GPL", "GNU Public license";
    "LGPL", "GNU Lesser General Public license";
  ]

(* end *)

$ ocaml license_gen.ml > license.ml



Martin

-- 
http://mjambon.com/


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

* Re: [Caml-list] Create a constraint between variant type and data list
  2010-09-03 18:51 ` [Caml-list] " Martin Jambon
@ 2010-09-03 19:39   ` Ashish Agarwal
  0 siblings, 0 replies; 12+ messages in thread
From: Ashish Agarwal @ 2010-09-03 19:39 UTC (permalink / raw)
  To: Sylvain Le Gall, caml-list

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

See the Enum section of deriving:
http://code.google.com/p/deriving/wiki/Introduction

I haven't used it myself so cannot comment on how well it works.


On Fri, Sep 3, 2010 at 2:51 PM, Martin Jambon <martin.jambon@ens-lyon.org>wrote:

> Sylvain Le Gall wrote:
> > Hello all,
> >
> > I would like to somehow enforce that a variant type is associated with
> > an entry in a data list.
> >
> > For example,
> >
> > I would like to define:
> >
> > type license = GPL | LGPL
> >
> > and
> >
> > let data = [ GPL, "GNU Public license";
> >              LGPL, "GNU Lesser General Public license" ]
> >
> >
> > I would like to enforce that all variants of license are in the
> > association list.
> >
> > I have tried to use polymorphic variants, but don't see how to enforce
> > this constraint.
> >
> > The point, is that if I add a new variant to license (e.g. BSD3), the
> > compiler output an error because this new variant is not in data list.
> >
> > Any ideas ? If you need to use another type expression rather than
> > variant, please do so, as long as I am able to link the license type
> > and data list.
>
> I don't see a solution other than meta-programming or runtime checks.
>
> Here is a simple code generator that would do the job:
>
> (* license_gen.ml *)
> open Printf
>
> let print_licenses l =
>  printf "type license =";
>  List.iter (fun (k, v) -> printf " | %s" k) l;
>  printf "\n";
>  printf "let licences = [\n";
>  List.iter (fun (k, v) -> printf "  %s, %S;\n" k v) l;
>  printf "]\n"
>
> let () =
>  print_licenses [
>    "GPL", "GNU Public license";
>    "LGPL", "GNU Lesser General Public license";
>  ]
>
> (* end *)
>
> $ ocaml license_gen.ml > license.ml
>
>
>
> Martin
>
> --
> http://mjambon.com/
>
> _______________________________________________
> 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: 3488 bytes --]

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

* Re: [Caml-list] Create a constraint between variant type and data list
  2010-09-03 17:16 Create a constraint between variant type and data list Sylvain Le Gall
  2010-09-03 17:38 ` [Caml-list] " bluestorm
  2010-09-03 18:51 ` [Caml-list] " Martin Jambon
@ 2010-09-03 21:13 ` Maxence Guesdon
  2010-09-03 21:25   ` Sylvain Le Gall
  2010-09-04  6:35 ` [Caml-list] " Julien Signoles
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 12+ messages in thread
From: Maxence Guesdon @ 2010-09-03 21:13 UTC (permalink / raw)
  To: caml-list

Le Fri, 3 Sep 2010 17:16:48 +0000 (UTC),
Sylvain Le Gall <sylvain@le-gall.net> a écrit :

> Hello all,
> 
> I would like to somehow enforce that a variant type is associated with
> an entry in a data list. 
> 
> For example, 
> 
> I would like to define:
> 
> type license = GPL | LGPL 
> 
> and 
> 
> let data = [ GPL, "GNU Public license"; 
>              LGPL, "GNU Lesser General Public license" ]
> 
> 
> I would like to enforce that all variants of license are in the
> association list.
> 
> I have tried to use polymorphic variants, but don't see how to enforce
> this constraint.
> 
> The point, is that if I add a new variant to license (e.g. BSD3), the
> compiler output an error because this new variant is not in data list.
> 
> Any ideas ? If you need to use another type expression rather than
> variant, please do so, as long as I am able to link the license type
> and data list.

A solution is to add your new license to your list of associations, then
the compiler will complain about the unknown variant :)

Regards,

-- 
Maxence Guesdon



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

* Re: Create a constraint between variant type and data list
  2010-09-03 21:13 ` Maxence Guesdon
@ 2010-09-03 21:25   ` Sylvain Le Gall
  0 siblings, 0 replies; 12+ messages in thread
From: Sylvain Le Gall @ 2010-09-03 21:25 UTC (permalink / raw)
  To: caml-list

On 03-09-2010, Maxence Guesdon <maxence.guesdon@inria.fr> wrote:
> Le Fri, 3 Sep 2010 17:16:48 +0000 (UTC),
> Sylvain Le Gall <sylvain@le-gall.net> a écrit :
>
> A solution is to add your new license to your list of associations, then
> the compiler will complain about the unknown variant :)
>

This is my current solutions. I try to find something better ;-)

Regards,
Sylvain Le Gall


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

* Re: Create a constraint between variant type and data list
  2010-09-03 17:38 ` [Caml-list] " bluestorm
@ 2010-09-03 21:28   ` Sylvain Le Gall
  2010-09-17  7:29     ` [Caml-list] " Maxence Guesdon
  0 siblings, 1 reply; 12+ messages in thread
From: Sylvain Le Gall @ 2010-09-03 21:28 UTC (permalink / raw)
  To: caml-list

On 03-09-2010, bluestorm <bluestorm.dylc@gmail.com> wrote:
> Hi,
>
> Finally, I have a third solution based on code generation : given my
> first solution (turning the association list into a function), what
> you need is only a list of all the constructors (and you can build
> your assoc list with List.map (fun x -> x, assoc_function x)). This
> can easily be generated from the datatype declaration using direct
> camlp4, or Markus Mottl's type-conv (
> http://www.ocaml.info/home/ocaml_sources.html#toc11 ).
>

Your answer and the one from Martin/Ashish, makes me think that I need
to go back to camlp4/type-conv... I would have like to avoid this
solution, but I think it is the best one.

Thanks for all your answer...

Regards,
Sylvain Le Gall

p.s.: this remains an open problem, so solutions are still welcome ;-)


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

* Re: [Caml-list] Create a constraint between variant type and data list
  2010-09-03 17:16 Create a constraint between variant type and data list Sylvain Le Gall
                   ` (2 preceding siblings ...)
  2010-09-03 21:13 ` Maxence Guesdon
@ 2010-09-04  6:35 ` Julien Signoles
  2010-09-04  6:40   ` Julien Signoles
  2010-09-04 16:25 ` Anil Madhavapeddy
  2010-09-17  8:57 ` Sylvain Le Gall
  5 siblings, 1 reply; 12+ messages in thread
From: Julien Signoles @ 2010-09-04  6:35 UTC (permalink / raw)
  To: Sylvain Le Gall; +Cc: caml-list

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

Hello Sylvain,

2010/9/3 Sylvain Le Gall <sylvain@le-gall.net>

> I would like to define:
>
> type license = GPL | LGPL
>
> and
>
> let data = [ GPL, "GNU Public license";
>             LGPL, "GNU Lesser General Public license" ]
>
>
> I would like to enforce that all variants of license are in the
> association list.
>

As said by other guys, you can't enforce statically such an invariant
without a code generator.
In pure ocaml, the below solution use a "social check" : it works only if
the developer who adds a new constructor is not too bad and takes care of
context when solving an error ;-).

type license = GPL | LGPL

let data =
  [ GPL, "GNU Public license";
     LGPL, "GNU Lesser General Public license" ]

let () = match GPL with
  | GPL | LGPL ->
     assert
       (let s = "Do not forget to add new constructors to the data list
above." in
        Format.ifprintf Format.std_formatter "%s" s;
        true)

Hope this helps,
Julien

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

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

* Re: [Caml-list] Create a constraint between variant type and data list
  2010-09-04  6:35 ` [Caml-list] " Julien Signoles
@ 2010-09-04  6:40   ` Julien Signoles
  0 siblings, 0 replies; 12+ messages in thread
From: Julien Signoles @ 2010-09-04  6:40 UTC (permalink / raw)
  To: Sylvain Le Gall; +Cc: caml-list

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

let () = match GPL with

>   | GPL | LGPL ->
>      assert
>        (let s = "Do not forget to add new constructors to the data list
> above." in
>         Format.ifprintf Format.std_formatter "%s" s;
>         true)
>

Sorry could be much better like this:

let () = match GPL with GPL | LGPL ->
  ignore "Do not forget to add new constructors to the data list above."

--
Julien

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

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

* Re: [Caml-list] Create a constraint between variant type and data list
  2010-09-03 17:16 Create a constraint between variant type and data list Sylvain Le Gall
                   ` (3 preceding siblings ...)
  2010-09-04  6:35 ` [Caml-list] " Julien Signoles
@ 2010-09-04 16:25 ` Anil Madhavapeddy
  2010-09-17  8:57 ` Sylvain Le Gall
  5 siblings, 0 replies; 12+ messages in thread
From: Anil Madhavapeddy @ 2010-09-04 16:25 UTC (permalink / raw)
  To: Sylvain Le Gall; +Cc: caml-list

Here's a (very) quick and dirty implementation that will automatically populate data, using our dynamic typing library ( http://github.com/mirage/dyntype ).  You just need to fill in desc_of_license with the matching descriptions, and data will be auto-populated at program startup.

-anil

--
type license = GPL | LGPL with type_of,value

(* For a type, generate a description string *)
let desc_of_license = function
  |"GPL",_ -> "GNU General Public License"
  |"LGPL",_ -> "GNU Lesser GPL"
  |_ -> failwith "unknown license"

(* Generate a list of license types *)
let licenses = match type_of_license with 
  | Type.Ext ("license", Type.Sum ts) -> ts 
  | _ -> assert false

(* From a license type, generate a Value of that type *)
let value_of_license_t = function
  |name,[] -> Value.Ext (("",0L), Value.Sum (name,[]))
  |name,_ -> failwith "no args allowed"

(* Populate data with licenses and their description *)
let data = List.map 
  (fun lic ->
    (license_of_value (value_of_license_t lic)) , 
    (desc_of_license lic)
  ) licenses

let _ =
  Printf.printf "GPL: %s\n%!" (List.assoc GPL data);
  Printf.printf "LGPL: %s\n%!" (List.assoc LGPL data)
--

-anil

On 3 Sep 2010, at 18:16, Sylvain Le Gall wrote:

> Hello all,
> 
> I would like to somehow enforce that a variant type is associated with
> an entry in a data list. 
> 
> For example, 
> 
> I would like to define:
> 
> type license = GPL | LGPL 
> 
> and 
> 
> let data = [ GPL, "GNU Public license"; 
>             LGPL, "GNU Lesser General Public license" ]
> 
> 
> I would like to enforce that all variants of license are in the
> association list.
> 
> I have tried to use polymorphic variants, but don't see how to enforce
> this constraint.
> 
> The point, is that if I add a new variant to license (e.g. BSD3), the
> compiler output an error because this new variant is not in data list.
> 
> Any ideas ? If you need to use another type expression rather than
> variant, please do so, as long as I am able to link the license type
> and data list.
> 
> Thanks all,
> Sylvain Le Gall
> 
> _______________________________________________
> 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] 12+ messages in thread

* Re: [Caml-list] Re: Create a constraint between variant type and data list
  2010-09-03 21:28   ` Sylvain Le Gall
@ 2010-09-17  7:29     ` Maxence Guesdon
  0 siblings, 0 replies; 12+ messages in thread
From: Maxence Guesdon @ 2010-09-17  7:29 UTC (permalink / raw)
  To: caml-list

On Fri, 3 Sep 2010 21:28:24 +0000 (UTC)
Sylvain Le Gall <sylvain@le-gall.net> wrote:

> On 03-09-2010, bluestorm <bluestorm.dylc@gmail.com> wrote:
> > Hi,
> >
> > Finally, I have a third solution based on code generation : given my
> > first solution (turning the association list into a function), what
> > you need is only a list of all the constructors (and you can build
> > your assoc list with List.map (fun x -> x, assoc_function x)). This
> > can easily be generated from the datatype declaration using direct
> > camlp4, or Markus Mottl's type-conv (
> > http://www.ocaml.info/home/ocaml_sources.html#toc11 ).
> >
> 
> Your answer and the one from Martin/Ashish, makes me think that I need
> to go back to camlp4/type-conv... I would have like to avoid this
> solution, but I think it is the best one.

Hello,

Here is another solution, based on oug[1]. The idea is to:
1. make oug analyse your source files and dump the result,
2. write a program loading the dump and performing the checks you want.

Here is an example, which performs 1. and 2., with only one source file "foo.ml".

=== myoug.ml
let (data, _) = Ouglib.Analyze.analyze ["foo.ml"];;

let number_variants =
  Ouglib.Lang.filter_elements data
    (Ouglib.Lang.Name
     {
       Ouglib.Lang.sel_name = "Foo.number.*" ;
       sel_kind = [ Ouglib.Data.Type_field ] ;
     }
    )
;;

let numbers_list_id =
  match
    Ouglib.Lang.filter_elements data
      (Ouglib.Lang.Name
       {
         Ouglib.Lang.sel_name = "Foo.numbers" ;
         sel_kind = [ Ouglib.Data.Value ] ;
       }
      )
  with
    [id] -> id
  | _ -> assert false
;;

let graph = data.Ouglib.Data.graph ;;
let created_by_numbers_list =
  let l = Ouglib.Graph.succ graph numbers_list_id in
  let f acc (id, k) =
    match k with
      Ouglib.Data.Create -> id :: acc
    | _ -> acc
  in
  List.fold_left f [] l
;;

let (_,missing) =
  List.partition (fun id -> List.mem id created_by_numbers_list) number_variants
;;

match missing with
  [] -> exit 0
| _ ->
    let b = Buffer.create 256 in
    Ouglib.Dump.print_element_list data b  missing;
    prerr_endline
      (Printf.sprintf "The following constructors are not present in Foo.numbers:\n%s"
       (Buffer.contents b)
      );
    exit 1
;;
=== /myoug.ml ==

=== foo.ml ===
type number = One | Two | Three | Four;;

let numbers = [ One, 1 ; Two, 2 ];;
=== /foo.ml ==

Compilation of myoug.ml to myoug.x:
# ocamlc -I +oug str.cma toplevellib.cma oug.cma -o myoug.x myoug.ml 

Launching myoug.x gives the following:
The following constructors are not present in Foo.numbers:
f Foo.number.Three
f Foo.number.Four

Of course, you can adapt the program to fit your needs: have a makefile
target to create the oug dump and use this dump in various checking
programs, or in one program performing various checks.

Hope this helps,

Maxence

[1] http://home.gna.org/oug/index.fr.html



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

* Re: Create a constraint between variant type and data list
  2010-09-03 17:16 Create a constraint between variant type and data list Sylvain Le Gall
                   ` (4 preceding siblings ...)
  2010-09-04 16:25 ` Anil Madhavapeddy
@ 2010-09-17  8:57 ` Sylvain Le Gall
  5 siblings, 0 replies; 12+ messages in thread
From: Sylvain Le Gall @ 2010-09-17  8:57 UTC (permalink / raw)
  To: caml-list

Hello all,

On 03-09-2010, Sylvain Le Gall <sylvain@le-gall.net> wrote:
> Hello all,
>
> I would like to somehow enforce that a variant type is associated with
> an entry in a data list. 
>
> For example, 
>
> I would like to define:
>
> type license = GPL | LGPL 
>
> and 
>
> let data = [ GPL, "GNU Public license"; 
>              LGPL, "GNU Lesser General Public license" ]
>
>

Thank you for all your answer. I pick the one from oleg@okmij.org, I
hide the license with a type and the creation of license is done in the
module. The to_string/from_string is done by registering extra data in
an Hashtable.

See the implementation here.
http://darcs.ocamlcore.org/cgi-bin/darcsweb.cgi?r=oasis;a=headblob;f=/src/oasis/OASISLicense.mli
http://darcs.ocamlcore.org/cgi-bin/darcsweb.cgi?r=oasis;a=headblob;f=/src/oasis/OASISLicense.ml

Regards,
Sylvain Le Gall


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

end of thread, other threads:[~2010-09-17  8:57 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-03 17:16 Create a constraint between variant type and data list Sylvain Le Gall
2010-09-03 17:38 ` [Caml-list] " bluestorm
2010-09-03 21:28   ` Sylvain Le Gall
2010-09-17  7:29     ` [Caml-list] " Maxence Guesdon
2010-09-03 18:51 ` [Caml-list] " Martin Jambon
2010-09-03 19:39   ` Ashish Agarwal
2010-09-03 21:13 ` Maxence Guesdon
2010-09-03 21:25   ` Sylvain Le Gall
2010-09-04  6:35 ` [Caml-list] " Julien Signoles
2010-09-04  6:40   ` Julien Signoles
2010-09-04 16:25 ` Anil Madhavapeddy
2010-09-17  8:57 ` Sylvain Le Gall

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).