I'm struggling with understanding some aspects of using GADTs in OCaml. Let me try and talk you through my example and my understanding ...
I'm trying to implement Simon Peyton-Jones' classic paper on Combinatorial Contracts (https://www.microsoft.com/en-us/research/wp-content/uploads/2000/09/pj-eber.pdf)
I want to be able to manipulate an Observable, defined as a function from a date to a value of some type (realistically a float or a bool). So, using GADTs I define a function type and an Observation type
type _ value =
| Float : float -> float value
| Bool : bool -> bool value
type _ obs = Observation : (date -> 'v value) -> (date -> 'v) obs
What I think I am defining is that
- a
valueis either afloator abool(built using theFloatorBoolconstructors, and - an
obsis a function from adateto one of thevaluetypes, so either adate -> floator adate -> bool
Now I define an expression, which enables me to combine Observables
type _ expr =
| Konst : 'v value -> 'v expr
| Obs : 'v obs -> 'v expr
| Lift : ('v1 -> 'v2) * 'v1 expr -> 'v2 expr
So far so good. An expression is either a constant, an observation (which is either a date->float or date->bool), or a function applied to an expression.
Now I want to be able to evaluate an Observable. In reality, an Observable is built on a random process, so I have a Distribution module (Note that in the original paper the idea is to separate out the concept of an Observable, from it's implementation - so we could implement through lattices, monte carlo, or whatever approach we want).
module type Distribution_intf =
sig
type element = Element of float * float
type t = element list
val lift : t -> f:(float -> float) -> t
val expected : t -> float
end
so, given a compose function
let compose f g = fun x -> f (g x)
I should be able to think about the evaluation of an Observable. Here is a mapping function (I've taken out the Boolean case for clarity)
type rv = date -> Distribution.t
let rec observable_to_rv : type o. o Observable.expr -> rv =
let open Distribution in
function
| Konst (Float f) -> fun (_:date) -> [Element(1.0, f)]
| Lift (f,obs) -> compose (lift ~f) (observable_to_rv o) (*ERROR HERE *)
Now things get problematic. When I try and compile, I get the following error message (on the Lift pattern match):
Error: This expression has type v1#0 -> o
but an expression was expected of type float -> float
Type v1#0 is not compatible with type float
I don't understand why: A Lift expression has type
Lift: ('v1 -> 'v2) * 'v1 expr -> 'v2 expr
So given a Lift(f,o), then shouldn't the compiler constrain that since observable_to_rv has type date -> float, then 'v2 must be a float, and since lift has type float -> float then 'v1 must be a float, so Lift should be defined on any tuple of type (float -> float, float expr).
What am I missing ?
Steve