X-Git-Url: http://lambda.jimpryor.net/git/gitweb.cgi?p=lambda.git;a=blobdiff_plain;f=exercises%2Fassignment5_answers.mdwn;h=9618cd3c2f58a16d429703933fc0431a67d0c4d1;hp=5425ac519c64655736d30afbce232d3dc2dcfa6c;hb=a7f15a6a8100c10bde1cf7b97a02660a3d00b9d9;hpb=155b996c1975e17a34ecdd0e23f2dba9fea8b879 diff --git a/exercises/assignment5_answers.mdwn b/exercises/assignment5_answers.mdwn index 5425ac51..9618cd3c 100644 --- a/exercises/assignment5_answers.mdwn +++ b/exercises/assignment5_answers.mdwn @@ -557,36 +557,44 @@ any type `α`, as long as your function is of type `α -> α` and you have a bas OCAML ANSWERS: - type ('a) sysf_bool = 'a -> 'a -> 'a - let sysf_true : ('a) sysf_bool = fun y n -> y - let sysf_false : ('a) sysf_bool = fun y n -> n + type 'a sysf_bool = 'a -> 'a -> 'a + let sysf_true : 'a sysf_bool = fun y n -> y + let sysf_false : 'a sysf_bool = fun y n -> n - type ('a) sysf_nat = ('a -> 'a) -> 'a -> 'a - let sysf_zero : ('a) sysf_nat = fun s z -> z + type 'a sysf_nat = ('a -> 'a) -> 'a -> 'a + let sysf_zero : 'a sysf_nat = fun s z -> z - let sysf_iszero (n : 'a sysf_nat) : 'b sysf_bool = n (fun _ -> sysf_false) sysf_true + let sysf_iszero (n : ('a sysf_bool) sysf_nat) : 'a sysf_bool = n (fun _ -> sysf_false) sysf_true (* Annoyingly, though, if you just say sysf_iszero sysf_zero, you'll get an answer that isn't fully polymorphic. This is explained in the comments linked below. The way to get a polymorphic result is to say instead `fun next -> sysf_iszero sysf_zero next`. *) let sysf_succ (n : 'a sysf_nat) : 'a sysf_nat = fun s z -> s (n s z) - (* Again, to use this you'll want to call `fun next -> sysf_succ sysf_zero next` *) - - let sysf_pred (n : 'a sysf_nat) : 'a sysf_nat = (* NOT DONE *) - - - + (* Again, to get a polymorphic result you'll want to call `fun next -> sysf_succ sysf_zero next` *) + + And here is how to get `sysf_pred`, using a System-F-style encoding of pairs. (For brevity, I'll leave off the `sysf_` prefixes.) + + type 'a natpair = ('a nat -> 'a nat -> 'a nat) -> 'a nat + let natpair (x : 'a nat) (y : 'a nat) : 'a natpair = fun f -> f x y + let fst x y = x + let snd x y = y + let shift (p : 'a natpair) : 'a natpair = natpair (succ (p fst)) (p fst) + let pred (n : ('a natpair) nat) : 'a nat = n shift (natpair zero zero) snd + + (* As before, to get polymorphic results you need to eta-expand your applications. Witness: + # let one = succ zero;; + val one : '_a nat = + # let one next = succ zero next;; + val one : ('a -> 'a) -> 'a -> 'a = + # let two = succ one;; + val two : '_a nat = + # let two next = succ one next;; + val two : ('a -> 'a) -> 'a -> 'a = + # pred two;; + - : '_a nat = + # fun next -> pred two next;; + - : ('a -> 'a) -> 'a -> 'a = + *) Consider the following list type, specified using OCaml or Haskell datatypes: @@ -778,3 +786,5 @@ and that `bool` is any boolean expression. Then we can try the following: match b with true -> y () | false -> n () that would arguably still be relying on the special evaluation order properties of OCaml's native `match`. You'd be assuming that `n ()` wouldn't be evaluated in the computation that ends up selecting the other branch. Your assumption would be correct, but to avoid making that assumption, you should instead first select the `y` or `n` result, _and then afterwards_ force the result. That's what we do in the above answer. + + Question: don't the rhs of all the match clauses in `match b with true -> y | false -> n` have to have the same type? How can they, when one of them is `blackhole` and the other is `blackhole ()`? The answer has two parts. First is that an expression is allowed to have different types when it occurs several times on the same line: consider `let id x = x in (id 5, id true)`, which evaluates just fine. The second is that `blackhole` will get the type `'a -> 'b`, that is, it can have any functional type at all. So the principle types of `y` and `n` end up being `y : unit -> 'a -> 'b` and `n : unit -> 'c`. (The consequent of `n` isn't constrained to use the same type variable as the consequent of `y`.) OCaml can legitimately infer these to be the same type by unifying the types `'c` and `'a -> 'b`; that is, it instantiates `'c` to the functional type had by `y ()`.