[LLVMdev] improving the ocaml binding's type safety

Erick Tryzelaar idadesub at users.sourceforge.net
Sat Mar 15 14:49:49 PDT 2008


So just to compare, here are two different typesafe phantom type
implementations. One is bottom down, the other bottom up. This is an
example of the following functions:

string Value::getName()
bool Constant::isNull()
bool GlobalValue::isDeclaration()
bool GlobalVariable::isGlobalConstant()
bool Function::isVarArg()

Driver code:

val make_constant : unit -> llconstant t
val make_global_variable : unit -> llglobalvariable t
val make_function : unit -> llfunction t

(* typesafe *)
value_name (make_constant ());;
value_name (make_global_variable ());;
value_name (make_function ());;
is_null (make_constant ());;
is_declaration (make_global_variable ());;
is_declaration (make_function ());;
is_global_constant (make_global_variable ());;
is_var_arg (make_function ());;

(* type errors *)
is_null (make_global_variable ());;
is_null (make_function ());;
is_declaration (make_constant ());;
is_global_constant (make_constant ());;
is_var_arg (make_constant ());;
is_var_arg (make_global_variable ());;


Bottom up. The advantage of this one is that we'll always be typesafe
since we're saying the arguments must be a subset of the specified
variants, and thus all the variants are closed. The disadvantage is
that if we want to add another type we have to edit all the type
definitions. This also means that we can't add another library that
subclasses from something and still use these functions:

type 'a t
type llfunction = [ `Function ]
type llglobalvariable = [ `GlobalVariable ]
type llglobalvalue = [ llfunction | llglobalvariable ]
type llconstant = [ `Constant ]
type llvalue = [ llconstant | llglobalvalue ]

val value_name : [<  llvalue] t -> string
val is_null : [<  llconstant] t -> bool
val is_declaration : [< llglobalvalue] t -> bool
val is_global_constant : [< llglobalvariable] t -> bool
val is_var_arg : [< llfunction] t -> bool


The other way is top down. This lets us extend our types, but
sacrifices some type safety, as we're saying that the arguments are a
superset of the variants. We can control this by limiting who can
create 't's:

type 'a t

type llvalue = [ `Value ]
type llconstant = [ llvalue | `Constant ]
type llglobalvalue = [ llvalue | `GlobalValue ]
type llglobalvariable = [ llglobalvalue | `GlobalVariable ]
type llfunction = [ llglobalvalue | `Function ]

val value_name : [> `Value] t -> string
val is_null : [> `Constant] t -> bool
val is_declaration : [> `GlobalValue] t -> bool
val is_global_constant : [> `GlobalVariable] t -> bool
val is_var_arg : [> `Function] t -> bool


So what's better to use?



More information about the llvm-dev mailing list