I try to better understand the nuances of coding and use the existential type after we turn it into a universal one. In short, it seems to me that using the existential type is much simpler than coding, and I will explain what this means to me below. To better explain this, let's start with two rules from logic.
- ∀xP (x) ⇒ ¬ (∃x.¬P (x))
- ∃xP (x) ⇒ ¬ (∀x.¬P (x))
It follows that
- ∀x. (P (x) ⇒ Q)
- ∀x. (¬P (x) ⩖ Q)
- (∀x.¬P (x)) ⩖ Q
- (¬¬ (∀x.¬P (x))) ⩖ Q
- (¬ (¬ (∀x.¬P (x)))) ⩖ Q
- (¬ (∃xP (x))) ⩖ Q
- (∃xP (x)) ⇒ Q
Hence,
(∃xP (x)) ⇒ Q = ∀x. (P (x) ⇒ Q).
, , . , . , ,
∃x.P(x) = ∀y. (∀x.P(x) ⇒ y) ⇒ y.
, . ,
- ∀y. (∀x.P(x) ⇒ y) ⇒ y
- ∀y. ((∃x.P(x)) ⇒ y) ⇒ y
- ∀y.¬ ((∃x.P(x)) ⇒ y) ⩖ y
- ∀y.¬ (¬ (∃x.P(x)) ⩖ y) ⩖ y
- ∀y. ((∃x.P(x)) ⩕ ¬y) ⩖ y
- ∀y. ((∃x.P(x)) ⩖ y) ⩕ (y v ¬y)
- ∀y. (∃x.P(x)) ⩖ y
- (∃x.P(x)) ⩖ ∀y.y
- ∃x.P()
, , - , , P (x) y y.
, . , OCaml,
type 't mypackage = {
add : 't * 't -> 't;
fromInt : int -> 't;
toString : 't -> string};;
let int_package = {
add = (fun (x,y) -> x+y) ;
fromInt = (fun x->x) ;
toString = (fun x->string_of_int x)};;
let str_package = {
add = (fun (x,y) -> x^y) ;
fromInt = (fun x->string_of_int x) ;
toString = (fun x-> x)};;
let simpleProg fns =
let exp01 = fns.fromInt 1 in
let exp02 = fns.fromInt 2 in
let exp03 = fns.add (exp01,exp02) in
fns.toString exp03
let _ = simpleProg int_package;;
let _ = simpleProg str_package;;
. ,
val int_package : int mypackage
val str_package : string mypackage
val simpleProg : 'a mypackage -> string = <fun>
, simpleProg: (∃x.mypackage(x)) ⇒ . ∀x.mypackage(x) ⇒ , . , int_package str_package , , ,
type 't mypackage = {
add : 't * 't -> 't;
fromInt : int -> 't;
toString : 't -> string};;
let apply (arg : 't mypackage) (f : 't mypackage -> 'u) : 'u =
f arg;;
let int_package_ = {
add = (fun (x,y) -> x+y) ;
fromInt = (fun x->x) ;
toString = (fun x->string_of_int x)};;
let int_package = apply int_package_;;
let str_package_ = {
add = (fun (x,y) -> x^y) ;
fromInt = (fun x->string_of_int x) ;
toString = (fun x-> x)};;
let str_package = apply str_package_;;
let simpleProg_ fns =
let exp01 = fns.fromInt 1 in
let exp02 = fns.fromInt 2 in
let exp03 = fns.add (exp01,exp02) in
fns.toString exp03
(* Notice that we have inverted how we use the existentials here. We give the
existential the program and don't give the program the existential *)
let _ = int_package simpleProg_;;
let _ = str_package simpleProg_;;
(* This flips things *)
let simpleProg fns =
fns simpleProg_;;
let _ = simpleProg int_package;;
let _ = simpleProg str_package;;
,
val int_package : (int mypackage -> '_a) -> '_a = <fun>
val str_package : (string mypackage -> '_a) -> '_a = <fun>
. int string . ,
val simpleProg : (('a mypackage -> string) -> 'b) -> 'b = <fun>
, . ,
val simpleProg : (('a mypackage -> 'b) -> 'b) -> string = <fun>
( . .)
, , , . , , , , , .
, :
- , , , ? , , , , ( mypackage .)
- , ? , , mypackage .
1
camlspotter , , .
(* Creates the type forall t.P(t) *)
type 't mypackage = {
add : 't * 't -> 't;
fromInt : int -> 't;
toString : 't -> string};;
(* Creates the type forall u.(forall t.P(t) -> u) *)
type 'u program_that_takes_open_package_and_returns_u = {
code : 't. 't mypackage -> 'u};;
(* Creates the type forall u.(forall t.P(t) -> u) -> u *)
type 'u applies_program_to_specified_packaged_and_produces_u =
'u program_that_takes_open_package_and_returns_u -> 'u;;
(* Has type P(int) *)
let int_package = {
add = (fun (x,y) -> x+y) ;
fromInt = (fun x->x) ;
toString = (fun x->string_of_int x)};;
(* Has type forall u.(forall.t P(t) -> u) -> u. *)
let (applies_prog_to_int_package :
'u applies_program_to_specified_packaged_and_produces_u) =
(* Has type forall u.(forall.t P(t) -> u) *)
(* Even though we specify an int_package, the program must accept all
packages *)
fun (program:'u program_that_takes_open_package_and_returns_u) ->
program.code int_package;;
(* Has type P(string) *)
let str_package = {
add = (fun (x,y) -> x^y) ;
fromInt = (fun x->string_of_int x) ;
toString = (fun x-> x)};;
(* Has type forall u.(forall.t P(t) -> u) -> u. *)
let (applies_prog_to_str_package :
'u applies_program_to_specified_packaged_and_produces_u) =
(* Has type forall u.(forall.t P(t) -> u) *)
(* Even though we specify an str_package, the program must accept all
packages *)
fun (program:'u program_that_takes_open_package_and_returns_u) ->
program.code str_package_;;
(* The code of a program that takes a package called fns and produces a string *)
let simpleProg = { code = fun fns ->
let exp01 = fns.fromInt 1 in
let exp02 = fns.fromInt 2 in
let exp03 = fns.add (exp01,exp02) in
fns.toString exp03}
(* Apply the program to each of the packages *)
let _ = applies_prog_to_int_package simpleProg;;
let _ = applies_prog_to_str_package simpleProg;;
(* Show that we can, in fact, keep all of the packages in a list *)
let _ = [applies_prog_to_int_package;applies_prog_to_str_package];;
, , , - , , reencoding . , , , . , . , , , , " ".
- , . , , , . , , , , , .
- , , - . , , . , , .
, - . int_package str_package , . , .