Version française
Home     About     Download     Resources     Contact us    
Browse thread
Polymorphic variants question
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: skaller <skaller@u...>
Subject: Re: [Caml-list] Polymorphic variants question
On Fri, 2006-09-01 at 18:31 +0100, David Allsopp wrote:

> let f x = if x = `A then (true, `B) else (false, x)

> let (f : [`A | `C] -> bool * [`A | `B | `C]) = fun x -> ...

BTW: when using polymorphic variants I find it is a good idea to:

(a) provide names (aliases, abbreviations) for your types.

(b) annotate function arguments and returns -- if not
   all of them, focus on the top level ones: it's necessary
   for the mli file anyhow.

(c) Prefer

let f x = match x with | ...

over

let f = fun x -> ...

and

let f = function | ..

and in particular for big top level functions like

let f (x:t1):t2 = 
  print_endline ("In Debug " ^ string_of_t1 x);
  let r : t2 = match x with
   ..
  in
  print_endline ("Out Debug " ^ string_of_t2 r);
  r

This shape instruments the input type, output type,
input value and output value.

The thing about polymorphic variants is that because

(a) the typing is structural not nominal (like ordinary variants)

(b) type inference tries to figure out the types from 
  usage scattered through the program

it is not only easy to extend them .. it is easy to
extend them incorrectly .. and the diagnostics from
big variants are horrible :)

Using annotations tends to localise the errors
and give you more information, including Ocaml's
smart trick of using your own alias. (This is very
clever :) It also often reports that you're missing
a particular constructor.

I've been using PM variants for a while now.
I just converted a couple of non-PM ones over.
I kind of like this function, it prints many of
the types I use in my compiler in one function,
almost like overloading :)

let string_of_term dfns term = match term with
  | #qualified_name_t as x -> string_of_qualified_name x
  | #regexp_t as x -> string_of_re x
  | #typecode_t as x -> string_of_typecode x
  | #tpattern_t as x -> string_of_tpattern x
  | #literal_t as x -> string_of_literal x
  | #expr_t as x -> string_of_expr x
  | #pattern_t as x -> string_of_pattern x
  | #statement_t as x -> string_of_statement 0 x
  | #exe_t as x -> string_of_exe 0 x
  | #btypecode_t as x -> string_of_btypecode dfns x

  (* hack .. the type because tbexpr_t is a pair not a variant *)
  | #bexpr_t as x -> string_of_bound_expression dfns (x,`BTYP_void)
  | #bexe_t as x -> string_of_bexe dfns 0 x
  | #ast_term_t as x -> string_of_ast_term 0 x

  (* hack cause we don't know the name *)
  | #symbol_definition_t as x -> string_of_symdef x "unk" [] 

  | #bbdcl_t as x -> string_of_bbdcl dfns x 0
  | #param_kind_t as x -> string_of_param_kind x
  | #property_t as x -> string_of_property x
  | #c_t as x -> string_of_code_spec x
  | #dcl_t as x -> string_of_dcl 0 "unk" (Some 0) [] x
  | #asm_t as x -> string_of_asm 0 x
  | #iface_t as x -> string_of_iface 0 x
  | #access_t as x -> string_of_access x
  | #biface_t as x -> string_of_biface dfns 0 x
  | #btype_qual_t as x -> string_of_bqual dfns x
  | #type_qual_t as x -> string_of_qual x
  | #requirement_t as x -> string_of_raw_req x
  | #ikind_t as x -> string_of_ikind x
  | #named_req_expr_t as x -> string_of_named_req_expr x
  | #raw_req_expr_t as x -> string_of_raw_req_expr x
  | #glr_term_t as x -> string_of_glr_term x

let st dfns term = string_of_term dfns term

This function doesn't DO anything I couldn't already do.
It just saves me worrying what the type of the term is,
and then trying to remember the name of the function that
prints it.

The PM variants let me unify any variants types I'm using,
which is why I converted most of the remaining non-PM
variants over.

Stick with them .. they're worth it!

-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net