Define types in OCaml - ocaml

If I have the types:
type test = float * float
type helper =
| test1 of test
| test2 of test
| test3 of test
And right now I have a function with the type:
f: ('a * 'b) -> ('b -> 'a) -> 'a
What will the type of f(helper(1., 2.), true) be? Will it be something like:
(helper * bool) -> (bool -> helper) -> helper
Or am I still confused with the polymorphic type in OCaml.

A value of type helper would be constructed with test1, test2, or test3, which must begin with a capital letter.
type test = float * float
type helper =
| Test1 of test
| Test2 of test
| Test3 of test
If we then test f (Test1 (1., 2.), true), we have partially applied f and get a function which takes a function argument and returns helper, or a type signature of:
(bool -> helper) -> helper

Related

Type of statement given a function with polymorphic type ocaml

If I have a function with polymorphic type
f : 'a -> 'a * 'a -> ('a * 'a) list
I also have:
type test = float * float
What will be the type of the following statement?
let ab: test = (0., 0.) in fun x -> f x test
I'm confused with the x here, is it the name of fun or it's simply input of fun, cause the input for f should be 'a, but not 'a -> 'a.
Right now I'm thinking if the type of the statement should be:
'a -> test * test -> (test * test) list
You have a good tool to try all your hypothesis on OCaml type system at hand: the OCaml REPL.
Currently,
type test = float * float
exception Hole
let f: 'a -> ('a * 'a) -> ('a * 'a) list = fun x -> fun y -> raise Hole
let mysterious =
let ab: test = (0., 0.) in fun x -> f x cd
yields
Error: Unbound value cd
Thus the mysterious expression doesn't have a type because it is not well defined.
EDIT: After correcting the typo,
let mysterious =
let ab: test = (0., 0.) in fun x -> f x ab
will yield the correct type. Then you should compare this type with the one you obtained by applying the type inference rule by hand to catch your mistake.
However, without knowing how you concluded that the type of mysterious was 'a -> test * test -> (test * test) list it is hard to pinpoint the first mistake in your reasoning.
There seems to be some basic syntax confusion regarding functions.
fun x -> ...
You've introduced an anonymous function that takes an argument named x and returns some other value.
If you wish to give that function a name, you can bind a name to it.
let foo = fun x -> ...
Note that the following is equivalent.
let foo x = ...

How do I Pattern-match GADT Types Get Alternative More Concrete Return Types?

I want to exploit GADT to implement the type ('a, 'b) liInstr_t in order to hold various types of instructions which are recursively decoded into basic operations (if need be) that then executed. (Eventually, I should construct more abstract types over them but in a mechanical, compositional and scripted fashion.) Unfortunately, I have difficulties associating the locally abstract types from pattern-matching function argument with the alternative concrete return types desired for the GADT.
I believe I'm missing something fundamental or making wrong assumptions though I have looked at the ocaml 4.10.0 manual on locally abstract types and gadts, the Real world Ocaml book, and the responses to similar questions such as here and here. This is because I seem to follow their explanations but cannot somehow apply them to my task.
From the above, I understand that polymorphic type variables annotate functions so that they can take on (be unified with) arbitrary types compatible with their constraints, and that locally abstract types let us have different types along alternative paths through a pattern-matching expression, say. Also, the local abstract types cannot be unified but can be refined to concrete types compatible with GADT result types. As such, GADTs can recurse over polymorphic types, and aggregate multiple result types into a single sum type.
I deliberately let the type ('a, 'b) liInstr_t have two type variables (so I can add more later), and its variants capture various constraint formats and scenarios I may have to use together.
type
liLabel_t = string (* Instruction name (label) *)
and
context_t = string (* TODO: execution context *)
and
'a context_list_t = 'a list
and
'a liChooser_t = 'a -> int (* get index of i-th list entry *)
and
('a, 'b) liInstr_t =
LiExec: 'a -> ('a, 'b) liInstr_t (* executable operation *)
| LiExecTRY: ('a, _) liInstr_t (* Ignore: Experiment on GADT *)
| LiLab: liLabel_t -> ('a, 'b) liInstr_t (* instruction label *)
| LiLabTRY: (liLabel_t, _) liInstr_t (* Ignore: Experiment on GADT *)
| LiSeq: 'a liChooser_t * 'b list -> ('a, 'b) liInstr_t (* sequence *)
| LiAlt: 'a liChooser_t * 'b list -> ('a, 'b) liInstr_t (* choice *)
| LiLoop: 'a liChooser_t * 'b list -> ('a, 'b) liInstr_t (* loop *)
| LiName: 'a liChooser_t * liLabel_t * 'b context_list_t ->
('a, 'b) liInstr_t (* change context *)
| Err_LiInstr: ('a, 'b) liInstr_t (* error handling *)
| Nil_LiInstr: ('a, 'b) liInstr_t (* no action *)
After experimenting, the sample function used is:
let ft1: type b c. (b, c) liInstr_t -> b = function
(* *) | LiExec n -> n
(* *) | LiExecTRY -> "4"
(* *) | LiLab s -> "LiLab"
(* *) | LiLabTRY -> "LiLabTRY"
(* *) | LiSeq (f, il) -> "LiSeq"
(* *) | LiAlt (f, il) -> "LiAlt"
(* *) | LiLoop (f, il) -> "LiLoop"
(* *) | LiName (f, il, ic) -> "LiName"
(* *) | Err_LiInstr -> "Err_LiInstr"
(* *) | Nil_LiInstr -> "Nil_LiInstr"
;;
and it gave the error:
Line 3, characters 22-25:
3 | (* *) | LiExecTRY -> "4"
^^^
Error: This expression has type string but an expression was expected of type
b
I still got errors when I changed the function annotation (and typing), or commented out some alternatives in the function pattern matching and the GADT type variants. Some of the errors (elided for brevity) were obtained as follows:
Using an extra locally-typed variable:
let ft1 : type b c d. (b, c) liInstr_t -> d = function ...
2 | (* *) | LiExec n -> n
^
Error: This expression has type b but an expression was expected of type d
Using only polymorphic type variables:
let ft1: 'b 'c. ('b, 'c) liInstr_t -> 'b = function ...
Error: This definition has type 'c. (liLabel_t, 'c) liInstr_t -> liLabel_t
which is less general than 'b 'c. ('b, 'c) liInstr_t -> 'b
My questions then are the following:
How can we capture and use the abstract types identified with alternative paths? A locally abstract type should bind (or be refined) to compatible concrete type(s) for values found in the resulting expression, or can be ignored, right? Ignoring recursion, this example:
let rec eval : type a. a term -> a = function
| Int n -> n (* a = int *)
| Add -> (fun x y -> x+y) (* a = int -> int -> int *)
| App(f,x) -> (eval f) (eval x)
(* eval called at types (b->a) and b for fresh b *)
on expression evaluation in the ocaml manual seems to suggest that is the case, at least for a 1-parameter GADT type. So, why aren't my types b and c not suitably bound (or refined) in the return type? And if they are binding (or being refined), which should bind to abstract type b and which to c, if at all? How can I find values for my return type so they can correctly associate with the abstract, value-less types reaching them. For, there is seems no way to obtain a result that has the type b in my first error above!
Why am I forced to have the same result type for the alternative paths to succeed (string type in my examples) whereas all possible result types of the GADT should be admissible. In this regard, the first variant (LiExec n -> n) seemed forced to have type string! Also, the abstract types and polymorphic variables along execution paths seem irrelevant to the result type!
I could not reproduce it but at one point, making the first variant LiExec n -> 4 seemed to require integer return values from all alternative pattern matches. If indeed this is the case, why should abstract types on alternative paths require values from the same non-GADT return type? (This behaviour is of non-polymorphic types, right?)
To work around incomprehensible issues on polymorphic types and locally abstract types, is there a simple way to mix them in a constraint? Various permutations to mix them always seem to result in a syntax error. e.g.:
let ft1: (type d) 'b 'c. ('b, 'c) liInstr_t -> d = function
^^^^
Error: Syntax error
Suppose we have the following GADT:
type _ simple_gadt =
| Con : 'a -> 'a simple_gadt
The type signature of Con can be understood as ('a : Type) -> 'a -> 'a simple_gadt (not real OCaml syntax); in other words, it takes a type as its first argument, and the rest of the type is dependent on this input type. The client provides the type; for example:
let value : int simple_gadt = Con 0
Implicitly, you can understand this definition as really meaning let value = Con(type int, 0), where the type is given as an argument (again, not real OCaml syntax).
When you write a function that takes a 'a simple_gadt as an argument, you don't know what 'a is. 'a is said to be an "existential type" provided by the caller of the function. Consider the following function:
let f (type a) (param : a simple_gadt) : a = match param with
| Con x -> x
The type of f is 'a . 'a simple_gadt -> 'a. A client can evaluate f (Con 0) and get back 0, of type int. A client can also evaluate f (Con true) and get back true, of type bool. The definition of the function has no control over what the actual type 'a is; only the caller does.
Suppose we attempt to define:
let g (type a) (param : a simple_gadt) : a = match param with
| Con _ -> ""
One would be able to evaluate g (Con 0) and get back "", a string, but based on the type of Con 0, the output of the function should be an int. This is clearly a type error, so g has an ill-typed definition, and the compiler rightfully rejects it. Likewise, your definition
let ft1: type b c. (b, c) liInstr_t -> b = function
(* ... *)
(* *) | LiExecTRY -> "4"
(* ... *)
is ill-typed because it assumes that b is string, while b could be any type that the caller provides. It looks like you have other similar type errors because you are attempting to pick more specific types for the existential types.
If the caller can choose any type, how can one use GADTs to "refine" the type variable to a more concrete type? The only way to do this is through the information that the caller provides.
Consider the following type definition:
type _ term =
| Abs : ('a -> 'b) -> ('a -> 'b) term
| App : ('a -> 'b) term * 'a term -> 'b term
| Bool : bool -> bool term
In a GADT, each constructor can make the type parameters more specific. Therefore, by pattern matching against each constructor, a function can refine the existential type parameter.
Consider this function on the GADT defined above:
let rec eval : 'a . 'a term -> 'a =
fun (type a) (term : a term) : a ->
match term with
| Abs f -> f
| App(f, x) -> (eval f) (eval x)
| Bool b -> b
In the Abs f case, Abs f is known to have type ('a -> 'b) term for some 'a and 'b by the definition of Abs. Similar reasoning applies for the App(f, x) and Bool b cases.
What's a universally quantified type from the caller's perspective (i.e. the caller can pick any type) must be an existentially quantified type from the callee's perspective (i.e. the callee must work with some fixed arbitrary type that the caller provides).
In brief, the type li_Instr_t as defined is not an interesting GADT and it can be rewritten to the strictly equivalent ADT
type ('a, 'b) liInstr_t =
| LiExec of 'a
| LiExecTRY
| LiLab of liLabel_t
| LiLabTRY
| LiSeq of 'a liChooser_t * 'b list
| LiAlt of 'a liChooser_t * 'b list
| LiLoop of 'a liChooser_t * 'b list
| LiName of 'a liChooser_t * liLabel_t * 'b context_list_t
| Err_LiInstr
| Nil_LiInstr
because the type declaration never introduces equations (or existential quantifications) between the result type and the constructor of the GADT.
If we look at a simple example for GADT:
type ('elt,'array) compact_array =
| String: (char, String.t) compact_array
| Float_array: (float, Float.Array.t) compact_array
| Standard: ('a, 'a array) compact_array
let init: type elt array.
(elt,array) compact_array -> int -> (int -> elt) -> array =
fun kind n f -> match kind with
| String -> String.init n f
| Float_array -> Float.Array.init n f
| Standard -> Array.init n f
The difference is that the constructor String constrains the type of compact_array to be (char,string) compact_array. Thus, when I observe String in the pattern matching above, I can introduce the equation
elt=char and array=string in the branch String and use those equation locally . Similarly, after observing the constructor Float_array in the pattern matching, I can work with the equation elt=float and array=Float.Array.t inside the corresponding branch.
Contrarily, with the definition of liInstr_t as it stands, observing a constructor of a value of type ('a,'b) liInstr_t brings no information on the type ('a,'b) liInstr_t. Consequently, the function ft1 of type type a b. (a,b) liInstr_t -> b is is promising to return a float array when called with ft1 (LiExecTRY:('a,float array) li_Instr_t). More generally, a function of type a b. (a,b) liInstr_t -> awhere no constructor impose a constraint onbis necessarily returning some value of typebthat was contained inside(a,b) liInstr_t` (or is not returning).
Using that knowledge, we can update your type liInstr_t to make the function ft1 works by adding the equations corresponding to the expected return type for ft1 to the definition of the type:
type liLabel_t = string
and context_t = string
and 'a context_list_t = 'a list
and 'a liChooser_t = 'a -> int
and ('a, 'b, 'ft1) liInstr_t =
| LiExec: 'a -> ('a, 'b,'a) liInstr_t (* ft1 returns the argument of LiExec *)
(* ft1 returns a string in all other cases *)
| LiExecTRY: ('a, 'b, string) liInstr_t
| LiLab: liLabel_t -> ('a, 'b, string) liInstr_t
| LiLabTRY: (liLabel_t, 'b, string) liInstr_t
| LiSeq: 'a liChooser_t * 'b list -> ('a,'b, string) liInstr_t
| LiAlt: 'a liChooser_t * 'b list -> ('a,'b, string) liInstr_t
| LiLoop: 'a liChooser_t * 'b list -> ('a,'b, string) liInstr_t
| LiName: 'a liChooser_t * liLabel_t * 'b context_list_t ->
('a,'b, string) liInstr_t
| Err_LiInstr: ('a, 'b, string) liInstr_t
| Nil_LiInstr: ('a, 'b, string) liInstr_t
and now that we have the right equation in place, we can define ft1 as:
let ft1: type a b c. (a, b, c) liInstr_t -> c = function
| LiExec n -> n
| LiExecTRY -> "4"
| LiLab s -> "LiLab"
| LiLabTRY -> "LiLabTRY"
| LiSeq (f, il) -> "LiSeq"
| LiAlt (f, il) -> "LiAlt"
| LiLoop (f, il) -> "LiLoop"
| LiName (f, il, ic) -> "LiName"
| Err_LiInstr -> "Err_LiInstr"
| Nil_LiInstr -> "Nil_LiInstr"
which typechecks without any error.

How do I interpret this SML typing expression?

(* val bar = fn : (’a * ’b -> ’b) -> ’b -> ’a list -> ’b *)
fun bar f b nil = b
| bar f b (h::t) = f (h, bar f b t)
This function was given to us with the instructions of explaining what it does. The only further information given are that the parameters are a binary function, a value, and a list. From looking at it, I already know that if the list is nil, it returns the b value, otherwise it applies the binary function to the list head and recurses. I just don't understand how to interpret this line:
(* val bar = fn : (’a * ’b -> ’b) -> ’b -> ’a list -> ’b *)
There are numerous tutorials explaining SML's typing, but I can't find anything in-depth enough to apply to this. Could anyone translate it to English so I know how it works for future reference?
To understand this type sgnature, you need to first understand currying.
A definition like
fun sum a b = a + b
has type int -> int -> int.
It is a function of one variable (an integer) where the return value is itself a function, one which sends ints to ints.
For example, val f = sum 1 assigns to f the function which adds one to its input (in other words, the successor function) so that, e.g., f 5 evaluates to 6.
In practice, such functions are often used like sum 3 4 but what is happening there isn't the passing of 2 values to sum. Rather, the one value 3 is passed, which returns a function, and this returned value is then applied to 4. Thus, sum 3 4 should be parsed as (sum 3) 4 rather than sum (3,4) -- which would be a type error.
Note that this is fundamentally different from something like
fun add (a,b) = a + b
which is a function of two variables, it has type int * int -> int, which is different than sum's type of int -> int -> int. The latter is not syntactic sugar for the former, but instead has a fundamentally different semantics.
When reading something such as int -> int -> int, you should read it as right-associative. In other words, it is the same as int -> (int -> int).
Another thing that is happening with ('a * 'b -> 'b) -> 'b -> 'a list -> 'b is the use of type variables 'a, 'b. This means that the type you are trying to parse is of a higher-order polymorphic function. It 'a and 'b can represent any type.
Putting it all together, a function, f, of type ('a * 'b -> 'b) -> 'b -> 'a list -> 'b is a function which takes as input any function whose type is of the form 'a * 'b -> 'b (a function of two variables whose return type is the type of the second variable). The return value of f is a function of the form 'b -> 'a list -> 'b. This latter is a function which takes an element of type 'b and returns a function which sends 'a lists to objects of type 'b
You could summarize it by saying that f is a curried function which takes a function of type ('a * 'b -> 'b), a value of type 'b, a list of values of type 'a, and returns a value of type 'b. That is accurate enough, but don't slip into thinking of it as equivalent to a function of type
('a * 'b -> 'b) * 'b * 'a list -> 'b
By the way, two of the most useful functions in SML, foldl and foldr have type ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, so this isn't merely an academic exercise. Being able to unpack such type descriptions is a key to being able to use such functions correctly.
I was able to find the solution based on what is apparently called type inferencing. I had never learned this before but
(* val bar = fn : (’a * ’b -> ’b) -> ’b -> ’a list -> ’b *)
is display of argument and return types for the function.
(’a * ’b -> ’b) refers to the first argument function. It requires 2 arguments ('b and 'a) in itself and returns 1 value 'b.
'b refers to the second argument, a value.
'a list refers to a list of values, the third argument in the function.
Finally, the last 'b is the return value.

Function type signatures

I tried to find this somewhere in internet, but I failed to do this. I want to learn OCaml and that's difficult to understand for me, how to do this.
My question is - how can I easy write a function, when I have a signature. For example - I have a signature like this:
((int -> int) -> int -> int -> int) -> (int -> int) -> int -> int -> int
and I want to write a function, that have signature like this above. Would someone help me or try to explain this? I would be very grateful :)
A function with type 'a -> 'b, takes values of some type 'a and returns values of some type 'b. To use specific types, a function of type int -> int takes an int and returns an int. This is the type of integer negation, for example.
One way to look at a function with type 'a -> 'b -> 'c is that it takes two values, of type 'a and 'b, and returns a value of type 'c. A specific example would be the type int -> int -> int, which is the type of integer addition (say):
# (+);;
- : int -> int -> int = <fun>
This pattern continues for more arguments.
The type you give has this type at the high level: 'a -> 'b -> 'c -> 'd -> 'e. So, it's a function with four arguments.
The first argument has type (int -> int) -> int -> int -> int, which is itself quite complicated. The second argument has type int -> int, described above. The third and fourth arguments have type int. The function returns an int.
Using this same analysis on the first argument, you can see that it's a function with three arguments. The first is a function of type int -> int, and the second and third are of type int. This function returns an int.
Here's a function of type (int -> int) -> int -> int -> int:
let myfun f a b =
(f a) + a + b
You can see the type in the OCaml toplevel:
# let myfun f a b =
(f a) + a + b;;
val myfun : (int -> int) -> int -> int -> int = <fun>
#
You should be able to work out the rest. I don't want to take all the enjoyment out of the problem by giving a full answer.

Functor compilation issue: Signature mismatch: Modules do not match

First the code:
module Boolean = struct
exception SizeMismatch
type boolean = T | F | Vec of boolean array
let to_bool v = match v with
T -> true
| F -> false
| _ -> raise SizeMismatch
end
module Logic = struct
type 'a var_t = { name: string; mutable value: 'a }
type 'a bexp = Const of 'a
| Var of 'a var_t
let eval exp = match exp with
Const x -> x
| Var x -> x.value
let make_var s v = { name = s; value = v }
let set v n = v.value <- n
let get_var_name v = v.name
let get_var_val v = v.value
end
module type EXP =
sig
type 'a var_t
type 'a bexp
val eval_exp : 'a bexp -> bool
val get_var_name : 'a var_t -> string
val get_var_val : 'a var_t -> 'a
end
module LogicExp =
struct
include Logic
let eval_exp exp = Boolean.to_bool (Logic.eval exp)
end
module FSM ( Exp : EXP ) =
struct
let print_var v = Printf.printf "%s = %d\n" (Exp.get_var_name v)
(Exp.get_var_val v)
end
module MyFSM = FSM(LogicExp)
let myvar = Logic.make_var "foo" 1;;
MyFSM.print_var myvar ;;
When I compile it I get the following error:
File "test.ml", line 57, characters 19-27:
Error: Signature mismatch:
Modules do not match:
sig
type 'a var_t =
'a Logic.var_t = {
name : string;
mutable value : 'a;
}
type 'a bexp = 'a Logic.bexp = Const of 'a | Var of 'a var_t
val eval : 'a bexp -> 'a
val make_var : string -> 'a -> 'a var_t
val set : 'a var_t -> 'a -> unit
val get_var_name : 'a var_t -> string
val get_var_val : 'a var_t -> 'a
val eval_exp : Boolean.boolean Logic.bexp -> bool
end
is not included in
EXP
Values do not match:
val eval_exp : Boolean.boolean Logic.bexp -> bool
is not included in
val eval_exp : 'a bexp -> bool
What I don't understand is how the more specific type isn't included in the more general type?
The error message is actually quite accurate:
Values do not match:
val eval_exp : Boolean.boolean Logic.bexp -> bool
is not included in
val eval_exp : 'a bexp -> bool
The MyFSM functor expects a module argument that, amongst other things, should contain a function eval_exp of type 'a bexp -> bool. That means that given a value of type 'a bexp for whatever choice of 'a the function should produce a value of type bool. You are, however, supplying a module that contains a function that only does this for one particular choice of 'a, i.e., the one where 'a is the type boolean from the module Boolean.
This quickest fix is to define your signature EXP as
module type EXP =
sig
type b (* added *)
type 'a var_t
type 'a bexp
val eval_exp : b bexp -> bool (* changed *)
val get_var_name : 'a var_t -> string
val get_var_val : 'a var_t -> 'a
end
so that eval_exp now operates on Boolean expressions over a fixed type b and then define LogicExp as
module LogicExp =
struct
type b = Boolean.boolean (* added *)
include Logic
let eval_exp exp = Boolean.to_bool (Logic.eval exp)
end
so that it fixes b to Boolean.boolean.
Implementing these changes will make your code compile.
Now, let us look at your question on "how the more specific type isn't included in the more general type?". This assumes that 'a bexp -> bool is indeed more general than boolean bexp -> bool, but in fact it isn't. A function type A -> B is considered more general than a function type C -> D if C is more general than A and B is more general than D:
A <: C D <: B
--------------------
C -> D <: A -> B
Note the "flipping" of C and A in the premise. We say that the function-space constructor ... -> ... is contravariant in its argument position (versus covariant in its result position).
Intuitively, a type is more general than another if it contains more values. To see why the function-space constructor is contravariant in its argument position, consider a function f of type A -> C for some types A and C. Now, consider a type B that is strictly more general than A, that is, all values in A are also in B, but B contains some values that are not in A. Thus, there is at least one value b to which we can assign type B, but not type A. Its type tells us that f knows how to operate on values of type A. However, if we were to (incorrectly!) conclude from A <: B that A -> C <: B -> C, then we could use f as if it had type B -> C and, hence, then we could pass the value b as an argument to f. But b is not of type A and f only knows how to operate on values of type A!
Clearly, covariance of ... -> ... in argument positions wouldn't work. To see that contravariance does work, consider the same types A, B, and C and now also consider a function g of type B -> C. That is, g knows how to operate on all values of type B. Contravariance of the function-space constructor in its argument position allows us to conclude that g can also be safely assigned the type A -> C. As we know that all values in A are also in B and g knows how to deal with all of B this poses no problems and we can safely pass values in A to g.