diff --git a/website/presentation.html b/website/presentation.html index cb4a3e2..99057f4 100644 --- a/website/presentation.html +++ b/website/presentation.html @@ -13,8 +13,11 @@ body { max-width: unset; } line-height:1; } h1 { - font-size: 2.5em; line-height:1; + + --rec-wght: 700; + //--rec-slnt: -0; + --rec-casl: 0.40; } .pull-left > * { float: left; @@ -56,6 +59,13 @@ class: center, middle, title _Lisp Background_ --- +# Background: Fexprs in Lisp + +- 1960s - In earliest Lisps +- 1980 - Argument that they shoudln't be included due to inability for static analysis to fix optimization problems (Kent Pitman, "Special Forms in Lisp") +- 1998 - Adding fexprs to lambda calculus produces a trivial theory (Mitchell Wand, "The Theory of Fexprs is Trivial") +- 2010 - Fexprs are not trivial with internal syntax, make sense with lexical scoping (John Shutt, "Fexprs as the basis of Lisp function application or $ vau: the ultimate abstraction", also earlier papers) +--- (from Wikipedia) .fullWidthImg[] --- @@ -147,29 +157,15 @@ This function takes in code as a data structure, and in R5RS Scheme an "environm # Background: Fexprs - detail - **Normal Lisp** (Scheme, Common Lisp, etc) --- - - Functions - runtime, evaluate parameters once, return value --- - - Macros - expansion time, do not evaluate parameters, return code to be inlined --- - - Special Forms - look like function or macro calls, but do something special (if, lambda, etc) -- - **Kraken** (and Kernel) --- - - Combiners --- - - Applicatives (like normal functions, combiners that evaluate all their parameters once in their dynamic environment) --- - Operatives (combiners that do something unusual with their parameters, do not evaluate them right away) --- - -_Operatives can replace macros and special forms, so combiners replace all_ --- # Background: Fexprs - detail @@ -182,15 +178,14 @@ As we've mentioned, in Scheme _or_ is a macro expanding
(or a b)
to
-(let ((temp a))
- (if temp temp
- b))
+(let ((temp a)) (if temp temp b))
So passing it to a higher-order function doesn't work, you have to wrap it in a function:
> (fold or #f (list #t #f))
Exception: invalid syntax and
-> (fold (lambda (a b) (or a b)) #f (list #t #f))
+
+> (fold (lambda (a b) (or a b)) #f (list #t #f))
#t
---
@@ -207,25 +202,12 @@ true
---
# Background: Fexprs - detail
foldr:
-(let (helper (rec-lambda recurse (f z vs i)
- (if (= i (len (idx vs 0)))
- z
- (lapply f (snoc (recurse f z vs (+ i 1))
- (map (lambda (x) (idx x i)) vs))))))
- (lambda (f z & vs) (helper f z vs 0)))
-
-(lapply reduces the wrap-level of the function by 1, equivalent to quoting the inputs)
----
-# Background: Fexprs - detail
- foldr:
(rec-lambda recurse (f z l)
(if (= nil l)
z
(lapply f (list (car l) (recurse f z (cdr l))))))
(lapply reduces the wrap-level of the function by 1, equivalent to quoting the inputs)
----
-# Background: Fexprs - detail
foldr:
(rec-lambda recurse (f z l)
(if (= nil l)
@@ -233,22 +215,7 @@ true
(f (car l) (recurse f z (cdr l)))))
---
-# Background: Fexprs - detail
-
-All special forms in Kaken are combiners too, and are thus also first class.
-In this case, we can not only pass the raw _if_ around, but we can make an _inverse_if_ which inverts its condition (kinda macro-like) and pass it around.
-> (let ((use_if (lambda (new_if) (new_if true 1 2)))
- (inverse_if (vau de (c t e) (if (not (eval c de))
- (eval t de)
- (eval e de))))
- )
- (list (use_if if) (use_if inverse_if)))
-(1 2)
-
-What were special forms in Lisp are now just built-in combiners in Kraken.
-*if* is not any more special than *+*, and in both cases you can define your own versions that would be indistinguishable, and in both cases they are first-class.
----
-# Motivation and examples
+# Pros and Cons
1. Vau/Combiners unify and make first class functions, macros, and built-in forms in a single simple system
2. They are also much simpler conceptually than macro systems while being hygienic by default
@@ -258,10 +225,8 @@ What were special forms in Lisp are now just built-in combiners in Kraken.
---
# Solution: Partial Eval
-1. Partially evaluate a purely functional version of this language in a nearly-single pass over the entire program
-2. Environment chains consisting of both "real" environments with every contained symbol mapped to a value and "fake" environments that only have placeholder values.
-3. Since the language is purely functional, we know that if a symbol evaluates to a value anywhere, it will always evaluate to that value at runtime, and we can perform inlining and continue partial evaluation.
-4. If the resulting partially-evaluated program only contains static references to a subset of built in combiners and functions (combiners that evaluate their parameters exactly once), the program can be compiled just like it was a normal Scheme program
+1. Evaluate parts of program that only depend on statically-known data ahead of time and insert resulting values into generated code
+2. The parts of the resulting partially-evaluated program that only contains static references to a subset of built in combiners and functions (combiners that evaluate their parameters exactly once) can be compiled just like it was a normal Scheme program
---
# Intuition
@@ -276,14 +241,13 @@ For Kraken, this is exactly what we do, using a specialized form of Partial Eval
So what's the hard part? Why hasn't this been done before?
-- Detour through Lisp history?
+- Previously mentioned history
Determining even what code will be evaluated is difficult.
- Partial Evaluation
- Can't use a binding time analysis pass with offline partial evaluation, which eliminates quite a bit of mainline partial evaluation research
- - Online partial evaluation research generally does not have to deal with the same level of partially/fully dynamic and sometimes explicit environments
- - woo
+ - Online partial evaluation research generally does not have to deal with the same level of partially/fully dynamic first-class explicit environments
---
# Research
@@ -385,32 +349,6 @@ $$
$$
]
----
-# Selected Explanations
-
-.mathSize9[
-- \\(\kprim{0}{eval}\\): evaluates its argument in the given environment.
-- \\(\kprim{0}{vau}\\): creates a new combiner and is analogous to lambda in other languages, but with a "wrap level" of 0, meaning the created combiner does not evaluate its arguments.
-- \\(\kprim{0}{wrap}\\): increments the wrap level of its argument. Specifically, we are "wrapping" a "wrap level" n combiner (possibly "wrap level" 0, created by *vau* to create a "wrap level" n+1 combiner. A wrap level 1 combiner is analogous to regular functions in other languages.
-- \\(\kprim{0}{unwrap}\\): decrements the "wrap level" of the passed combiner, the inverse of *wrap*.
-- \\(\kprim{0}{if}\\): evaluates only its condition and converts to the \\(\kprim{0}{vif}\\) primitive for the next step. It cannot evaluate both branches due to the risk of non-termination.
-- \\(\kprim{0}{vif}\\): evaluates and returns one of the two branches based on if the condition is non-zero.
-- \\(\kprim{0}{int-to-symbol}\\): creates a symbol out of an integer.
-- \\(\kprim{0}{array}\\): returns an array made out of its parameter list.
-]
-
----
-# Less Interesting Prims
-
-.mathSize9[
-- \\(\kcombine{\kprim{0}{type-test?}}{(A)}{E}\\): *array?*, *comb?*, *int?*, and *symbol?*, each return 0 if the single argument is of that type, otherwise they return 1.
-- \\(\kcombine{\kprim{0}{len}}{(A)}{E}\\): returns the length of the single array argument.
-- \\(\kcombine{\kprim{0}{idx}}{(A~n)}{E}\\): returns the nth item array A.
-- \\(\kcombine{\kprim{0}{concat}}{(A~B)}{E}\\): combines both array arguments into a single concatenated array.
-- \\(\kcombine{\kprim{0}{+}}{(A~A)}{E}\\): adds its arguments
-- \\(\kcombine{\kprim{0}{<=}}{(A~A)}{E}\\): returns 0 if its arguments are in increasing order, and 1 otherwise.
-]
-
---
# Base Language Summary
@@ -420,9 +358,6 @@ $$
---
class: center, middle, title
# Slow
---
-
-*so slow......*
---
# Partial Eval: How it works
@@ -483,16 +418,15 @@ To something like
(lambda (f) (f))
+compiled to equivalent of
function(f):
if uses_env(f):
- env_cache = make_env()
+ if not env_cache:
+ env_cache = make_env()
f(env_cache)
else:
f()
-
-Not only do we ask if *f* evaluate its parameters, but also does it take in an environment containing `{ f: <>, ...}`, etc
-
---
# Type-Inference-Based Primitive Inlining
@@ -528,12 +462,10 @@ and then inlined (plus lazy environment allocation)
- Eager lang - extra lambda - eta-conversion in the compiler
---
-# Current Status
-1. No longer *super* slow
-2. Fixed most BigO algo problems (any naive traversal is exponential)
-3. Otherwise, the implementation is slow (pure function, Chicken Scheme not built for it, mostly un-profiled and optimized, etc)
-4. Compiles wraplevel=0 combs to a assert(false), but simple to implement.
-5. Working through bugs - right now figuring out why some things don't partially evaluate as far as they should
+# Outcomes
+1. All macro-like combiner calls are partially evaluated away
+2. No interpreted evaluation calls remain
+3. Optimizations allow reasonable performance
---
# Benchmarks
@@ -614,53 +546,93 @@ $$
---
# Results:
+.fullWidthImg[]
+---
+# Results:
+.fullWidthImg[]
+---
+# Results:
+.fullWidthImg[]
+---
+# Results:
+.fullWidthImg[]
+---
+# Results: (log scale)
+.fullWidthImg[]
+---
+# Results:
+.fullWidthImg[]
+---
+# Results:
+.fullWidthImg[]
+---
+# Current & Future
+- Partial Evaluation / Kraken evolution:
+ - More normal language: purely functional Scheme
+ - More standard Scheme
+ - Environments as association-lists
+ - Fully manipulateable as normal list/pairs
+ - Partial evaluation that supports naturally-written operative combiners, like the running *or* example
+ - Performance: Better Reference Counting, Tail-Recursion Modulo Cons
+- Implement Delimited Continuations as Fexprs
+- Implement Automatic Differentiation as Fexprs
+- Investigate Hardware as Fexprs
+- Allow type systems to be built using Fexprs, like the type-systems-as-macros paper
+---
+class: center, middle, title
+# Backup Slides
+---
+# Background: Fexprs - detail
+
+All special forms in Kaken are combiners too, and are thus also first class.
+In this case, we can not only pass the raw _if_ around, but we can make an _inverse_if_ which inverts its condition (kinda macro-like) and pass it around.
+> (let ((use_if (lambda (new_if) (new_if true 1 2)))
+ (inverse_if (vau de (c t e) (if (not (eval c de))
+ (eval t de)
+ (eval e de))))
+ )
+ (list (use_if if) (use_if inverse_if)))
+(1 2)
+
+What were special forms in Lisp are now just built-in combiners in Kraken.
+*if* is not any more special than *+*, and in both cases you can define your own versions that would be indistinguishable, and in both cases they are first-class.
+---
+# Solution: Partial Eval
+1. Partially evaluate a purely functional version of this language in a nearly-single pass over the entire program
+2. Environment chains consisting of both "real" environments with every contained symbol mapped to a value and "fake" environments that only have placeholder values.
+4. The parts of the resulting partially-evaluated program that only contains static references to a subset of built in combiners and functions (combiners that evaluate their parameters exactly once) can be compiled just like it was a normal Scheme program
+---
+# Selected Explanations
+
+.mathSize9[
+- \\(\kprim{0}{eval}\\): evaluates its argument in the given environment.
+- \\(\kprim{0}{vau}\\): creates a new combiner and is analogous to lambda in other languages, but with a "wrap level" of 0, meaning the created combiner does not evaluate its arguments.
+- \\(\kprim{0}{wrap}\\): increments the wrap level of its argument. Specifically, we are "wrapping" a "wrap level" n combiner (possibly "wrap level" 0, created by *vau* to create a "wrap level" n+1 combiner. A wrap level 1 combiner is analogous to regular functions in other languages.
+- \\(\kprim{0}{unwrap}\\): decrements the "wrap level" of the passed combiner, the inverse of *wrap*.
+- \\(\kprim{0}{if}\\): evaluates only its condition and converts to the \\(\kprim{0}{vif}\\) primitive for the next step. It cannot evaluate both branches due to the risk of non-termination.
+- \\(\kprim{0}{vif}\\): evaluates and returns one of the two branches based on if the condition is non-zero.
+- \\(\kprim{0}{int-to-symbol}\\): creates a symbol out of an integer.
+- \\(\kprim{0}{array}\\): returns an array made out of its parameter list.
+]
+
+---
+# Less Interesting Prims
+
+.mathSize9[
+- \\(\kcombine{\kprim{0}{type-test?}}{(A)}{E}\\): *array?*, *comb?*, *int?*, and *symbol?*, each return 0 if the single argument is of that type, otherwise they return 1.
+- \\(\kcombine{\kprim{0}{len}}{(A)}{E}\\): returns the length of the single array argument.
+- \\(\kcombine{\kprim{0}{idx}}{(A~n)}{E}\\): returns the nth item array A.
+- \\(\kcombine{\kprim{0}{concat}}{(A~B)}{E}\\): combines both array arguments into a single concatenated array.
+- \\(\kcombine{\kprim{0}{+}}{(A~A)}{E}\\): adds its arguments
+- \\(\kcombine{\kprim{0}{<=}}{(A~A)}{E}\\): returns 0 if its arguments are in increasing order, and 1 otherwise.
+]
+---
+# Results:
.pull-left[]
.pull-right[]
----
-# Results:
-
-.pull-left[]
-
-.pull-right[]
-
----
-# Results:
-
-.pull-left[]
-
-.pull-right[]
-
----
-# Results:
-
-.pull-right[]
-
-.pull-left[]
-
----
-# Current Research
-
-- More normal language: purely functional Scheme
-- Environments are just association lists
- - Fully manipulateable as normal list/pairs
-- Partial evaluation that supports naturally-written operative combiners, like the running *or* example
-
----
-# Future
-
-- Partial Evaluation / Kraken evolution:
- - More standard Scheme
- - Environments as association-lists
- - More flexibly-written macro-like fexprs
-- Performance: Better Reference Counting, Tail-Recursion Modulo Cons
-- Implement Delimited Continuations as Fexprs
-- Allow type systems to be built using Fexprs, like the type-systems-as-macros paper (https://www.ccs.neu.edu/home/stchang/pubs/ckg-popl2017.pdf).
----
-class: center, middle, title
-# Backup Slides
---
# Introduction