Continuing my series of questions regarding type-level arithmetic, I stumbled across another problem: return types of polymorphic recursive functions.
This is the code:
module Nat : sig
type z = Z
type 'n s = S of 'n
type ('n) nat =
Zero : (z) nat
| Succ : ('n) nat -> ('n s) nat
val of_int : int -> 'n nat
end = struct
type z = Z
type 'n s = S of 'n
type ( 'n) nat =
Zero : ( z) nat
| Succ : ('n) nat -> ('n s) nat
let rec of_int n : int -> 'n nat = function
0 -> Zero
| n -> Succ (of_int (n - 1))
end
Compiling yields:
Error: This expression [Succ (of_int (n - 1))] has type 'a s nat
but an expression was expected of type z nat
The problem is that the return value of the function is set to z nat by Zero in the first pattern matching clause. Instead it should be something like 'a. 'a nat? This problem also occurs when trying to make a function that adds two different Nat:s.
Grateful for your help.
The type int -> 'n nat, means forall n, int -> n nat: the user of the function may choose at which nto use the function. So you could call the of_int function saying "hey, give me a (z s) nat this time", just like you can pick which type [] : 'a list ends up being. That doesn't match what really happens, which is that you do not choose the type, it is determined by the value of the integer.
By definition, the value of the integer is not known to the type system, so you can't say precisely which type n nat you will get as a result. All you can say is that there exists some type n such as the result has type n nat. You can express that using "existential types", and there are various ways to do that, GADTs being one of them (GADTs are algebraic datatypes plus existential types and equality constraints).
type some_nat =
| Some : 'n nat -> some_nat
let rec of_int = function
| 0 -> Some Zero
| n ->
let (Some nat) = of_int (n - 1) in
Some (Succ nat)
some_nat is an existential wrapping around nat, it is equivalent to what you might write exists n. n nat if you had first-class existential types.
Handling addition is even harder, because you have to express at the type level that the type you get in return really corresponds to the addition of two other types. Here is how I defined the type for it:
type (_, _, _) add =
| AddZ : (z, 'b, 'b) add
| AddS : ('a, 'b, 'c) add -> ('a s, 'b, 'c s) add
type ('a, 'b) add_result =
| Result : ('a, 'b, 'c) add * 'c nat -> ('a, 'b) add_result
let rec add : type a b . a nat -> b nat -> (a, b) add_result = function
...
I'll let you define the body of add so that you can play with this stuff yourself.
I'm not very happy of all the runtime wrapping that goes around the add type (which is really just useful as a type-level witness), so maybe there is a better way.
Related
I'm trying to define a simple type system with a custom set type:
notation bot ("⊥")
typedef 'a myset = "UNIV :: 'a fset option set" ..
copy_bnf 'a myset
setup_lifting type_definition_myset
lift_definition myset :: "'a fset ⇒ 'a myset" is Some .
instantiation myset :: (type) bot
begin
lift_definition bot_myset :: "'a myset" is None .
instance ..
end
free_constructors case_myset for
myset
| "⊥ :: 'a myset"
apply (simp_all add: bot_myset_def myset_def)
apply (metis Rep_myset_inverse option.collapse)
apply (metis Abs_myset_inverse iso_tuple_UNIV_I option.inject)
apply (metis Abs_myset_inverse iso_tuple_UNIV_I option.distinct(1))
done
lift_definition map_myset :: "('a ⇒ 'b) ⇒ 'a myset ⇒ 'b myset"
is "map_option ∘ fimage" .
Type system has integer, real and set types:
datatype type =
IType
| RType
| SType type
Also there are 3 kinds of values:
datatype val =
IVal int
| RVal real
| SVal "val myset"
I need to define a function type_of which determines a type of a value:
fun elem_type :: "val myset ⇒ type" and
type_of :: "val ⇒ type" where
"elem_type xs = ???"
| "type_of (IVal x) = IType"
| "type_of (RVal x) = RType"
| "type_of (SVal x) = SType (elem_type x)"
value "type_of (SVal (myset {|IVal 1, IVal 2|}))"
value "type_of (SVal (myset {|RVal 1, RVal 2|}))"
value "type_of (SVal (myset {|SVal (myset {|IVal 1|})|}))"
Could you suggest how to get element type of myset?
I would say there is a fundamental problem with your modelling: your type_of function seems to suggest that every value has exactly one type.
In his comment, Lars has already pointed out that there are some values (e.g. a set containing both integers and real values) that you would probably consider to be invalid and that therefore have no type at all. That you could handle by returning ‘undefined’ or something like that, perhaps, and introduce some kind of additional ‘well-formedness’ predicate for values, but I'm not sure that's a good idea.
Another problem is that there are some values that are polymorphic, i.e. that have more than one type, in a sense: For instance, what should your type_of function return for the empty set, i.e. SVal (myset {|})? There are infinitely many reasonable types that this thing could have.
The usual approach to handle this is to introduce a typing relation instead of a typing function so that each value can have several types – or none at all. Usually, you do this with the inductive command, defining an inductive predicate like has_type v t which states that the value v has the type t (or ‘can be given the type t’ in case of polymorphism).
Also, on an unrelated note, I am not sure why you define a new type and use copy_bnf, free_constructors etc. I don't know what you're planning to do but my guess would be that whatever it is does probably not get easier with that typedef; it will probably just cause some annoyances.
I prepare for GATE Exam. one of the oldest question is unfamiliar with us. some experts please clarify this for us.
Which of the following can be a type for following ML function ?
my f(g, nil)=nil | f(g,x::xs)=(fn a ⇒ g(a,x))::f(g, xs);
1) (int *book → real) * bool list → (int → real) list
2) (bool *int → int) * real list → (bool → int) list
3) (int *int → real) * real list → (real → bool) list
4) (bool *real → int) * int list → (int → int) list
The Answer Sheets say (1) Is corrects. comments or short description for better understanding?
One of the first things you should do is rewrite the function definition yourself. This will force you to actually parse and understand it.
fun f (g, nil) = nil
| f (g, x :: xs) = (fn a => g (a, x)) :: f (g, xs);
So, f is a function, even the question says that, it must have type ... -> ...:
val f : ... -> ...
What does it receive? Let's take a look at the first pattern of the function:
fun f (g, nil) = nil
It's something of the form (a, b), which is a 2-tuple. The function's argument must be a tuple then:
val f : (... * ...) -> ...
Just by looking at the definition, we can't figure out what type g must have, but it uses nil for the second element of the tuple and nil is the empty list. That means the second component of the tuple must be a list of something:
val f : (... * ... list) -> ...
What else can we deduce? The return value is nil as well, which means that the function returns a list of something, unclear what that something is yet.
val f : (... * ... list) -> ... list
Ok, let's jump to the second pattern of the function definition now:
| f (g, x :: xs) = (fn a => g (a, x)) :: f (g, xs);
We don't find anything more about the type of the argument, we just got confirmation that the second element of the tuple must indeed be a list, because it uses the :: constructor.
Let's take a look at the body:
(fn a => g (a, x)) :: f (g, xs)
It looks like it's building a list, so it must return a list, which is in accordance with the return type we've sketched so far, i.e., ... list. Let's
try to figure out the type of elements.
It seems to add a function object as the head of the list built by recursively calling the function f, which we're currently investigating. So the elements of the list we're returning must be functions:
val f : (... * ... list) -> (... -> ...) list
What does that function do, though? It calls g with a 2-tuple argument. Now we can fill in some information about the first element of the 2-tuple f receives. It must be a function that receives a 2-tuple as well:
val f : (((... * ...) -> ...) * ... list) -> (... -> ...) list
Can we say anything about the a parameter received by the function literal added to the return list? Not really, just that it's passed to g. Can we tell anything about the type of x? Not really, just that it's passed to g. Moreover, is there any constraint between a and x? Doesn't look like it. So far, we can only tell that g's type must be looking something like this:
('a * 'b) -> 'c'
Where 'a, 'b and 'c are polymorphic types, i.e., any concrete type can satisfy them. You can view them as wholes. We can now fill in more of the type for the f function:
val f : ((('a * 'b) -> 'c) * ... list) -> (... -> ...) list
We can do more actually, we've already assign a type to the x variable, but that comes from the second argument of the 2-tuple received by f, so let's fill in that too:
val f : ((('a * 'b) -> 'c) * 'b list) -> (... -> ...) list
And we can even fill in the element type of the returned list, because we've already assigned types for that, too.
val f : ((('a * 'b) -> 'c) * 'b list) -> ('a -> 'c) list
We can remove some extra parenthesis from the type we came up with without changing the meaning, because of the type operator precedence rules:
val f : ('a * 'b -> 'c) * 'b list -> ('a -> 'c) list
Now, our function's type is complete. However, this type can't be found in the list of possible answers, so we'll have to see if any of them can be used instead of what we've determined. Why? Because our function type uses type variables, which can be filled in by concrete types. Let's take them one by one:
Choice 1
val f : ('a * 'b -> 'c) * 'b list -> ('a -> 'c) list
val f : (int * bool -> real) * bool list -> (int -> real) list
It looks like 'a could be int, 'b could be a bool (it's book in what you've pasted, but I'm assuming it was a typo) and 'c could be a real. All the replacements match these correspondences, so we declare that, yes, the first choice can be a possible type for the given function, even though not the most general. Let's take the second choice:
Choice 2
val f : ('a * 'b -> 'c) * 'b list -> ('a -> 'c) list
val f : (bool * int -> int) * real list -> (bool -> int) list
The type-variable to concrete -type correspondence table could be this:
'a -> bool
'b -> int
'c -> int
'b -> real
We can stop here because we can see that 'b was assigned to different types, so this function signature can't be assigned to the implementation we've been given.
Choice 3 and 4
They are similar to choice 2, but I'll let them as an exercise to the reader :)
When I write an Ocaml function to recursively compose the same function n times, I did this:
let rec compose f n =
(fun x -> if n = 1 then f x else ((compose f (n-1))) (f x));;
It gives the type
val compose : ('a -> 'a) -> int -> 'a -> 'a = <fun>
what is the difference between type
('a -> 'a) -> int -> 'a -> 'a
and type
('a -> 'a) -> int -> ('a -> 'a)
?
How would a similar compose function look with the latter type?
There is no difference between them. But sometimes authors of libraries use parens to denote, that the computation is actually staged, so that it is better to apply it partially, so that you can get a more efficient function, rather then applying it every time. But from the type system perspective this functions are exactly the same, since -> type operator associates to the right.
As an addition to #ivg's answer, here is a mistake i made. Consider these two functions which have the same type int -> int -> int. (;; added for pasting in the toplevel)
let f a b =
let ai =
Printf.printf "incrementing %d to %d\n" a (a + 1);
a + 1 in
b + ai;;
let f' a =
let ai =
Printf.printf "incrementing %d to %d\n" a (a + 1);
a + 1 in
function b -> b + ai;;
If you partially apply
let f_1 = f 1;;
let f'_1 = f' 1;;
you'll see the difference. What I thought is that f does what f' does. In reality, Ocaml is eager but not so eager as to start evaluating away in partial function applications until it runs out of arguments. To point out the difference, it can make sense to write f''s type as int -> (int -> int).
Is there a way to have a recursive call but with different type parameters?
Here's an example which I think should compile, but doesn't.
let swap (a, b) = (b, a)
module Test : sig
val test : bool option ->
('a -> 'b -> 'c) * ('b -> 'a -> 'c)
-> 'a -> 'b -> 'c
end = struct
let rec test (b : bool option)
(f : ('a -> 'b -> 'c) * ('b -> 'a -> 'c))
(x : 'a) (y : 'b) : 'c =
match b with
| None -> (fst f) x y
| Some true -> (test None f x y)
| Some false -> (test None (swap f) y x)
end
The compiler insists that 'a and 'b must be the same type in test even though there's no reason for it. Is this a bug?
This is actually an very interesting question.
I didn't know the answer before, but by reading through the two answers before me, plus a bit research, I will try explain and answer below.
Basically, what you want to achieve is the signature like this
val test : bool option -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c
First of all, I have to emphasise that trying to force the parameters to be different polymorphic types, i.e., use 'a, 'b, 'c etc, will not necessarily force OCaml compiler to think that the parameters must have different types.
for example, if you do
let f (x:'a) (y:'b) = x + y;;
It seems you are forcing x and y to be different types, but after compiling, it gives
val f : int -> int -> int = < fun >
Basically, OCaml will anyway do its type inference, and apply the conclusion if it is not just against the forced type.
You may think the 'a and 'b in let f (x:'a) (y:'b) = x + y;; to be maybe x and y will have different types and also possibly same types. So it is pointless to force types of parameters like that, right?
So, let's remove all the types forced on parameters and we get
let rec test b f x y =
match b with
| None -> (fst f) x y
| Some true -> test None f x y
| Some false -> test None (swap f) y x
The type of test will be given by OCaml like this:
val test : bool option -> ('a -> 'a -> 'b) * ('a -> 'a -> 'b) -> 'a ->
'a -> 'b = < fun >
So, basically, OCaml thinks x and y must have the same types, and c is not there because the next available type tag for OCaml to use is 'b.
Why x and y must have same types?
When OCaml meets let rec test b f x y, ok, it will think x has type of 'a and y has type of 'b'.
When OCaml meets | Some true -> test None f x y, no problem, the above type inference still stand because you are pass same x and y to test again.
The the funny part is when OCaml meets | Some false -> test None (swap f) y x. You are trying pass y and x (notice the order) to test. In order to let test work, x and y must have the same type, right?
Basically, above is what the counter side of polymorphism recursion #Jeffrey Scofield answered.
polymorphism recursion means, a function can have some parameters whose types can be changed during recursion, instead of stay constant.
OCaml by default of course only allow constant parameter types.
So how does rank 2 polymorphism work?
So how to solve it?
You need a for_all type 'a.. have a look at my question: In OCaml, what type definition is this: 'a. unit -> 'a.
If we use 'a. or 'a 'b. in type definition, then it means it is really for all types, real polymorphic types, and please, ocaml, do not narrow them down as long as it does not harm.
let rec test : 'a 'b. bool option -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c =
fun b f x y ->
match b with
| None -> (fst f) x y
| Some true -> test None f x y
| Some false -> test None (swap f) y x
Above is the new version of test.
You force type with 'a 'b. for the function test and for 'a and 'b, ocaml will think they are really both polymorphic, and thus the parameter x and y can be accepted in both orders.
You're asking for polymorphic recursion, for which type inference is undecidable in the general case: Wikipedia on Polymorphic Recursion. So I don't think it's a bug.
I think there are ways to get what you want using rank-2 polymorphism.
I think it's a bug because if you create a new function (identical to the first one), then the type is correct:
# let rec t b f x y = match b with
| true -> (fst f) x y
| false -> u true (swap f) y x
and u b f x y = match b with
| true -> (fst f) x y
| false -> t true (swap f) y x;;
val t : bool -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c = <fun>
val u : bool -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c = <fun>
using OCaml 4.01.0:
module rec Test : sig
val test : bool option -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c
end = struct
let rec test b f x y =
match b with
| None -> (fst f) x y
| Some true -> (Test.test None f x y)
| Some false -> (Test.test None (swap f) y x)
end;;
See the section on Recursive modules.
Small example as a test case:
let add_float_int x y = x +. (float y);;
let add_int_float x y = add_float_int y x;;
let adds = add_float_int, add_int_float;;
List.map (fun x -> Test.test x adds 10 10.) [None; Some true; Some false];;
Functors in Standard ML are related to the module system and can generate structures based on other structures. An example of a functor generating list combinators for various types of lists is given below, but this example has a problem:
The various types of lists all have advantages -- for example, lazy lists can be infinitely long, and concantenation lists have a O(1) concat operator. But when all of these list types conform to the same signature, the functor can only use their general properties.
My question is therefore: What is a good example of when functors are useful and the various generated structures don't lose their special abilities?
signature MYLIST =
sig
type 'a t
val null : 'a t -> bool
val empty : 'a t
val cons : 'a * 'a t -> 'a t
val hd : 'a t -> 'a
val tl : 'a t -> 'a t
end
structure RegularList : MYLIST =
struct
type 'a t = 'a list
val null = List.null
val empty = []
val cons = op::
val hd = List.hd
val tl = List.tl
end
structure LazyList : MYLIST =
struct
datatype 'a t = Nil | Cons of 'a * (unit -> 'a t)
val empty = Nil
fun null Nil = true
| null _ = false
fun cons (x, xs) = Cons (x, fn () => xs)
fun hd Nil = raise Empty
| hd (Cons (x, _)) = x
fun tl Nil = raise Empty
| tl (Cons (_, f)) = f ()
end
structure ConcatList : MYLIST =
struct
datatype 'a t = Nil | Singleton of 'a | Concat of 'a t * 'a t
val empty = Nil
fun null Nil = true
| null (Singleton _) = false
| null (Concat (xs, ys)) = null xs andalso null ys
fun cons (x, xs) = Concat (Singleton x, xs)
fun hd Nil = raise Empty
| hd (Singleton x) = x
| hd (Concat (xs, ys)) = hd xs
fun tl Nil = raise Empty
| tl (Singleton x) = Nil
| tl (Concat (xs, ys)) = (* exercise *)
end
signature MYLISTCOMB =
sig
type 'a t
val length : 'a liste -> int
val map : ('a -> 'b) -> 'a liste -> 'b liste
val foldl : ('a * 'b -> 'b) -> 'b -> 'a liste -> 'b
val append : 'a liste * 'a liste -> 'a liste
val concat : 'a liste liste -> 'a liste
val sort : ('a * 'a -> order) -> 'a t -> 'a t
end
functor ListComb (X : MYLIST) : MYLISTCOMB =
struct
type 'a t = 'a X.t
open X
fun length xs =
if null xs then 0
else 1 + length (tl xs)
fun map f xs =
if null xs then empty
else cons(f (hd xs), map f (tl xs))
fun foldl f e xs =
if null xs then e
else foldl f (f (hd xs, e)) (tl xs)
fun append (xs, ys) =
if null xs then ys
else cons (hd xs, append (tl xs, ys))
fun concat xs =
if null xs then empty
else append (hd xs, concat (tl xs))
fun sort cmp xs = (* exercise *)
end
structure RegularListComb = ListComb (RegularList)
structure LazyListComb = ListComb (LazyList)
structure ConcatListComb = ListComb (ConcatList)
Not sure I fully understand your question. Obviously, functors are useful for defining modular abstractions that (1) are polymorphic, (2) require a whole set of operations over their type parameters, and (3) provide types as part of their result (in particular, abstract types), and (4) provide an entire set of operations.
Note that your example doesn't make use of (3), which probably is the most interesting aspect of functors. Imagine, for example, implementing an abstract matrix type that you want to parameterise over the vector type it is based on.
One specific characteristic of ML functors -- as well as of core-language polymorphic functions -- is that they are parametric. Parametricity is a semantic property saying that evaluation (of polymorphic code) is oblivious to the concrete type(s) it is instantiated with. That is an important property, as it implies all kinds of semantic goodness. In particular, it provides very strong abstraction and reasoning principles (see e.g. Wadler's "Theorem's for free!", or the brief explanation I gave in reply to another question). It also is the basis for type-erasing compilation (i.e., no types are needed at runtime).
Parametricity implies that a single functor cannot have different implementations for different types -- which seems to be what you are asking about. But of course, you are free to write multiple functors that make different semantic/complexity assumptions about their parameters.
Hope that kind of answers your question.
Here is a number of useful examples of SML functors. They are made on the following premise: If you can do one set of things, this enables you to do another set of things.
A functor for sets: If you can compare elements, you can create sets using balanced data structures (e.g. binary search trees or other kinds of trees).
signature SET =
sig
type elem
type set
val empty : set
val singleton : elem -> set
val union : set -> set -> set
val intersect : set -> set -> set
end
signature ORD =
sig
type t
val compare : t * t -> order
end
functor BalancedSetFunctor(structure Cmp : ORD) :> SET =
struct
type elem = Cmp.t
type set = ...
val empty = ...
fun singleton x = ...
fun union s1 s2 = ...
fun intersect s1 s2 = ...
end
A functor for iteration: For any kind of collection of things (e.g. lists), if you can iterate them, you can automatically fold them. You can also create different structures for different ways to fold across the same datatype (e.g. pre-order, in-order and post-order traversal of trees).
signature ITERABLE =
sig
type elem
type collection
val next : collection -> (elem * collection) option
end
signature FOLD =
sig
type elem
type collection
val fold : (elem * 'b -> 'b) -> 'b -> collection -> 'b
end
functor FoldFunctor(Iter : ITERABLE) :> FOLD =
struct
type elem = Iter.elem
type collection = Iter.collection
fun fold f e xs =
case Iter.next xs of
NONE => e
| SOME (x, xs') => fold f (f (x, e)) xs'
end
Functors are "lifters" - they lift (this verb is standard FP terminology): for a given set of types and values, they let you create a new set of types and values on top of them.
All the modules conforming to the required module interface can "benefit" from the functor, but they don't lose their special abilities, if by abilities you mean the implementation specific advantages.
Your very example, for instance, works well to demonstrate my point: concatenation lists have a very fast concat operator, as you wrote, and when lifted with the functor, this 'ability' doesn't vanish. It's still there and perhaps even used by the functor code. So in this example the functor code actually benefit from the list implementation, without knowing it. That's a very powerful concept.
On the other hand, since modules have to fit an interface when lifted by a functor, the superfluous values and types are lost in the process, which can be annoying. Still, depending on the ML dialect, this restriction might be somewhat relaxed.