Applicative vs Generative functors - functional-programming

I have recently been learning SML, when I came to know the terms - applicative and generative functors. I also know that SML uses generative functors.
I tried to Google the terms but couldn't find any compelling resources that explains what these terms mean and what are the differences between the two.
So, I just wanted to know the actual meaning of these terms in some practically understable way and how this relates to SML being generative.

It has to do with equality of abstract types in the module resulting from functor application.
Generative means that two invocations of a functor will produce modules containing non-equal abstract types.
Applicative means that two invocations of a functor will produce modules containing equal abstract types if the arguments are equal in some sense (such as being syntactically identical).
I'll give an example in OCaml, since it happens to support both:
module App (M : sig end) : sig
type t
val zero : t
end = struct
type t = int
let zero = 0
end
(* A () argument signifies a generative functor in OCaml. *)
module Gen (M : sig end) () : sig
type t
val zero : t
end = struct
type t = int
let zero = 0
end
module Empty = struct end
module A = App (Empty)
module B = App (Empty)
module C = App (struct end) (* The argument is syntactically different. *)
module D = Gen (Empty) ()
module E = Gen (Empty) ()
let _ = begin
(* A.t and B.t are compatible. *)
ignore (A.zero = B.zero); (* OK *)
(* A.t and C.t are not compatible because the functor arguments
* are not syntactically equal. *)
ignore (A.zero = C.zero); (* type error *)
(* D.t and C.t are not compatible because they are produced
* from generative functors. *)
ignore (D.zero = E.zero); (* type error *)
end

Related

Why are my types not working out? (Use of type 'a pred = 'a -> bool)

I have been stuck on this question for a while. I've been editing and reviewing and changing the types for a while but I can't get the type checker to accept what I am doing, probably because I don't fully understand the error/where I am going wrong on this. I am working with the type:
type 'a pred = 'a -> bool
I believe this means I can use 'a pred as a shortcut to mean 'a -> bool, so an int going to result in a bool in my case, but I don't fully get how to implement it because I can't find many examples on this online which I have checked for.
My latest version is below, but I am getting a few errors from the checker, including Error: operator and operand do not agree. Would someone be able to explain where my error is, and why?
Edit: I now think there is a mismatch between this function and the rest of the code. The rest of the code requires it to be an 'a, polymorphic, while here I am assuming it is an int. However, I'm not sure how to do this function (check if odd) while keeping it a polymorphic type.
fun isOdd (p : int) : bool =
case p
of 1 => true
| 0 => false
| _ => isOdd (p - 2)
I believe this means I can use 'a pred as a shortcut to mean 'a -> bool
That is correct.
In the case of your isOdd predicate, it is an int pred:
> val isOdd = fn : int -> bool
- isOdd : int pred;
> val it = fn : int -> bool
Perhaps your misconception lies in the fact that in spite of expressing : int pred, the result in the REPL is still described as int -> bool? This is because we have only defined a type alias, and those tend reduce to their non-aliased form in SML.
Or perhaps your misconception lies in the 'a reducing to some concrete value? You can operate with 'a pred by not referring to concrete values of 'a. For example, if you want to filter an 'a list for only values that are true for a given 'a pred, then the standard function List.filter will have the type:
- List.filter : 'a pred -> 'a list -> 'a list;
> val 'a it = fn : ('a -> bool) -> 'a list -> 'a list
I'm not sure how to do this function (check if odd) while keeping it a polymorphic type.
I'm not sure, either.
Oddness is a property of integers, not arbitrary types 'a.
You would need to extend the meaning of "odd" to any type first. Then you would need some kind of overloading, since the oddness of every type presumably isn't determined by the same mechanism. I'm pretty sure this is a side-track caused by one or two confusions.

SML: Value restriction error when recursively calling quicksort

I'm writing a quicksort function for an exercise. I already know of the 5-line functional quicksort; but I wanted to improve the partition by having it scan through the list once and return a pair of lists splitting the original list in half. So I wrote:
fun partition nil = (nil, nil)
| partition (pivot :: rest) =
let
fun part (lst, pivot, (lesseq, greater)) =
case lst of
[] => (lesseq, greater)
| (h::t) =>
if h <= pivot then part (t, pivot, (h :: lesseq, greater))
else part (t, pivot, (lesseq, h :: greater))
in
part (rest, pivot, ([pivot], []))
end;
This partitions well enough. It gives me a signature val partition = fn : int list -> int list * int list. It runs as expected.
It's when I use the quicksort below that things start to break.
fun quicksort_2 nil = nil
| quicksort_2 lst =
let
val (lesseq, greater) = partition lst
in
quicksort_2 lesseq # quicksort_2 greater
end;
I can run the above function if I eliminate the recursive calls to quicksort_2; but if I put them back in (to actually go and sort the thing), it will cease to run. The signature will be incorrect as well, giving me val quicksort_2 = fn : int list -> 'a list. The warning I receive when I call the function on a list is:
Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...)
What is the problem here? I'm not using any ref variables; the type annotation I've tried doesn't seem to help...
The main issue is that you're lacking the singleton list base case for your quicksort function. It ought to be
fun quicksort [ ] = [ ]
| quicksort [x] = [x]
| quicksort xs =
let
val (l, r) = partition xs
in
quicksort l # quicksort r
end
which should then have type int list -> int list given the type of your partition. We have to add this case as otherwise you'll never hit a base case and instead recurse indefinitely.
For some more detail on why you saw the issues you were having though:
The signature will be incorrect as well, giving me val quicksort_2 = fn : int list -> 'a list
This is because the codomain of your function was never restricted to be less general than 'a list. Taking a look at the possible branches in your original implementation we can see that in the nil branch you return nil (of most general type 'a list) and in the recursive case you get two 'a lists (per our assumptions thus far) and append them, resulting in an 'a list---this is fine so your type is not further restricted.
[Value Restriction Warning]
What is the problem here? I'm not using any ref variables
The value restriction isn't really related to refs (though can often arise when using them). Instead it is the prohibition that anything polymorphic at the top level must be a value by its syntax (and thus precludes the possibility that a computation is behind a type abstractor at the top level). Here it is because given xs : int list we (ignoring the value restriction) have quicksort_2 xs : 'a list---which would otherwise be polymorphic, but is not a syntactic value. Correspondingly it is value restricted.

OCaml: recursion with first-class modules and existential type

I'm trying to make a function that uses two modules (of the same type) alternately while going deeper into recursion. I pass the modules as arguments and everything went wrong when I added an existential type to the module. What surprised me a little is that if I make the function non-recursive (like all remotely similar examples I found), it works.
Here is what I think is the minimal example (just a single module passed around):
module type TEST =
sig
type t
val foo : t -> unit
end
let rec foo
(type a)
(module Test : TEST with type t = a)
(arg : a) : unit =
(* Test.foo arg *) (* <- works *)
(* I tried various type annotations, but none worked: *)
foo (module Test : TEST with type t = a) (arg : a)
Error message for the example:
Error: This expression has type
(module TEST with type t = a) -> a -> 'b
but an expression was expected of type
(module TEST with type t = a) -> a -> 'b
The type constructor a would escape its scope
Why doesn't it work and what can I do to make it work?
Not sure to completely understand your Error, but when doing recursion, it is often better to put the type annotation at the highest level.
Here is a version that works:
module type TEST =
sig
type t
val foo : t -> unit
end
let rec foo : type a. (module TEST with type t = a) -> a -> unit
= fun (module Test) arg ->
if true
then foo (module Test) arg
else Test.foo arg

Try to further understanding the interface/module of OCaml

I understand in OCaml there are concepts of interfaces and module.
And I understand how to use them now.
However, what I don't understand is how to fully utilise them.
For example, in Java, let's say we have a interface Map and we also have Hashtable and HashMap that implement Map.
In code, I can do like:
Map m = new Hashtable();
m.put("key", value);
Someday, if I change my mind, I can change to Hashmap very quickly by changing Map m = new Hashtable(); to Map m = new HashMap();, right?
But how can I easily do that in Ocaml?
For example, I have MapSig and 'HashMap:MapSigand "Hashtable:MapSig in OCaml.
How can I change the implementation easily?
I don't think I can because in OCaml I have to do like:
let m = Hashtable.create ();;
Hashtable.put m key value;;
if I want to use HashMap instead, I have to replace every Hashtable with HashMap in the code, right?
Edit:
I am not only seeking a way to make a alias to modules. I also consider the validity of implementations, i.e., whether the implementation follow the desired interface.
For example, in above Java example, only if HashMap has implemented Map interface, I can replace Hashtable with HashMap. otherwise, Java compiler will complain.
but if I do module M = Hashtable in OCaml, and if HashMap does not follow MapSig and I replace Hashtable with HashMap, what will happen? I think compiler won't complain, right?
Here's an example that shows what I think you're asking for:
# module type HASH = sig type t val hash : t -> int end ;;
module type HASH = sig type t val hash : t -> int end
# module I = struct type t = int let hash i = i end ;;
module I : sig type t = int val hash : 'a -> 'a end
# module J = struct type t = int end ;;
module J : sig type t = int end
# module M : HASH = I ;;
module M : HASH
# module N : HASH = J ;;
Error: Signature mismatch:
Modules do not match: sig type t = int end is not included in HASH
The field `hash' is required but not provided
The extra ": HASH" specifies that the module must match the HASH signature (and it also restricts it to that signature).
Just as a side comment, I believe the OCaml module system is world famous for its expressivity (at least in module system circles). I'm still a beginner at it, but it is worth studying.
Since 3.12.1 OCaml allows this syntax for opening and aliasing modules:
let foo .... =
let module HashTable = HashMap in (* magic is here *)
let h = HashTable.create () in
....
So u just need to rename module what you are using where you are using it.
The most direct correspondence between your Java example and OCaml is using a functor (what OCaml calls a static function from a module to a module). So suppose you have the following implemented in OCaml:
module type Map = sig
(* For simplicity assume any key and value type is allowed *)
type ('k, 'v) t
val make : unit -> ('k, 'v) t
val put : ('k, 'v) t -> ~key:'k -> ~value:'v -> unit
end
module Hashtable : Map = struct ... end
module HashMap : Map = struct ... end
Then you would write a functor like this:
module MyFunctor(Map : Map) = struct
let my_map =
let map = Map.make () in
Map.put map ~key ~value;
map
end
Then you would instantiate a module using the functor:
module MyModule = MyFunctor(Hashtable)
And voila, changing the implementation is a one-line diff because both the module implementations conform to the Map signature:
module MyModule = MyFunctor(HashMap)

Does "Value Restriction" practically mean that there is no higher order functional programming?

Does "Value Restriction" practically mean that there is no higher order functional programming?
I have a problem that each time I try to do a bit of HOP I get caught by a VR error. Example:
let simple (s:string)= fun rq->1
let oops= simple ""
type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)
let oops2= get ""
and I would like to know whether it is a problem of a prticular implementation of VR or it is a general problem that has no solution in a mutable type-infered language that doesn't include mutation in the type system.
Does “Value Restriction” mean that there is no higher order functional programming?
Absolutely not! The value restriction barely interferes with higher-order functional programming at all. What it does do is restrict some applications of polymorphic functions—not higher-order functions—at top level.
Let's look at your example.
Your problem is that oops and oops2 are both the identity function and have type forall 'a . 'a -> 'a. In other words each is a polymorphic value. But the right-hand side is not a so-called "syntactic value"; it is a function application. (A function application is not allowed to return a polymorphic value because if it were, you could construct a hacky function using mutable references and lists that would subvert the type system; that is, you could write a terminating function type type forall 'a 'b . 'a -> 'b.
Luckily in almost all practical cases, the polymorphic value in question is a function, and you can define it by eta-expanding:
let oops x = simple "" x
This idiom looks like it has some run-time cost, but depending on the inliner and optimizer, that can be got rid of by the compiler—it's just the poor typechecker that is having trouble.
The oops2 example is more troublesome because you have to pack and unpack the value constructor:
let oops2 = F(fun x -> let F f = get "" in f x)
This is quite a but more tedious, but the anonymous function fun x -> ... is a syntactic value, and F is a datatype constructor, and a constructor applied to a syntactic value is also a syntactic value, and Bob's your uncle. The packing and unpacking of F is all going to be compiled into the identity function, so oops2 is going to compile into exactly the same machine code as oops.
Things are even nastier when you want a run-time computation to return a polymorphic value like None or []. As hinted at by Nathan Sanders, you can run afoul of the value restriction with an expression as simple as rev []:
Standard ML of New Jersey v110.67 [built: Sun Oct 19 17:18:14 2008]
- val l = rev [];
stdIn:1.5-1.15 Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
val l = [] : ?.X1 list
-
Nothing higher-order there! And yet the value restriction applies.
In practice the value restriction presents no barrier to the definition and use of higher-order functions; you just eta-expand.
I didn't know the details of the value restriction, so I searched and found this article. Here is the relevant part:
Obviously, we aren't going to write the expression rev [] in a program, so it doesn't particularly matter that it isn't polymorphic. But what if we create a function using a function call? With curried functions, we do this all the time:
- val revlists = map rev;
Here revlists should be polymorphic, but the value restriction messes us up:
- val revlists = map rev;
stdIn:32.1-32.23 Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
val revlists = fn : ?.X1 list list -> ?.X1 list list
Fortunately, there is a simple trick that we can use to make revlists polymorphic. We can replace the definition of revlists with
- val revlists = (fn xs => map rev xs);
val revlists = fn : 'a list list -> 'a list list
and now everything works just fine, since (fn xs => map rev xs) is a syntactic value.
(Equivalently, we could have used the more common fun syntax:
- fun revlists xs = map rev xs;
val revlists = fn : 'a list list -> 'a list list
with the same result.) In the literature, the trick of replacing a function-valued expression e with (fn x => e x) is known as eta expansion. It has been found empirically that eta expansion usually suffices for dealing with the value restriction.
To summarise, it doesn't look like higher-order programming is restricted so much as point-free programming. This might explain some of the trouble I have when translating Haskell code to F#.
Edit: Specifically, here's how to fix your first example:
let simple (s:string)= fun rq->1
let oops= (fun x -> simple "" x) (* eta-expand oops *)
type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)
let oops2= get ""
I haven't figured out the second one yet because the type constructor is getting in the way.
Here is the answer to this question in the context of F#.
To summarize, in F# passing a type argument to a generic (=polymorphic) function is a run-time operation, so it is actually type-safe to generalize (as in, you will not crash at runtime). The behaviour of thusly generalized value can be surprising though.
For this particular example in F#, one can recover generalization with a type annotation and an explicit type parameter:
type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)
let oops2<'T> : 'T SimpleType = get ""

Resources