presentation updates

This commit is contained in:
2023-04-24 10:48:24 -04:00
parent c994f1b1af
commit 9ec99a94eb

View File

@@ -13,8 +13,11 @@ body { max-width: unset; }
line-height:1; line-height:1;
} }
h1 { h1 {
font-size: 2.5em;
line-height:1; line-height:1;
--rec-wght: 700;
//--rec-slnt: -0;
--rec-casl: 0.40;
} }
.pull-left > * { .pull-left > * {
float: left; float: left;
@@ -56,6 +59,13 @@ class: center, middle, title
_Lisp Background_ _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) (from Wikipedia)
.fullWidthImg[![](images/lisp_timeline_screenshot.png)] .fullWidthImg[![](images/lisp_timeline_screenshot.png)]
--- ---
@@ -147,29 +157,15 @@ This function takes in code as a data structure, and in R5RS Scheme an "environm
# Background: Fexprs - detail # Background: Fexprs - detail
- **Normal Lisp** (Scheme, Common Lisp, etc) - **Normal Lisp** (Scheme, Common Lisp, etc)
--
- Functions - runtime, evaluate parameters once, return value - Functions - runtime, evaluate parameters once, return value
--
- Macros - expansion time, do not evaluate parameters, return code to be inlined - 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) - Special Forms - look like function or macro calls, but do something special (if, lambda, etc)
-- --
- **Kraken** (and Kernel) - **Kraken** (and Kernel)
--
- Combiners - Combiners
--
- Applicatives (like normal functions, combiners that evaluate all their parameters once in their dynamic environment) - 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 (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 # Background: Fexprs - detail
@@ -182,15 +178,14 @@ As we've mentioned, in Scheme _or_ is a macro expanding
<pre><code class="remark_code">(or a b) <pre><code class="remark_code">(or a b)
</code></pre> </code></pre>
to to
<pre><code class="remark_code">(let ((temp a)) <pre><code class="remark_code">(let ((temp a)) (if temp temp b))
(if temp temp
b))
</code></pre> </code></pre>
So passing it to a higher-order function doesn't work, you have to wrap it in a function: So passing it to a higher-order function doesn't work, you have to wrap it in a function:
<pre><code class="remark_code">> (fold or #f (list #t #f)) <pre><code class="remark_code">> (fold or #f (list #t #f))
Exception: invalid syntax and Exception: invalid syntax and
> (fold (lambda (a b) (or a b)) #f (list #t #f)) </code></pre>
<pre><code class="remark_code">> (fold (lambda (a b) (or a b)) #f (list #t #f))
#t #t
</code></pre> </code></pre>
--- ---
@@ -207,25 +202,12 @@ true
--- ---
# Background: Fexprs - detail # Background: Fexprs - detail
<pre><code class="remark_code"> foldr: <pre><code class="remark_code"> 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)))
</code></pre>
(lapply reduces the wrap-level of the function by 1, equivalent to quoting the inputs)
---
# Background: Fexprs - detail
<pre><code class="remark_code"> foldr:
(rec-lambda recurse (f z l) (rec-lambda recurse (f z l)
(if (= nil l) (if (= nil l)
z z
(lapply f (list (car l) (recurse f z (cdr l)))))) (lapply f (list (car l) (recurse f z (cdr l))))))
</code></pre> </code></pre>
(lapply reduces the wrap-level of the function by 1, equivalent to quoting the inputs) (lapply reduces the wrap-level of the function by 1, equivalent to quoting the inputs)
---
# Background: Fexprs - detail
<pre><code class="remark_code"> foldr: <pre><code class="remark_code"> foldr:
(rec-lambda recurse (f z l) (rec-lambda recurse (f z l)
(if (= nil l) (if (= nil l)
@@ -233,22 +215,7 @@ true
(f (car l) (recurse f z (cdr l))))) (f (car l) (recurse f z (cdr l)))))
</code></pre> </code></pre>
--- ---
# Background: Fexprs - detail # Pros and Cons
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.
<pre><code class="remark_code">> (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)
</code></pre>
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
1. Vau/Combiners unify and make first class functions, macros, and built-in forms in a single simple system 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 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 # Solution: Partial Eval
1. Partially evaluate a purely functional version of this language in a nearly-single pass over the entire program 1. Evaluate parts of program that only depend on statically-known data ahead of time and insert resulting values into generated code
2. Environment chains consisting of both "real" environments with every contained symbol mapped to a value and "fake" environments that only have placeholder values. 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
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
--- ---
# Intuition # 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? 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. Determining even what code will be evaluated is difficult.
- Partial Evaluation - Partial Evaluation
- Can't use a binding time analysis pass with offline partial evaluation, which eliminates quite a bit of mainline partial evaluation research - 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 - Online partial evaluation research generally does not have to deal with the same level of partially/fully dynamic first-class explicit environments
- woo
--- ---
# Research # 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 # Base Language Summary
@@ -420,9 +358,6 @@ $$
--- ---
class: center, middle, title class: center, middle, title
# Slow # Slow
--
*so slow......*
--- ---
# Partial Eval: How it works # Partial Eval: How it works
@@ -483,16 +418,15 @@ To something like
<pre><code class="remark_code">(lambda (f) (f)) <pre><code class="remark_code">(lambda (f) (f))
</code></pre> </code></pre>
compiled to equivalent of
<pre><code class="remark_code">function(f): <pre><code class="remark_code">function(f):
if uses_env(f): if uses_env(f):
if not env_cache:
env_cache = make_env() env_cache = make_env()
f(env_cache) f(env_cache)
else: else:
f() f()
</code></pre> </code></pre>
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 # 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 - Eager lang - extra lambda - eta-conversion in the compiler
--- ---
# Current Status # Outcomes
1. No longer *super* slow 1. All macro-like combiner calls are partially evaluated away
2. Fixed most BigO algo problems (any naive traversal is exponential) 2. No interpreted evaluation calls remain
3. Otherwise, the implementation is slow (pure function, Chicken Scheme not built for it, mostly un-profiled and optimized, etc) 3. Optimizations allow reasonable performance
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
--- ---
# Benchmarks # Benchmarks
@@ -614,53 +546,93 @@ $$
--- ---
# Results: # Results:
.fullWidthImg[![](images/fib_table.csv_.png)]
---
# Results:
.fullWidthImg[![](images/slow_fib_table.csv_.png)]
---
# Results:
.fullWidthImg[![](images/slow_rbtree_table.csv_.png)]
---
# Results:
.fullWidthImg[![](images/rbtree_table.csv_.png)]
---
# Results: (log scale)
.fullWidthImg[![](images/rbtree_table.csv_log.png)]
---
# Results:
.fullWidthImg[![](images/cfold_table.csv_.png)]
---
# Results:
.fullWidthImg[![](images/deriv_table.csv_.png)]
---
# 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.
<pre><code class="remark_code">> (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)
</code></pre>
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[![](images/fib_table.csv_.png)] .pull-left[![](images/fib_table.csv_.png)]
.pull-right[![](images/slow_fib_table.csv_.png)] .pull-right[![](images/slow_fib_table.csv_.png)]
---
# Results:
.pull-left[![](images/slow_rbtree_table.csv_.png)]
.pull-right[![](images/slow_rbtree_table.csv_.png)]
---
# Results:
.pull-left[![](images/rbtree_table.csv_.png)]
.pull-right[![](images/rbtree_table.csv_log.png)]
---
# Results:
.pull-right[![](images/cfold_table.csv_.png)]
.pull-left[![](images/deriv_table.csv_.png)]
---
# 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 # Introduction