├── .gitattributes ├── .gitignore ├── Balls.scm ├── Docs ├── Images │ ├── balls.gif │ ├── many_worlds.jpg │ ├── pong.gif │ └── universe_splitter.jpg ├── amb.md ├── continuations.md ├── debugging.md ├── functional_i.md ├── functional_o.md ├── intro.md ├── lambda.md ├── let.md ├── letstar.md ├── library.md ├── lists.md ├── macros.md ├── mutation.md └── recurse.md ├── FScheme.fs ├── FScheme.fsproj ├── FScheme.sln ├── Main.fs ├── Pong.scm ├── Prelude.scm ├── README.md ├── World.fs └── license.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /Balls.scm: -------------------------------------------------------------------------------- 1 | (define red '(255 0 0)) 2 | (define green '(0 255 0)) 3 | (define blue '(0 0 255)) 4 | 5 | (define ballworld (lambda (fn world) 6 | (let ((ball (car world)) 7 | (color (cadr world))) 8 | (fn (car ball) (cadr ball) ; position x/y 9 | (caddr ball) (cadddr ball) ; delta x/y 10 | (car color) (cadr color) (caddr color))))) ; red/green/blue 11 | 12 | (define bounce (lambda (i di) 13 | (if (or (and (> di 0) (> i 31)) ; heading toward right/bottom and off edge 14 | (and (> 0 di) (> 0 i))) ; or heading toward left/top and off edge 15 | (- di) di))) ; reverse direction 16 | 17 | (define ballmove (lambda (ball) 18 | (ballworld (lambda (x y dx dy r g b) '(( 19 | ,(+ x dx) ,(+ y dy) ; increment position 20 | ,(bounce x dx) ,(bounce y dy)) ; bounce off edges 21 | (,r ,g ,b))) ball))) ; color unchanged 22 | 23 | (define balldraw (lambda (ball) 24 | (ballworld (lambda (x y dx dy r g b) 25 | '((,x ,y) (,r ,g ,b))) ball))) ; simply draw as single pixel at x/y position 26 | 27 | (define init (lambda (_) 28 | '(((16 0 2 1) ,red) 29 | ((6 9 1 -1) ,green) 30 | ((16 16 -1 2) ,blue)))) 31 | (define tick (lambda (input) (map ballmove (car input)))) 32 | (define draw (lambda (world) (map balldraw (car world)))) -------------------------------------------------------------------------------- /Docs/Images/balls.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AshleyF/FScheme/c4b3fd43d8b32f046b0906c1111ff3800925ddf9/Docs/Images/balls.gif -------------------------------------------------------------------------------- /Docs/Images/many_worlds.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AshleyF/FScheme/c4b3fd43d8b32f046b0906c1111ff3800925ddf9/Docs/Images/many_worlds.jpg -------------------------------------------------------------------------------- /Docs/Images/pong.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AshleyF/FScheme/c4b3fd43d8b32f046b0906c1111ff3800925ddf9/Docs/Images/pong.gif -------------------------------------------------------------------------------- /Docs/Images/universe_splitter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AshleyF/FScheme/c4b3fd43d8b32f046b0906c1111ff3800925ddf9/Docs/Images/universe_splitter.jpg -------------------------------------------------------------------------------- /Docs/amb.md: -------------------------------------------------------------------------------- 1 | # Playing Dice with the Universe 2 | 3 | This is the eleventh in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * Playing Dice with the Universe 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | We’re now taking the first small step into the world of nondeterministic logic programming (chapter 16 of [Bill Hails’ book](http://billhails.net/Book/)). Hopefully you enjoyed the [last post about continuation passing](continuations.md) and found the idea to be ripe with potential power; indeed so powerful that we’re going to use it now to bifurcate the universe! No really, we are… 21 | 22 | ![Universe Splitter](Images/universe_splitter.jpg) 23 | ![Many Worlds](Images/many_worlds.jpg) 24 | 25 | There is a funny [app for the iPhone](http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=329233299&mt=8) that illustrates the [Many-worlds Interpretation](http://en.wikipedia.org/wiki/Many-worlds_interpretation) of quantum mechanics. It’s really just a super-glorified coin toss app in which every time you’re ambivalent about a decision, you simply choose both; split into parallel universes and carry on with your life(s)? 26 | 27 | # What Might Have Been 28 | 29 | We're not going to go as far as to spin up multiple threads to simulate parallel universes (although that might be fun to try!). The big idea is to use continuations to do chronological backtracking. We’re going to add to FScheme an ambivalent operator (`amb` for short). Here’s an example of how it will work: 30 | 31 | ``` scheme 32 | > (amb "take a chance" "play it safe") 33 | take a chance 34 | ``` 35 | 36 | See what we did there? We had mixed feelings about what to do so we told the interpreter to do both. It chose for us. If we don’t like that resulting universe, we can go back in time and try the other option by issuing a special backtracking command (`?`): 37 | 38 | ``` scheme 39 | > ? 40 | play it safe 41 | ``` 42 | 43 | Once we’ve exhausted our options the interpreter tells us: 44 | 45 | ``` scheme 46 | > ? 47 | No more solutions. 48 | ``` 49 | 50 | The options are chosen in a deterministic way, but simply looking at the `(amb …)` expression alone is not enough to determine its value. 51 | 52 | This works with more complicated sets of options; any expressions we like: 53 | 54 | ``` scheme 55 | > (amb (* 3 5) (- 50 8)) 56 | 15 57 | > ? 58 | 42 59 | > ? 60 | No more solutions. 61 | ``` 62 | 63 | It also works embedded within other expressions: 64 | 65 | ``` scheme 66 | > (* 2 (amb 3 4)) 67 | 6 68 | > ? 69 | 8 70 | > ? 71 | No more solutions. 72 | ``` 73 | 74 | And you can have multiple ambivalent values within expressions: 75 | 76 | ``` scheme 77 | > (list (amb 1 2) (amb 'a 'b)) 78 | (1 a) 79 | > ? 80 | (1 b) 81 | > ? 82 | (2 a) 83 | > ? 84 | (2 b) 85 | > ? 86 | No more solutions. 87 | ``` 88 | 89 | ## Behind the Magic 90 | 91 | Below is all there is to the magic. The heart of the idea is to realize (as we [discovered in the last post](continuations.md)) that continuations represent nice bundles of complete execution state and can be stored away to be resumed at will. Every time we’re forced to make an arbitrary choice, we store away a snapshot of the universe so we can come back and change our minds later if we wish. 92 | 93 | ``` fsharp 94 | let mutable backtrack = [] // ambivalent back stack 95 | and Ambivalent cont env args = 96 | match args with 97 | | choice :: t –> 98 | backtrack <- (fun () -> Ambivalent cont env t) :: backtrack 99 | eval cont env choice 100 | | [] –> 101 | match backtrack with 102 | | back :: t -> backtrack <- t; back () 103 | | [] -> printfn "No more solutions."; Dummy("Unsolvable") |> cont 104 | ``` 105 | 106 | Whenever we’re asked to produce an ambivalent result, we first take the current continuation and push a recursive ‘amb’ call onto a backtrack stack and then yield the first choice. This backtrack stack is the key to it all. It represents all the points in the execution at which we made an arbitrary choice; all the points at which we split the universe. The continuation, along with the remaining list of choices, is enough information to later come back and choose a different parallel universe any time we like! 107 | 108 | If ever we’re asked to yield a choice from an empty list of options then we abandon the current continuation (just don’t call it) and force a backtrack. Additionally, we allow forcing a backtrack from the REPL by typing “?”: 109 | 110 | ``` fsharp 111 | let rep env = 112 | let eval' = function 113 | | Symbol("?") –> 114 | match backtrack with 115 | | h :: t -> backtrack <- t; h () 116 | | [] -> printfn "No current problem."; Dummy("No problem") 117 | | e -> eval id env e 118 | List.ofSeq >> parse >> List.head >> eval' >> print 119 | ``` 120 | 121 | [Note: In the book, a more complicated (though perhaps more elegant) approach is used in which an additional backtrack continuation is threaded through the whole works. I chose this simpler, less invasive, but impure method of maintaining a mutable stack.] 122 | 123 | ## Truly Declarative Programming 124 | 125 | There is a lot of recent talk about declarative languages; letting you declare what to do and allowing the runtime to decide exactly how. Functional programming is generally more declarative than imperative programming but, for the extreme end of the spectrum, a language like Prolog is most fascinating. Logic programming is a whole paradigm in which I’ve been interested for quite some time but I must admit I’ve only dabbled. Just last week in fact, I borrowed The Art of Prolog from a coworker’s bookshelf and am working my way through. It will take some time before I’m ready to blog about it, but here I want to whet your appetite with just a small peek. 126 | 127 | It might look a little tricky, but remember that evaluating ‘amb’ against an empty set of choices forces a backtrack. So we can define a function that backtracks when a particular condition isn’t met: 128 | 129 | ``` scheme 130 | (define require (lambda (e) (if e e (amb)))) 131 | ``` 132 | 133 | Then watch this: 134 | 135 | ``` scheme 136 | > (define odd? (lambda (x) (% x 2))) 137 | > (define even? (lambda (x) (not? (odd? x)))) 138 | > (let ((x (amb 1 2 3 4 5 6 7 8 9))) (begin (require (even? x)) x)) 139 | 2 140 | > ? 141 | 4 142 | > ? 143 | 6 144 | > ? 145 | 8 146 | > ? 147 | No more solutions. 148 | ``` 149 | 150 | We yield only choices meeting the required condition. In general, we can think of this style of declarative programming as: 151 | 152 | 1. Define the range of inputs 153 | 2. Define what a “solution” looks like 154 | 3. Let the machine find solutions for you 155 | 156 | Let’s try it with a little more complicated problem; finding Pythagorean Triples. These are sets of three positive integers (a, b and c) satisfying the equation a^2 + b^2 = c^2. For example 3^2 + 4^2 = 5^2. Here’s the solution (or rather, what solutions in general look like and the definition of input ranges – we won’t search all positive integers): 157 | 158 | ``` scheme 159 | (define square (lambda (x) (* x x))) 160 | 161 | (define pythagorean-triples (lambda () (let ( 162 | (a (amb 1 2 3 4 5 6 7 8 9 10)) 163 | (b (amb 1 2 3 4 5 6 7 8 9 10)) 164 | (c (amb 1 2 3 4 5 6 7 8 9 10))) 165 | (begin 166 | (require (= (+ (square a) (square b)) (square c))) 167 | '((a ,a) (b ,b) (c ,c)))))) 168 | ``` 169 | 170 | Really, look we didn’t say anything other than to declare the ranges for `a`, `b` and `c` and to `require` they satisfy the Pythagorean theorem. That’s all we have to do. The interpreter does the rest: 171 | 172 | ``` scheme 173 | > (pythagorean-triples) 174 | ((a 3) (b 4) (c 5)) 175 | > ? 176 | ((a 4) (b 3) (c 5)) 177 | > ? 178 | ((a 6) (b 8) (c 10)) 179 | > ? 180 | ((a 8) (b 6) (c 10)) 181 | > ? 182 | ``` 183 | 184 | Pretty sweet! 185 | 186 | [Note: In the book Bill Hails goes further and rewrites the imperative `define` and `set!` to undo their state changes upon backtracking. We haven't done that, so beware the pitfalls...] 187 | 188 | ## Next: [Functional I/O (or at least "O")](functional_o.md) 189 | -------------------------------------------------------------------------------- /Docs/continuations.md: -------------------------------------------------------------------------------- 1 | # Turning Your Brain Inside Out with Continuations 2 | 3 | This is the tenth in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * Turning Your Brain Inside Out With Continuations 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | We’re into one of the most magical chapters in [Bill Hails’ book](http://billhails.net/Book/). We’re about to add a very strange and dangerous feature to the interpreter: `call/cc` (“call with current continuation”). To appreciate the beauty of it, we’ll first look at continuation passing style (CPS) in general, what it’s good for, then how `call/cc` works, some of the implementation details, and finally how it’s used in Scheme. 21 | 22 | ## Continuation Passing Style 23 | 24 | Wes Dyer has a [nice post on CPS](http://blogs.msdn.com/wesdyer/archive/2007/12/22/continuation-passing-style.aspx). The concept is closely related to tail call optimization (TCO) which we [just talked about in the last post](library.md) (if you haven’t read it, you should and then continue here – ha!). The idea of CPS is that instead of functions returning values to their callers, they’re passed a continuation function to be called with what would have been the return value. That is, functions never return! No really, they never ever return! This is shocking enough that in Bill’s book he leaves a blank page “to allow time for reflection on the enormity of the previous statement.” It is indeed an astounding way to program! Once you’re comfortable with the idea of tail recursion though, this idea should at least not seem totally infeasible. 25 | 26 | Our running example is the standard factorial function: 27 | 28 | ``` fsharp 29 | let rec fac n = if n = 0 then 1 else n * fac (n - 1) 30 | ``` 31 | 32 | However `fac 100000000` causes a `StackOverflowException`. To solve this we used an accumulation parameter (`a`) and made it properly tail recursive: 33 | 34 | ``` fsharp 35 | let rec fac n a = if n = 0 then a else fac (n - 1) (n * a) 36 | ``` 37 | 38 | CPS is very similar but instead of passing forward an accumulated result, we pass a closure capturing the current result. This closure becomes the new continuation; expecting to be passed the next result which may recurse again with a new closure, etc. 39 | 40 | ``` fsharp 41 | let rec fac n c = if n = 0 then c 1 else fac (n - 1) (fun n' -> c (n * n')) 42 | ``` 43 | 44 | These closures of closures of closures are finally invoked when the base case is hit. We can try the above by passing in an initial continuation that simply prints the result: 45 | 46 | ``` fsharp 47 | fac 100000000 (printfn "%i") 48 | ``` 49 | 50 | It’s interesting that this is no longer blows the stack but instead it causes an OutOfMemoryException! The call is certainly tail recursive, but the continuation itself is accreting continuations of continuations of continuations. For CPS to be viable, we need to use a form of TCO that doesn’t accrete state. The following works: 51 | 52 | ``` fsharp 53 | let rec fac n a c = if n = 0 then c a else fac (n - 1) (n * a) c 54 | ``` 55 | 56 | This is really a mechanical transformation of the original TCO version. 57 | 58 | Side note: Luckily for us, F# supports TCO. In other languages it may be necessary to implement something like the trampoline in Bill's book. The idea is to not call continuations directly, but instead to always call a central function with a thunk that when invoked will call the continuation. This keeps the stack from growing and actually has a side benefit of centralizing all continuation dispatch which allows for some cool tricks. Maybe I’ll write another post on the trampoline later. 59 | 60 | ## Transforming To CPS 61 | 62 | Transforming functions to CPS is something like folding your code inside out and it feels like the same is happening to your brain. Monads can help turn it back to normal but that’s a separate topic. If you’re interested though, notice that the [Monadic Coconuts post from a while back](http://blogs.msdn.com/ashleyf/archive/2009/12/14/monadic-piles-of-coconuts.aspx) was essentially using continuations and F#’s monadic computation expression syntax to make the ugly CPS look pretty again. Continuations are at the heart of monads. 63 | 64 | Let’s practice some CPS conversions. A function returning a constant value is simplest of all: 65 | 66 | ``` fsharp 67 | let greeting () = "hello" 68 | ``` 69 | 70 | Becomes: 71 | 72 | ``` fsharp 73 | let greeting () c = c "hello" 74 | ``` 75 | 76 | Passing a continuation (`c`) and call it with what would have been the return value. A function performing a simple computation is no more difficult: 77 | 78 | ``` fsharp 79 | let add a b = a + b 80 | ``` 81 | 82 | Becomes: 83 | 84 | ``` fsharp 85 | let add a b c = c (a + b) 86 | ``` 87 | 88 | Again just passing in a continuation which is called with the result. What might not be obvious is how to get the result and do something with it; maybe print it. Just as we did with the factorial example, we pass in a print function as the initial continuation and it’s called with the return value: 89 | 90 | ``` fsharp 91 | > add 2 3 (printfn "%i") 92 | 5 93 | ``` 94 | 95 | Where CPS starts to get complicated is with nested function calls. Take this pair of non-CPS functions: 96 | 97 | ``` fsharp 98 | let double x = 2 * x 99 | let square x = x * x 100 | ``` 101 | 102 | And call them from a third function: 103 | 104 | ``` fsharp 105 | let sqrdbl x = double (square x) 106 | ``` 107 | 108 | Calling `square` and passing the result to `double`. 109 | 110 | Side note: This could have been written using composition as `let sqrdbl = double >> square` You'll notice that when we convert to CPS we lose this compositionality. Using monads gives CPS compositionality again. I just found that interesting... 111 | 112 | Converting `double` and `square` to CPS is simple enough: 113 | 114 | ``` fsharp 115 | let double x c = c (2 * x) 116 | let square x c = c (x * x) 117 | ``` 118 | 119 | But converting `sqrdbl` involves turning things inside out: 120 | 121 | ``` fsharp 122 | let sqrdbl x c = square x (fun y -> double y c) 123 | ``` 124 | 125 | First, `sqrdbl` now takes a continuation. It passes `x` to square as usual but can’t just pass the continuation (`c`) along to square. Instead we have to make a new closure (capturing ‘`c`’) that will be called with the square of `x`. This new continuation will then forward that on to `double` and, since `double` is the last in the sequence, pass on the original continuation which will be called with the ultimate result. 126 | 127 | ``` fsharp 128 | > sqrdbl 7 (printfn "%i") 129 | 98 130 | ``` 131 | 132 | As you can imagine, deeply nested function calls can result in very confusing CPS transformations that tie your brain in knots. Believe it or not, we intend to transform the whole guts of the FScheme interpreter into CPS. Fun! 133 | 134 | ## What is CPS Good For Anyway? 135 | 136 | Before we transform the whole interpreter and introduce `call/cc,` let’s see if we can get a glimpse of what CPS can do for us. Here’s a simple example that hopefully shows some of the power. First we define a silly little set of functions named ‘`a`’, ‘`b`’, ‘`c`’ and ‘`d`’ that print their names and call a passed in continuation: 137 | 138 | ``` fsharp 139 | let a cont = printf "A, "; cont () 140 | let b cont = printf "B, "; cont () 141 | let c cont = printf "C, "; cont () 142 | let d cont = printf "D, "; cont () 143 | ``` 144 | 145 | Calling a sequence of these in CPS would look like: 146 | 147 | ``` fsharp 148 | let demo () = a (fun _ -> b (fun _ -> c (fun _ -> d (fun _ -> printfn "Done")))) 149 | ``` 150 | 151 | We can try it out in the interactive: 152 | 153 | ``` fsharp 154 | > demo ();; 155 | A, B, C, D, Done 156 | ``` 157 | 158 | Nothing interesting yet; just calls each in turn, printing “A, B, C, D, Done”. What may not be apparent is that what we’ve done is really give up control over the flow of the sequence. For example, if we redefine ‘`b`’ to no longer call its continuation: 159 | 160 | ``` fsharp 161 | let b cont = printf "B (terminate!)" 162 | ``` 163 | 164 | The same demo function now produces: 165 | 166 | ``` fsharp 167 | > demo ();; 168 | A, B (terminate!) 169 | ``` 170 | 171 | This is something like throwing an exception and bailing out. In other words, we could use CPS to build the mechanics of structured exception handling if we needed. For example, we could define a continuation-based exception handling mechanism in which you always pass in two continuations; one to be called upon success and another (representing the ‘catch’ block) to be called upon error. 172 | 173 | ## The True Essence 174 | 175 | A very strange thing to realize about continuations is that they are essentially closures that represent all that is needed to complete the computation. That is, they are self-contained units that represent the rest of the computation; a very strange idea. You’re free to store them away and use them elsewhere. I hope you’re getting the same sense of eureka that I got when first realizing what this really means! Say we redefine ‘b’ to not just terminate, but to store away the current continuation and then fall through without continuing: 176 | 177 | ``` fsharp 178 | let mutable resume = (fun () -> printfn "Replace me!") 179 | let b cont = printf "B (suspend!)"; resume <- cont 180 | ``` 181 | 182 | The result is, as expected: 183 | 184 | ``` fsharp 185 | > demo ();; 186 | A, B (suspend!) 187 | ``` 188 | 189 | But now think about what `resume` represents. It contains the rest of the computation. It could easily represent some arbitrarily complicated mess as long as the mess is expressed in CPS. Now we can: 190 | 191 | ``` fsharp 192 | > resume ();; 193 | C, D, Done 194 | ``` 195 | 196 | Picking up where we left off. In fact we’re free to resume as many times as we like and, assuming pure (mutation free) code, get the same result each time. 197 | 198 | ## One Crazy Idea (of Many) 199 | 200 | You could imagine doing some crazy things with this. For example, we could suspend the sequence and kick off an asynchronous call to some web service and then resume upon callback. Normally, writing async code is a kludge with begin/end async calls all over and having to separate code into callback functions. Notice that we could do this automagically without changing the original definition of the `demo` function at all. The guy writing ‘demo’ could think of his code as a synchronous sequence and we could handle the async mess under the covers. This is actually very close to how async workflows work in F#. 201 | 202 | Plenty of other crazy ideas come to mind. What if we pass a pair of producer/consumer or generator/iterator continuations back and forth, creating coroutines? Or what if you could serialize continuations and ship them off to a different machine on which to resume? So many bizarre things are possible. 203 | 204 | ## Converting to a CPS-based Interpreter 205 | 206 | Honestly, transforming the interpreter to CPS was a bit of a pain. I’m not going to go into all the details but you can check the diffs. 207 | 208 | First, a continuation is just an Expression -> Expression function. We want to change Function and Special to take a continuation argument and add the idea of an expression resolving to a continuation: 209 | 210 | ``` fsharp 211 | type Expression = 212 | | Number of BigInteger 213 | | String of string 214 | | Symbol of string 215 | | List of Expression list 216 | | Function of (Continuation -> Expression list -> Expression) 217 | | Special of (Continuation -> Environment -> Expression list -> Expression) 218 | | Current of Continuation 219 | | Dummy of string 220 | and Continuation = Expression -> Expression 221 | ``` 222 | 223 | For many parts of the interpreter the transformation is pretty straight forward. For example, `cons`, `car` and `cdr` simply take a continuation and forward their return value to it (note the addition of `cont`): 224 | 225 | ``` fsharp 226 | and Cons cont = function [h; List(t)] -> (List(h :: t)) |> cont | … 227 | and Car cont = function [List(h :: _)] -> h |> cont | … 228 | and Cdr cont = function [List(_ :: t)] -> List(t) |> cont | … 229 | ``` 230 | 231 | The more complicated transformations were places where we were using maps and folds. Perhaps I should have written a general CPS version of List.map, List.fold, etc. As just one example, ‘Begin’ was a pretty simple fold: 232 | 233 | ``` fsharp 234 | and Begin env = List.fold (fun _ e -> eval env e) (Dummy("Empty 'begin'")) 235 | ``` 236 | 237 | And ended up turning into the more complicated version below; explicitly handling the recursion, creating a new continuation and handing off to the passed in continuation in the base case: 238 | 239 | ``` fsharp 240 | and Begin cont env = 241 | let rec foldeval last = function 242 | | h :: t -> eval (fun x -> foldeval x t) env h 243 | | [] -> last |> cont 244 | foldeval (Dummy("Empty 'begin'")) 245 | ``` 246 | 247 | We had to do something similar everywhere we were doing maps and folds. I don’t want to bore you with the mostly mechanical details. You can check the diffs or can imagine how it’s possible from the transformation samples we did earlier. We painstakingly went through and converted absolutely everything in the interpreter to CPS. 248 | 249 | Side note: We also added some math functions, added ‘display’ for printing expressions as a side effect, and added escaped whitespace characters to string literal parsing, added a ‘while’ loop macro to the prelude, etc. But I want to stick to the topic at hand. 250 | 251 | ## Two Worlds Intersect 252 | 253 | Like quantum entanglement or something, we're about to allow the language and the internals of the language implementation to intertwine. 254 | 255 | We’ve gone to all of this work rewriting the interpreter using CPS everywhere, what difference does it make from the Scheme author’s perspective? The underlying mechanics have changed out from under them but the behavior of running Scheme programs hasn’t changed at all. 256 | 257 | As far as the interpreter, we could use the fact that we’re internally using CPS to now build exception handling for example. We could add ‘try’ and ‘catch’ keywords to the interpreter and the implementation would be simple given our new architecture. We could imagine adding an async-workflows-like feature or threading or coroutines of any number of other new features, now made easier by having converted everything to CPS. All of these would be features added as new primitives to the interpreter itself. 258 | 259 | However, remember all the talk about how the [soul of Scheme is as a programmable programming language](macros.md)? The big idea is to imagine exposing the CPS mechanics to the running programs. Programs execute in one world while the interpreter is implemented in another. We want to add a single small hook joining the two worlds in a controlled way. It’s a very dangerous feature and could easily be abused. The point though is that Scheme is not meant to be the direct implementation language for a problem domain. Instead, it’s expected that a domain specific language will be build atop this language kernel. This shows the minimalistic nature of Scheme. Rather than build in various controlled features such as structured exception handling or yielding or threading, we build in the single essence of all of them and leave it up to the DSL author to implement them in a library. 260 | 261 | ## Call with Current Continuation 262 | 263 | The new primitive we will add is ‘call/cc’. It takes a lambda as an argument and passes to it the current continuation from the interpreter’s world. The implications of this are somewhat mind blowing! First, here’s the implementation: 264 | 265 | ``` fsharp 266 | and CallCC cont env = function 267 | | [callee] -> eval (function Special(fn) -> fn cont env [Current(cont)] 268 | | m -> malformed "call/cc" m) env callee 269 | | m -> malformed "call/cc" (List(m)) 270 | ``` 271 | 272 | Pretty innocent looking. It just takes a continuation (as does everything now) and expects a `callee` which should be a lambda expecting one argument. It then bundles the continuation from the interpreter’s world up as a Current expression and passes it to the lambda in the Scheme world. We’ve just handed over enormous power! 273 | 274 | Let’s try just a few ideas to get the wheels spinning. We can use it to abort computations by just defining a function that when called will return to the top-level: 275 | 276 | ``` scheme 277 | ; simple continuation to top-level 278 | (define escape nil) 279 | (call/cc (lambda (c) (set! escape c))) 280 | ``` 281 | 282 | This stores away the current (top-level) continuation as ‘escape’ which can now be called from some arbitrarily deeply nested expression, causing whatever is happening to abort (regular continuation is never called). To make it a bit more useful we can define an error function that prints a message and then escapes: 283 | 284 | ``` scheme 285 | ; error mechanism - print message and break out to top-level 286 | (define error (lambda (msg) (begin (display msg) (escape nil)))) 287 | ``` 288 | 289 | We can now use this to abort sequences: 290 | 291 | ``` scheme 292 | > (begin (display "A, ") (display "B, ") (error "Opps!") (display "C")) 293 | A, B, Opps! 294 | ``` 295 | 296 | Or we can do the pause/resume trick from earlier: 297 | 298 | ``` scheme 299 | (define resume nil) 300 | (define pause (lambda () (call/cc (lambda (k) (begin (set! resume k) (escape nil)))))) 301 | (begin (pause) (display 'A) (pause) (display 'B) (pause) (display 'C)) 302 | ``` 303 | 304 | Then: 305 | 306 | ``` scheme 307 | > (resume nil) 308 | A 309 | > (resume nil) 310 | B 311 | > (resume nil) 312 | C 313 | ``` 314 | 315 | Pretty neat! 316 | 317 | In fact, here’s a really cool idea: We can simulate threading by storing away multiple paused continuations and then resuming them round robin. First: 318 | 319 | ``` scheme 320 | (define t0 nil) 321 | (define t1 nil) 322 | (define pause (macro (t) '(call/cc (lambda (k) (begin (set! ,t (lambda () (k nil))) (escape nil)))))) 323 | ``` 324 | 325 | Wrapping in a macro lets us pass in a thread name: 326 | 327 | ``` scheme 328 | (begin (pause t0) (display 'A) 329 | (pause t0) (display 'B) 330 | (pause t0) (display 'C)) 331 | (begin (pause t1) (display 1) 332 | (pause t1) (display 2) 333 | (pause t1) (display 3)) 334 | ``` 335 | 336 | Now we can interleave the sequences very easily and get “A1B2C3”: 337 | 338 | ``` scheme 339 | (t0)(t1)(t0)(t1)(t0)(t1) 340 | ``` 341 | 342 | Each sequence is defined independently of the other and the thread scheduling is defined separately (we could build further infrastructure to maintain a list of threads and have a proper scheduler; all in Scheme). We’re well on our way to building a sort of [green threads](http://en.wikipedia.org/wiki/Green_threads) library. More importantly, we now have the power to build any kind of control flow we like into the language. Pretty potent feature! 343 | 344 | ## Next: [Playing Dice with the Universe](amb.md) 345 | -------------------------------------------------------------------------------- /Docs/debugging.md: -------------------------------------------------------------------------------- 1 | # Functional I/O (Historical Debugging) 2 | 3 | This is the last in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * Historical Debugging 19 | 20 | [Historical Debugging introduced in VS2010](http://channel9.msdn.com/posts/VisualStudio/Historical-Debugger-and-Test-Impact-Analysis-in-Visual-Studio-Team-System-2010/) is quite the engineering feat! We can actually pull off something similar in our interpreter with amazing ease. Because of the pure nature of our functional I/O system (last [two](functional_o.md) [posts](functional_i.md)) and the already centrally managed world state and input it’s very straight forward to implement. 21 | 22 | ## Adding a Historian 23 | 24 | First we need to keep a history of the world. We’ll use a double linked list so we can easily walk backward and forward in time: 25 | 26 | ``` fsharp 27 | let mutable history = new LinkedList() 28 | let mutable current = new LinkedListNode(Symbol("Dummy world")) 29 | ``` 30 | 31 | On each `tick` we’ll append to the history. We’ll also allow stopping and restarting of the passage of time (with a ‘running’ flag). We’re blindly tracking each entire world state. You can imagine a more memory-efficient implementation that only tracks input history and replays forward from the initial state or that maintains only “key frames” of computed world state and partially replays forward as needed. That would be interesting to build but let’s start with the simplest thing that could possibly work: 32 | 33 | ``` fsharp 34 | let form () = 35 | let f = new Form(Text = "Canvas", Width = w * s + 16 - 1, Height = h * s + 38 - 1, Visible = true) 36 | let running = ref false 37 | let time slice = 38 | running := false 39 | if slice <> null then 40 | current <- slice 41 | paint (); f.Refresh() 42 | let debug = function 43 | | Keys.S -> running := false 44 | | Keys.G -> running := true 45 | | Keys.Left -> time current.Previous 46 | | Keys.Right -> time current.Next 47 | | Keys.W -> print current.Value |> printfn "World: %s" 48 | | _ –> () 49 | let t = new Thread(new ThreadStart(fun () –> 50 | while true do 51 | if running.Value then 52 | current <- history.AddAfter(current, eval' "tick") 53 | paint (); f.Refresh() 54 | Thread.Sleep(33))) 55 | t.Start() 56 | f.Paint.Add(fun a -> lock bmp (fun () -> a.Graphics.DrawImage(bmp, 0, 0))) 57 | f.MouseMove.Add(fun a -> mouseX := a.X / s; mouseY := a.Y / s) 58 | f.Closing.Add(fun _ -> t.Abort()) 59 | f.KeyDown.Add(fun a -> debug a.KeyCode) 60 | current <- history.AddFirst(eval' "init") 61 | running := true 62 | f 63 | ``` 64 | 65 | The system behaves just as it always has; ticking off the progress of time upon being (run). But now we can press ‘S’top or ‘G’o to suspend things, we can press/hold left and right arrow keys to move back and forth through history, and we can print the current world state with ‘W’. 66 | 67 | Now you can cheat at the [“Pong” game from last post](functional_i.md). If you miss the ball, just back up and try again; rewriting history! 68 | 69 | Also, you might notice bugs in the implementation. For example the ball appears to go off the screen when it bounces. You can flip through the history to just before and after it happens and look at the world state. Sure enough, it goes one pixel off before reversing directions… great. Jump on GitHub and fix it! :-) 70 | 71 | The beauty of debugging this style of functional I/O code is that any bug is sure to be found in the transition from one state to the next. The 'tick' function, given a particular state, is returning the wrong thing. Just pin down which transition is broken and fix the logic or assumption in the 'tick'. If you're into TDD then first add a unit test passing the particular state in and asserting the expected result. Pure functions are so very much easier to test and debug. 72 | 73 | [Side note: I want to plug [another paper worth reading](http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=3041C7F2E6F57CAE760DCAA30CFD96FC?doi=10.1.1.49.695&rep=rep1&type=pdf) if you're interested in functional I/O systems.] 74 | -------------------------------------------------------------------------------- /Docs/functional_i.md: -------------------------------------------------------------------------------- 1 | # Functional I/O (including “I” this time) 2 | 3 | This is the thirteen in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * Functional I/O (including "I" this time) 18 | * [Historical Debugging](debugging.md) 19 | 20 | Now to add input to our Functional I/O system; following up on the [previous post](functional_o.md). We already had bouncing balls so how ‘bout we add a mouse-controlled paddle and make a primitive “Pong”? 21 | 22 | ## Mouse Input 23 | 24 | To keep it simple, we’re just going to add mouse position as input; including the current mouse x/y in the world state passed in on each iteration. We could add any other input we like this way: network responses, key events, etc. One issue we’re avoiding is handling events such as key/button down/up without missing them due to our `tick` sample rate - instead having to queue them and play catch up (losing time instead). Here we keep it pretty ridiculously easy (notice the addition of `f.MouseMove.Add(...)`: 25 | 26 | ``` fsharp 27 | let mouseX = ref 0 28 | let mouseY = ref 0 29 | let events () = List([Number(bigint mouseX.Value); Number(bigint mouseY.Value)]) 30 | 31 | let eval' name = eval id environment (List([Symbol(name); List([Symbol("quote"); List([world.Value; events ()])])])) 32 | 33 | let form () = 34 | let f = new Form(Text = "Canvas", Width = w * s + 16 - 1, Height = h * s + 38 - 1, Visible = true) 35 | f.Paint.Add(fun a -> a.Graphics.DrawImage(paint (), 0, 0)) 36 | f.MouseMove.Add(fun a -> mouseX := a.X / s; mouseY := a.Y / s) 37 | let loop () = while true do world := eval' "tick"; f.Refresh(); System.Threading.Thread.Sleep(33) 38 | (new System.Threading.Thread(new System.Threading.ThreadStart(loop))).Start() 39 | f 40 | ``` 41 | 42 | The last known mouse position is passed in on each tick. Again here we’re maintaining mutable state in the runtime but Scheme programs running within this system are expressed as pure functions of the input state. 43 | 44 | ## An Old Classic 45 | 46 | With the bouncing balls from before, Pong is the first interactive game that comes to mind. The left paddle will be the computer and follow the ball’s y position (you can’t win!). The right paddle is to be the player and is contolled by the mouse’s y. 47 | 48 | ``` scheme 49 | (define pongworld (lambda (fn world) 50 | (let ((ball (car world)) 51 | (mouse (cadr world))) 52 | (fn (car ball) (cadr ball) ; position x/y 53 | (caddr ball) (cadddr ball) ; delta x/y 54 | (cadr mouse))))) ; paddle position y 55 | 56 | (define bounce (lambda (i di) 57 | (if (or (and (> di 0) (> i 30)) ; heading toward right/bottom and off edge 58 | (and (> 0 di) (> 1 i))) ; or heading toward left/top and off edge 59 | (- di) di))) ; reverse direction 60 | 61 | (define paddle (lambda (x y color) (map 62 | (lambda (d) '((,x ,(+ y d)) ,color)) 63 | '(-2 -1 0 1 2)))) 64 | 65 | (define init (lambda (_) '(1 14 1 1))) 66 | 67 | (define tick (lambda (world) 68 | (pongworld (lambda (x y dx dy p) 69 | (if (and (> x 30) ; off player's edge? 70 | (or (< (+ y 3) p) (> (- y 3) p))) ; not hitting paddle? 71 | (init '()) ; reset, else move ball as usual... 72 | '(,(+ x dx) ,(+ y dy) ; increment position 73 | ,(bounce x dx) ,(bounce y dy) ; bounce off edges 74 | ))) world))) 75 | 76 | (define draw (lambda (world) 77 | (let ((red '(255 0 0)) 78 | (green '(0 255 0)) 79 | (blue '(0 0 255))) 80 | (pongworld (lambda (x y _ _ p) (cat (cat 81 | '(((,x ,y) ,green)) ; ball 82 | (paddle 0 y red)) ; computer's paddle 83 | (paddle 31 p blue) ; player's paddle 84 | )) world)))) 85 | ``` 86 | 87 | Give it a try: 88 | 89 | ``` scheme 90 | > (load "Pong.scm") 91 | Loaded 'Pong.scm'. 92 | > (run) 93 | ``` 94 | 95 | ![Pong](Images/pong.gif) 96 | 97 | ## The Beauty of FRP 98 | 99 | The real beauty of this FRP-ish system isn’t that we can write a cheesy Pong in 30 lines of Scheme. The beauty is that those 30 lines are absolutely pure! There is no mutation or side effects being used and yet we’re obviously maintaining state over time (ball position and vector). Unlike normal I/O APIs there are no impure zero-argument functions returning different things each time called (e.g. `Console.ReadLine`) or taking arguments and returning nothing (e.g. `Console.WriteLine`). The visual rendering is a pure function of the current world state instead of a sequence of mutations to a graphics context. 100 | 101 | It’s all expressed as a set of pure functions with no direct notion of time or state. Beautiful! 102 | 103 | ## Next: [Historical Debugging](debugging.md) 104 | -------------------------------------------------------------------------------- /Docs/functional_o.md: -------------------------------------------------------------------------------- 1 | # Functional I/O (or at least “O”) 2 | 3 | This is the twelth in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * Functional I/O (or at least "O") 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | I just watched [Matthias Felleisen’s talk](http://vimeo.com/6631514) on Functional I/O and read [this paper](http://delivery.acm.org/10.1145/1600000/1596561/p47-felleisen.pdf?key1=1596561&key2=4733387621&coll=GUIDE&dl=GUIDE&CFID=73785769&CFTOKEN=48695734) and decided to bolt on a little I/O system to the FScheme interpreter we’ve been building. It’s a pretty nice FRP-type system. Reading the paper or watching the talk is well worth the time. 21 | 22 | ## Pure Functional Simulation 23 | 24 | The basic idea is to run a simulation by iterating a pure function from world state to world state. We’ll add a new `run` primitive which will expect several user-defined functions to have been set up. The world state is initially produced by an `init` function. Then every 30th of a second a `tick` function is called to produce a new world state from the current state. Finally a `draw` function will be called to render the world. 25 | 26 | The world state doesn’t include any user input (mouse, keys, etc.), so I guess for now we’re not really handling “I/O” but just “O”. We could choose anything we like to represent the output state. It could be a set of 2D drawing primitives or a 3D model. It could include sounds to be played, network requests to be sent out, etc. For simplicity, here we’ll consider output to be a simple list of colored pixels. For example, a single red pixel at 10,15 would be: `((10 15) (255 0 0))` – the x/y coordinates and red/green/blue values. Output will be a list of such pixels which will be rendered on a 32x32 grid. 27 | 28 | To the programmer working within our system there is no state; only pure functions. But in the underlying runtime we’ll be maintaining the world and passing it into evaluated user-defined functions: 29 | 30 | ``` fsharp 31 | let world = ref (Symbol("Dummy world")) 32 | let eval' name = eval id environment (List([Symbol(name); List([Symbol("quote"); world.Value])])) 33 | ``` 34 | 35 | We’ll expect a `draw` function to be defined which will return a list of pixels to be rendered. We’ll call this and paint to a bitmap: 36 | 37 | ``` fsharp 38 | let w = 32 39 | let h = 32 40 | let s = 8 // pixel size 41 | let bmp = new Bitmap(w * s, h * s) 42 | let paint () = 43 | use gc = Graphics.FromImage(bmp) 44 | gc.Clear(Color.Black) |> ignore 45 | match eval' "draw" with 46 | | List(pixels) –> 47 | let fill = function 48 | | List([List([Number(x); Number(y)]); List([Number(r); Number(g); Number(b)])]) –> 49 | gc.FillRectangle(new SolidBrush(Color.FromArgb(0xFF, int r, int g, int b)), int x * s, int y * s, s - 1, s - 1) 50 | | _ –> () 51 | List.iter fill pixels 52 | | _ -> failwith "Malformed graphical output." 53 | bmp 54 | ``` 55 | 56 | The above ‘paint’ function asks ‘draw’ to produce a list of pixels which are painted as little filled rectangles. This ‘paint’ will be used to render a plain ol’ WinForms app: 57 | 58 | ``` fsharp 59 | type Form() = 60 | inherit System.Windows.Forms.Form() 61 | override x.OnPaintBackground _ = () // no flicker 62 | let form () = 63 | let f = new Form(Text = "Canvas", Width = w * s + 16 - 1, Height = h * s + 38 - 1, Visible = true) 64 | f.Paint.Add(fun a -> a.Graphics.DrawImage(paint (), 0, 0)) 65 | let loop () = while true do world := eval' "tick"; f.Refresh(); System.Threading.Thread.Sleep(33) 66 | (new System.Threading.Thread(new System.Threading.ThreadStart(loop))).Start() 67 | f 68 | ``` 69 | 70 | This form will eval the user-defined ‘tick’ function repeatedly and refresh (calling ‘paint’ from above). That’s about all there is to running a simulation. We’ll just define a simple ‘run’ function to initialize the world and to kick off this form: 71 | 72 | ``` fsharp 73 | let run cont _ = 74 | world := eval' "init" 75 | Application.Run(form ()); Dummy("Dummy 'run'.") |> cont 76 | ``` 77 | 78 | This is all the code for our little micro world simulation. Pretty dang simple really! 79 | 80 | ## What Can We Do With It? 81 | 82 | Here’s a sample balls.scm simulating several balls bouncing around on the screen: 83 | 84 | ``` scheme 85 | (define ballworld (lambda (fn world) 86 | (let ((ball (car world)) 87 | (color (cadr world))) 88 | (fn (car ball) (cadr ball) ; position x/y 89 | (caddr ball) (cadddr ball) ; delta x/y 90 | (car color) (cadr color) (caddr color))))) ; red/green/blue 91 | 92 | (define bounce (lambda (i di) 93 | (if (or (and (> di 0) (> i 31)) ; heading toward right/bottom and off edge 94 | (and (> 0 di) (> 0 i))) ; or heading toward left/top and off edge 95 | (- di) di))) ; reverse direction 96 | 97 | (define ballmove (lambda (ball) 98 | (ballworld (lambda (x y dx dy r g b) '(( 99 | ,(+ x dx) ,(+ y dy) ; increment position 100 | ,(bounce x dx) ,(bounce y dy)) ; bounce off edges 101 | (,r ,g ,b))) ball))) ; color unchanged 102 | 103 | (define balldraw (lambda (ball) 104 | (ballworld (lambda (x y dx dy r g b) 105 | '((,x ,y) (,r ,g ,b))) ball))) ; simply draw as single pixel at x/y position 106 | 107 | (define red '(255 0 0)) 108 | (define green '(0 255 0)) 109 | (define blue '(0 0 255)) 110 | 111 | (define init (lambda (_) 112 | '(((16 0 2 1) ,red) 113 | ((6 9 1 -1) ,green) 114 | ((16 16 -1 2) ,blue)))) 115 | (define tick (lambda (input) (map ballmove input))) 116 | (define draw (lambda (world) (map balldraw world))) 117 | ``` 118 | 119 | Ultimately the last three functions (‘init’, ‘tick’ and ‘draw’) represent the simulation. The ‘init’ will be called to create our world which consists of three different colored balls having various starting x/y positions and dx/dy motion deltas. The ‘tick’ function will produce a new world by moving the balls (mapping ‘ballmove’ over the three of them). Finally ‘draw’ will render this world to a list of pixels to be painted. The code’s pretty straight forward with maybe the exception of the ‘ballworld’ function which is used to unpack the world state with a bunch of ugly car/cadr/caddr/cadddr list accessors and pass as a cleaner set of parameters to a given function. To try it out, just load and run: 120 | 121 | ``` scheme 122 | > (load "Balls.scm") 123 | Loaded 'Balls.scm'. 124 | > (run) 125 | ``` 126 | 127 | And voila! 128 | 129 | ![Balls](Images/balls.gif) 130 | 131 | ## What About Input? 132 | 133 | In the next post we add the “I” of I/O. 134 | 135 | ## Next: [Functional I/O (including "I" this time)](functional_i.md) 136 | -------------------------------------------------------------------------------- /Docs/intro.md: -------------------------------------------------------------------------------- 1 | # FScheme - Scheme in F# 2 | 3 | This is the beginning of a fourteen part series: 4 | 5 | * Scheme in F# 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | One of my New Year’s goals (2007) is to re-read [Lisp in Small Pieces](http://www.amazon.com/gp/product/0521545668/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0521545668&linkCode=as2&tag=bookporn-20&linkId=PMDZINVZLBD65EE4) and implement all 11 interpreters and 2 compilers. As much as I like the "Lisp in Lisp" idea and enjoyed the eureka moment in SICP when Sussman writes the metacircular interpreter on the board to the music from Space Odyssey, I don't want to do Lisp in Lisp itself. Lisp in F# sounds like more fun. 21 | 22 | As a warm-up, I’m going through [Bill Hails’ absolutely awesome book](http://billhails.net/Book/) where he builds “PScheme”; a Scheme interpreter in Perl. Of course, doing it in F#. Here’s v0.0.0. It turned out pretty small (about 100 lines): 23 | 24 | ## Tokenizer 25 | 26 | I kind of hate these sort of state machine parsers. Later I’ll come back and redo this as parser combinators or lex/yacc, etc. but for now it’s simple enough (just strings, numbers and symbols): 27 | 28 | ``` fsharp 29 | open System 30 | open System.Numerics 31 | 32 | type Token = 33 | | Open | Close 34 | | Number of string 35 | | String of string 36 | | Symbol of string 37 | 38 | let tokenize source = 39 | let rec string acc = function 40 | | '\\' :: '"' :: t -> string (acc + "\"") t // escaped quote becomes quote 41 | | '"' :: t -> acc, t // closing quote terminates 42 | | c :: t -> string (acc + (c.ToString())) t // otherwise accumulate chars 43 | | _ -> failwith "Malformed string." 44 | let rec token acc = function 45 | | (')' :: _) as t -> acc, t // closing paren terminates 46 | | w :: t when Char.IsWhiteSpace(w) -> acc, t // whitespace terminates 47 | | [] -> acc, [] // end of list terminates 48 | | c :: t -> token (acc + (c.ToString())) t // otherwise accumulate chars 49 | let rec tokenize' acc = function 50 | | w :: t when Char.IsWhiteSpace(w) -> tokenize' acc t // skip whitespace 51 | | '(' :: t -> tokenize' (Open :: acc) t 52 | | ')' :: t -> tokenize' (Close :: acc) t 53 | | '"' :: t -> // start of string 54 | let s, t' = string "" t 55 | tokenize' (Token.String(s) :: acc) t' 56 | | '-' :: d :: t when Char.IsDigit(d) -> // start of negative number 57 | let n, t' = token ("-" + d.ToString()) t 58 | tokenize' (Token.Number(n) :: acc) t' 59 | | '+' :: d :: t | d :: t when Char.IsDigit(d) -> // start of positive number 60 | let n, t' = token (d.ToString()) t 61 | tokenize' (Token.Number(n) :: acc) t' 62 | | s :: t -> // otherwise start of symbol 63 | let s, t' = token (s.ToString()) t 64 | tokenize' (Token.Symbol(s) :: acc) t' 65 | | [] -> List.rev acc // end of list terminates 66 | tokenize' [] source 67 | ``` 68 | 69 | ## Parser 70 | 71 | The near-equivalence between syntax and semantics in Scheme makes the parser trivial. There’s almost a 1:1 correspondence between tokens and expressions. One exception is that the `Open`/`Close` tokens denote Expression lists: 72 | 73 | ``` fsharp 74 | type Expression = 75 | | Number of BigInteger 76 | | String of string 77 | | Symbol of string 78 | | List of Expression list 79 | | Function of (Expression list -> Expression) 80 | | Special of (Expression list -> Expression) 81 | 82 | let parse source = 83 | let map = function 84 | | Token.Number(n) -> Expression.Number(BigInteger.Parse(n)) 85 | | Token.String(s) -> Expression.String(s) 86 | | Token.Symbol(s) -> Expression.Symbol(s) 87 | | _ -> failwith "Syntax error." 88 | let rec parse' acc = function 89 | | Open :: t –> 90 | let e, t' = parse' [] t 91 | parse' (List(e) :: acc) t' 92 | | Close :: t -> (List.rev acc), t 93 | | h :: t -> parse' ((map h) :: acc) t 94 | | [] -> (List.rev acc), [] 95 | let result, _ = parse' [] (tokenize source) 96 | result 97 | ``` 98 | 99 | ## Printer 100 | 101 | For printing expressions to the console: 102 | 103 | ``` fsharp 104 | let rec print = function 105 | | List(list) -> "(" + String.Join(" ", (List.map print list)) + ")" 106 | | String(s) –> 107 | let escape = String.collect (function '"' -> "\\\"" | c -> c.ToString()) // escape quotes 108 | "\"" + (escape s) + "\"" 109 | | Symbol(s) –> s 110 | | Number(n) -> n.ToString() 111 | | Function(_) | Special(_) -> "Function" 112 | ``` 113 | 114 | ## Primitives 115 | 116 | We begin with just a few primitives (which are simply Expression –> Expression functions) for doing basic math and conditionals and to seed a global environment with them: 117 | 118 | ``` fsharp 119 | let Multiply args = 120 | let prod a = function Number(b) -> a * b | _ -> failwith "Malformed multiplication argument." 121 | Number(List.fold prod 1I args) 122 | 123 | let Subtract = function 124 | | [] -> Number(0I) // (-) == 0 125 | | [Number(n)] -> Number(-n) // (- a) == –a 126 | | Number(n) :: ns -> // (- a b c) == a - b – c 127 | let sub a = function Number(b) -> a - b | _ -> failwith "Malformed subtraction argument." 128 | Number(List.fold sub n ns) 129 | | _ -> failwith "Malformed subtraction." 130 | 131 | let rec If = function 132 | | [condition; t; f] –> 133 | match eval condition with 134 | | List([]) | String("") -> eval f // empty list or empty string is false 135 | | Number(n) when n = 0I -> eval f // zero is false 136 | | _ -> eval t // everything else is true 137 | | _ -> failwith "Malformed 'if'." 138 | 139 | and environment = 140 | Map.ofList [ 141 | "*", Function(Multiply) 142 | "-", Function(Subtract) 143 | "if", Special(If)] 144 | ``` 145 | 146 | ## Eval/Apply 147 | 148 | The classic yin/yang of eval/apply are very easy to implement. Literals eval to themselves, symbols are looked up in the environment, and lists are applied. Special forms are, well, special in that they don’t eval their arguments up front; leaving it up to the callee (e.g. `If` will eval one of two expressions depending on the conditional). This is important to allow short-circuiting. 149 | 150 | Notice that `If` and `environment` above along with eval and apply below are all mutually recursive: 151 | 152 | ``` fsharp 153 | and eval expression = 154 | match expression with 155 | | Number(_) as lit –> lit 156 | | String(_) as lit –> lit 157 | | Symbol(s) -> environment.[s] 158 | | List([]) –> List([]) 159 | | List(h :: t) –> 160 | match eval h with 161 | | Function(f) -> apply f t 162 | | Special(f) -> f t 163 | | _ -> failwith "Malformed expression." 164 | | _ -> failwith "Malformed expression." 165 | 166 | and apply fn args = fn (List.map eval args) 167 | ``` 168 | 169 | ## REPL 170 | 171 | The REPL now just reads a line from the console, parses, evals and prints it, then rinses and repeats. 172 | 173 | ``` fsharp 174 | let rep = List.ofSeq >> parse >> List.head >> eval >> print 175 | 176 | let rec repl output = 177 | printf "%s\n> " output 178 | try Console.ReadLine() |> rep |> repl 179 | with ex -> repl ex.Message 180 | ``` 181 | 182 | The whole deal is kicked off with: 183 | 184 | ``` fsharp 185 | repl "Welcome to FScheme" 186 | ``` 187 | 188 | ## Tests 189 | 190 | Gotta have tests: 191 | 192 | ``` fsharp 193 | let test () = 194 | let case source expected = 195 | try 196 | let output = rep source 197 | if output <> expected then 198 | printf "TEST FAILED: %s (Expected: %s, Actual: %s)" source expected output 199 | with _ -> printf "TEST CRASHED: %s" source 200 | case "1" "1" // numbers 201 | case "+1" "1" // explicit positive numbers 202 | case "-1" "-1" // negative numbers 203 | case "\"hello\"" "\"hello\"" // strings 204 | case "(*)" "1" // multiplication 205 | case "(* 2 3)" "6" // multiplication 206 | case "(* 2 3 4)" "24" // multiplication 207 | case "(-)" "0" // strange subtraction case 208 | case "(- 10)" "-10" // negation 209 | case "(- 10 2)" "8" // subtraction 210 | case "(- 10 2 3)" "5" // subtraction 211 | case "(if (* 0 1) 10 20)" "20" // if 212 | case "(if (* 1 1) 10 20)" "10" // if 213 | case "(if (* 1 1) 10 bomb)" "10" // if (special form) 214 | case "(* 1234567890987654321 1234567890987654321)" "1524157877457704723228166437789971041" // bigint math 215 | ``` 216 | ## Next: [Just 'let' Me Be!](let.md) 217 | -------------------------------------------------------------------------------- /Docs/lambda.md: -------------------------------------------------------------------------------- 1 | # Lambda the Ultimate! 2 | 3 | This is the third in the fourteen part series: 4 | 5 | * [Scheme in F#](intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * Lambda the Ultimate! 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | Continuing along in [Bill Hails’ book](http://billhails.net/Book/v2.html). Be sure to follow the [previous posts](let.md). 21 | 22 | ## Lambda 23 | 24 | Now we’re going to add the all-powerful ‘lambda’! Since we’ve already done all the environment-passing plumbing from the last post, this will be straight forward. Lambda creates closures (read [Bill’s book](http://billhails.net/Book/v2.html) for an excellent description with pictures). 25 | 26 | Essentially, it returns a new function that when called will do the following: 27 | 28 | 1. Creates a new environment frame, binding parameters to call-time arguments which are evaluated in the **caller’s environment** 29 | 2. Extends the captured **definition-time environment** with this new frame 30 | 3. Finally, evals the body in this extended environment 31 | 32 | ``` fsharp 33 | and Lambda env = function 34 | | [List(parameters); body] –> 35 | let closure env' args = 36 | // bind parameters to actual arguments (evaluated in the caller's environment) 37 | let bindings = List.zip parameters args 38 | let bind = function Symbol(p), a -> p, (eval env' a) 39 | | _ -> failwith "Malformed 'lambda' parameter." 40 | let env'' = List.map bind bindings |> extend env // extend the captured env 41 | eval env'' body 42 | Special(closure) 43 | | _ -> failwith "Malformed Lambda." 44 | ``` 45 | 46 | When Lambda is called, it’s given the definition-time environment as `env`. We capture this by way of the inner `closure` function immediately returned as a Function(closure) Expression. Notice that `closure` itself takes the call-time environment as `env'` (with a prime mark). Later, when the function happens to be called (potentially in a completely different scope) it is passed the caller’s environment and does it’s magic. 47 | 48 | We just need to add this guy to the global environment as a special form and we’re done: 49 | 50 | ``` fsharp 51 | and environment = 52 | extend [] [ 53 | ... 54 | "lambda", Special(Lambda)] 55 | ``` 56 | 57 | If you want to marvel at the beauty and power of lambda, [check out this post](http://blogs.msdn.com/ashleyf/archive/2008/12/03/the-lambda-calculus.aspx) :-) 58 | 59 | ## Tests 60 | 61 | As usual, more tests: 62 | 63 | ``` fsharp 64 | case "(let ((square (lambda (x) (* x x)))) (square 4))" "16" // lambda 65 | case "(let ((square (lambda (x) (* x x)))) square)" "Function" // print lambda 66 | case "(let ((times3 (let ((n 3)) (lambda (x) (* n x))))) (times3 4))" "12" // closure 67 | case "(let ((times3 (let ((makemultiplier (lambda (n) (lambda (x) (* n x))))) (makemultiplier 3)))) (times3 5))" "15" // higher order functions 68 | ``` 69 | 70 | ## Next: [Rinse and Recurse](recurse.md) 71 | -------------------------------------------------------------------------------- /Docs/let.md: -------------------------------------------------------------------------------- 1 | # Just ‘let’ Me Be Already! 2 | 3 | This is the second in the fourteen part series: 4 | 5 | * [Scheme in F#](intro.md) 6 | * Just 'let' Me Be! 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | Still working through [Bill Hails’ awesome book](http://billhails.net/Book/). Adding to the FScheme interpreter from [the previous post](intro.md). Now we’ll add `let`. To do this we’re changing the evaluation model into an environment-passing one. 21 | 22 | ## Environment Passing 23 | 24 | So now the environment isn’t going to just be a single global map. It’s now going to be a list of maps. Each time a new scope is created, the environment will be extended. When looking up symbols, the whole chain is searched. We’ll add a couple of helper functions for this and the global environment will become an extension of empty (plus we’ll go ahead and add a mapping for our yet-to-be-defined `let`): 25 | 26 | ``` fsharp 27 | let extend env bindings = (Map.ofList bindings) :: env 28 | let lookup env symbol = 29 | match List.tryPick (Map.tryFind symbol) env with 30 | | Some(e) –> e 31 | | None -> sprintf "No binding for '%s'." symbol |> failwith 32 | and environment = 33 | extend [] [ 34 | "*", Function(Multiply) 35 | "-", Function(Subtract) 36 | "if", Special(If) 37 | "let", Special(Let)] 38 | ``` 39 | 40 | Then special forms change to take an environment (which is a `Map` list): 41 | 42 | ``` fsharp 43 | type Expression = 44 | | Number of BigInteger 45 | | String of string 46 | | Symbol of string 47 | | List of Expression list 48 | | Function of (Expression list -> Expression) 49 | | Special of (Map list -> Expression list -> Expression) 50 | ``` 51 | 52 | Then eval and apply change to thread the environment through. Also symbols need to be looked up with our helper and the REPL needs to thread in the global initially (note `env` begin threaded throughout): 53 | 54 | ``` fsharp 55 | and eval env expression = 56 | match expression with 57 | | Expression.Number(_) as lit –> lit 58 | | Expression.String(_) as lit –> lit 59 | | Expression.Symbol(s) -> lookup env s 60 | | Expression.List(h :: t) –> 61 | match eval env h with 62 | | Function(f) -> apply env f t 63 | | Special(f) -> f env t 64 | | _ -> failwith "Malformed expression." 65 | | _ -> failwith "Malformed expression." 66 | and apply env fn args = fn (List.map (eval env) args) 67 | let rep = List.ofSeq >> parse >> List.head >> (eval environment) >> print 68 | ``` 69 | 70 | Finally, we need to continue this plumbing work through `If` which needs to just pass it along to `eval`: 71 | 72 | ``` fsharp 73 | let rec If env = function 74 | | [condition; t; f] –> 75 | match eval env condition with 76 | | List([]) | String("") -> eval env f // empty list or empty string is false 77 | | Number(n) when n = 0I -> eval env f // zero is false 78 | | _ -> eval env t // everything else is true 79 | | _ -> failwith "Malformed If." 80 | ``` 81 | 82 | ## Finally, ‘let’ 83 | 84 | Sheesh, finally we can implement `let`. We just map the bindings onto a new (extended) environment frame and eval the body within that: 85 | 86 | ``` fsharp 87 | and Let env = function 88 | | [List(bindings); body] –> 89 | let bind = function List([Symbol(s); e]) -> s, (eval env e) | _ -> failwith "Malformed 'let' binding." 90 | let env' = List.map bind bindings |> extend env 91 | eval env' body 92 | | _ -> failwith "Malformed Let." 93 | ``` 94 | 95 | ## Tests 96 | 97 | Add a couple of test cases: 98 | 99 | ``` fsharp 100 | case "(let ((x 2)) x)" "2" // simple let 101 | case "(let ((a 00) (b 10) (c 20)) (if a b c))" "20" // conditional eval 102 | ``` 103 | 104 | ## Next: [Lambda the Ultimate!](lambda.md) 105 | -------------------------------------------------------------------------------- /Docs/letstar.md: -------------------------------------------------------------------------------- 1 | # What ‘letrec’ Can’t Do 2 | 3 | This is the fifth in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * What 'letrec' Can't Do 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | In this series I’m glossing over all the gory details. You really should read [Bill Hails’ book](http://billhails.net/Book/)! 21 | 22 | ## Simultaneous 'let' 23 | 24 | We just added `letrec` to solve the issue of bindings referring back to themselves. Remember that to implement it we first extend the environment with a frame containing dummy values, then eval bindings against that dummy frame, and finally swap out the dummy values with actual results. But what happens if the bindings are not lambdas (which promise not to touch the values until later)? Won’t they end up trying to use dummy values before they’re swapped? Consider something like `(letrec ((a 1) (b a)) b)`. Will this work? Won’t `b` refer to a dummy value for `a`? 25 | 26 | Well, it turns out that, by sheer luck, our implementation is actually sequentially doing destructive updates to the dummy values so it works as long as there’s no forward refs. Remember this code from `LetRec`: 27 | 28 | ``` fsharp 29 | let update = function [Symbol(s); e] -> (env'.Head.Item s) := (eval env' e) … 30 | List.iter update bindings 31 | ``` 32 | 33 | By the time `(b a)` is evaluated, the real value for `a` will indeed be in place. This is pure luck though and isn’t necessarily the intended semantics of `letrec`. I do want to come back at some point and change `letrec` to avoid mutation and this behavior will almost certainly change. Interestingly enough though, even PLT Scheme acts this way. 34 | 35 | ## Sequential ‘let’ 36 | 37 | At any rate, we’re faithfully following along with Bill. We plan to introduce `let*` (“let star”) to allow sequential bindings. It frees us to later change `letrec` and it will be nice syntactic sugar to avoid resorting to nested lets to accomplish the same thing: `(let ((a 1)) (let ((b a)) b))`. The implementation is pretty similar to regular `let`: 38 | 39 | ``` fsharp 40 | and LetStar env = function 41 | | [List(bindings); body] –> 42 | let bind env = function List([Symbol(s); e]) -> [s, ref (eval env e)] |> extend env 43 | | _ -> failwith "Malformed 'let*' binding." 44 | let env' = List.fold bind env bindings 45 | eval env' body 46 | | _ -> failwith "Malformed 'let*'." 47 | ``` 48 | 49 | Notice how similar the `bind` and `env'` above are to that of normal `let`: 50 | 51 | ``` fsharp 52 | let bind = function List([Symbol(s); e]) -> s, ref (eval env e) … 53 | let env' = List.map bind bindings |> extend env 54 | ``` 55 | 56 | Instead of returning a single bound pair, `bind` takes an `env` and returns a new environment, extended with a frame containing the single binding. Then instead of mapping over a list of bindings, we fold over them while threading the environment through. The result is a new environment that’s been extended repeatedly with a chain of frames, each with one binding having been evaluated against the tail built before it. 57 | 58 | That’s it. All that’s left is to add our new `let*` to the global environment: 59 | 60 | ``` fsharp 61 | and environment = 62 | extend [] [ 63 | ... 64 | "let*", ref (Special(LetStar))] 65 | ``` 66 | 67 | ## Tests 68 | 69 | And add a few tests: 70 | 71 | ``` fsharp 72 | case "(let ((a 1) (b 2)) (let ((a b) (b a)) b))" "1" // let binds in parallel (should work in earlier versions too) 73 | case "(let ((a 1) (b 2)) (let* ((a b) (b a)) b))" "2" // let* binds sequentially 74 | case "(let ((a 5)) (let ((b (* a 2))) (let ((c (- b 3))) c)))" "7" // poor-man's sequential expressions 75 | case "(let* ((a 5) (b (* a 2)) (c (- b 3))) c)" "7" // let* sequential expressions 76 | ``` 77 | ## Next: [What's Lisp Without Lists?!](lists.md) 78 | -------------------------------------------------------------------------------- /Docs/library.md: -------------------------------------------------------------------------------- 1 | # Language vs. Library 2 | 3 | This is the second in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * Language vs. Library 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | ## Primitives 21 | 22 | Perhaps this post should have gone along with the [one about macros](macros.md) and how Lisp is a “programmable programming language.” The common tension in any language or runtime design is how much to build in as primitives and how much to implement as libraries within the language or atop the runtime. Many of the primitives we’ve already built into the interpreter are really not so primitive at all and can be defined in terms of each other. 23 | 24 | For example, we introduced ‘list’ as a convenience to avoid nested ‘cons’. The expression (list a b c) is equivalent to (cons a (cons b (cons c ()))). We could define ‘list’ as a macro in terms of ‘cons’ or maybe in terms of ‘quote’ and ‘eval’. Deciding what subset of Scheme to consider truly primitive, in terms of which seemingly primitive things are defined, is a balancing act. If you really want to go crazy you can ultimately define everything in terms of ‘lambda’ or ‘macro’ (see [this fun post!](http://blogs.msdn.com/b/ashleyf/archive/2008/12/03/the-lambda-calculus.aspx)). 25 | 26 | ## The Mother ‘load’ 27 | 28 | Here we’ll build a bit of infrastructure to begin the migration of features out of the language and into libraries. We’ll add a ‘load’ function to allow loading source files and we’ll designate a “Prelude.scm” library to be loaded automatically. 29 | 30 | ``` fsharp 31 | and load file = (File.OpenText(file)).ReadToEnd() |> 32 | List.ofSeq |> parse |> List.iter (eval environment >> ignore) 33 | and Load = function 34 | | [String(path)] -> load path; Symbol(sprintf "Loaded '%s'." path) 35 | | m -> malformed "load" (List(m)) 36 | ``` 37 | 38 | This lets us `(load “MySource.scm”)` presumably containing definitions. Besides explicitly loading your own libraries we’ll also add an implicit call to `load "Prelude.scm"` before kicking off the REPL to include library-defined primitive. We should go ahead and quickly add a feature to allow comments (simicolon to end-of-line); just skipping over them during tokenization. 39 | 40 | ``` fsharp 41 | let tokenize source = 42 | let rec comment = function 43 | | '\r' :: t | '\n' :: t -> t // terminated by line end 44 | | [] -> [] // or by EOF 45 | | _ :: t -> comment t 46 | ... 47 | let rec tokenize' acc = function 48 | ... 49 | | ';' :: t -> comment t |> tokenize' acc // skip over comments 50 | ``` 51 | 52 | ## Prelude 53 | 54 | We can now include in Prelude.scm the boolean operators we used as examples in the [macro post](macros.md): 55 | 56 | ``` scheme 57 | ; logical 'and', 'or', 'not', 'xor' 58 | (define and (macro (a b) '(if ,a (if ,b 1 0) 0))) 59 | (define or (macro (a b) '(if ,a 1 (if ,b 1 0)))) 60 | (define not? (lambda (x) (if x 0 1))) 61 | (define xor (lambda (a b) (and (or a b) (not? (and a b))))) 62 | ``` 63 | 64 | We can start to really turn this into a proper language with standard higher-order list functions: 65 | 66 | ``` scheme 67 | ; map function (f) over list (xs) 68 | (define map (lambda (f xs) ; apply f to each element of xs 69 | (if xs ; if not empty then 70 | (cons (f (car xs)) ; cons f of the head... 71 | (map f (cdr xs))) ; onto result of recursing down the tail 72 | ()))) ; otherwise return empty 73 | 74 | ; fold function (f) over list (xs) while accumulating (a) 75 | (define fold (lambda (f a xs) 76 | (if (not? xs) a 77 | (fold f (f (car xs) a) (cdr xs))) 78 | )) 79 | ``` 80 | 81 | This isn’t really a lesson in higher order functions, but notice how `map` and `fold` abstract over categories of recursion, allowing us to then define other things in terms of them without thinking recursively. For example, to reverse a list: 82 | 83 | ``` scheme 84 | (define reverse (lambda (xs) (fold cons () xs))) 85 | ``` 86 | 87 | Or, like we promised, we can start replacing built-in primitives such as `list` and then remove them from the F# source: 88 | 89 | ``` scheme 90 | (define list (macro (xs) '(map eval (quote ,xs)))) 91 | ``` 92 | 93 | It’s starting to get really fun! 94 | 95 | [One other feature recently added is the ability to pass a variable number of args to a lambda or macro by simply calling with more args than defined params. In this case the last param becomes a list containing the overflow. This is used in the definition of `list`. I didn’t talk about this, but feel free to check the diffs] 96 | 97 | ## Tests 98 | 99 | ``` fsharp 100 | case "(and 0 0)" "0" // or (false) 101 | case "(and 1 0)" "0" // or (false) 102 | case "(and 0 1)" "0" // or (false) 103 | case "(and 1 1)" "1" // or (true) 104 | case "(or 0 0)" "0" // or (false) 105 | case "(or 1 0)" "1" // or (true) 106 | case "(or 0 1)" "1" // or (true) 107 | case "(or 1 1)" "1" // or (true) 108 | case "(not? 0)" "1" // or (true) 109 | case "(not? 1)" "0" // or (false) 110 | case "(xor 0 0)" "0" // xor (false) 111 | case "(xor 1 0)" "1" // xor (true) 112 | case "(xor 0 1)" "1" // xor (true) 113 | case "(xor 1 1)" "0" // xor (false) 114 | case "(let ((square (lambda (x) (* x x)))) (map square '(1 2 3 4 5 6 7 8 9)))" "(1 4 9 16 25 36 49 64 81)" // mapping 115 | case "(let ((square (lambda (x) (* x x)))) (map square '(9)))" "(81)" // mapping single 116 | case "(let ((square (lambda (x) (* x x)))) (map square '()))" "()" // mapping empty 117 | case "(fold * 1 '(2 3 4 5))" "120" // fold 118 | case "(reverse '(1 2 3))" "(3 2 1)" // reverse 119 | ``` 120 | 121 | ## Next: [Turning Your Brain Inside Out With Continuations](continuations.md) 122 | -------------------------------------------------------------------------------- /Docs/lists.md: -------------------------------------------------------------------------------- 1 | # What's Lisp Without Lists?! 2 | 3 | This is the sixth in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * What's Lisp Without Lists?! 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | ## Lists 21 | 22 | How could we even be pretending this is Lisp when we have yet to add lists! We need to add the standard `cons`, `car`, and `cdr` for constructing and destructing list structure and we’ll go ahead and add the nice-to-have `list` (to construct lists from a set of arguments without an ugly chain of `cons`). Because of the isomorphism between Scheme and F# lists, these are embarrassingly simple to implement: 23 | 24 | ``` fsharp 25 | and Cons = function [h; List(t)] -> List(h :: t) | _ -> failwith "Malformed 'cons'." 26 | and Car = function [List(h :: _)] -> h | _ -> failwith "Malformed 'car'." 27 | and Cdr = function [List(_ :: t)] -> List(t) | _ -> failwith "Malformed 'cdr'." 28 | and Lst args = List(args) 29 | ``` 30 | 31 | It can’t get much easier than that. `Cons` takes an expression and a list and returns a new list with the expression prepended. `Car` returns the head of a list. `Cdr` returns the tail. `Lst` just makes a list. Add them to the environment as usual and we’re done: 32 | 33 | ``` fsharp 34 | and environment = 35 | extend [] [ 36 | ... 37 | "cons", ref (Function(Cons)) 38 | "car", ref (Function(Car)) 39 | "cdr", ref (Function(Cdr)) 40 | "list", ref (Function(Lst))] 41 | ``` 42 | 43 | ## Improper Lists 44 | 45 | I decided not to implement “improper lists” and the dotted-pair notation. This is where we would allow pairs to contain something other than a list as their `cdr` and allow a syntax like `(1 2 . 3)` to construct such things. I don’t believe that lacking this feature limits our expressiveness as far as data structures at all. You can still build trees and all, no problem. It would complicate things quite a bit and with little benefit. Maybe we’ll revisit this later… 46 | 47 | ## Tests 48 | 49 | ``` fsharp 50 | case "(list 1 2 3)" "(1 2 3)" // list 51 | case "(car (list 1 2 3))" "1" // car 52 | case "(cdr (list 1 2 3))" "(2 3)" // cdr 53 | case "(cons 1 (list 2 3))" "(1 2 3)" // cons 54 | case "(cons 1 (cons 2 (cons 3 ())))" "(1 2 3)" // cons x3 55 | case "(let ((a 1) (b 2) (c 3)) (list a b c))" "(1 2 3)" // list 56 | case "(let ((a (list 1 2 3))) (car a))" "1" // car 57 | case "(let ((a (list 1 2 3))) (cdr a))" "(2 3)" // cdr 58 | ``` 59 | ## Next: [No Wait, Macro the Ultimate!](macros.md) 60 | -------------------------------------------------------------------------------- /Docs/macros.md: -------------------------------------------------------------------------------- 1 | # No Wait, Macro the Ultimate! 2 | 3 | This is the seventh in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * No Wait, Macro the Ultimate! 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | ## The Soul of Scheme 21 | 22 | We’re now getting into the language-oriented features of Scheme. This is why Scheme is one of my very favorite languages. Scheme is of course a multi-paradigm language; functional, imperative (next post), object oriented (soon), but most wonderfully and uniquely language-oriented. In object-oriented programming the approach is to extend the type system to your problem domain and then solve the domain-specific problem at hand using these new types. In language-oriented development you first extend the language itself (not just the type system) with domain specific constructs and then solve the problem. All of the recent hype (circa 2007) around DSLs is old hat for Lisp guys. 23 | 24 | Lisp has been called a “programmable programming language" and this language-oriented approach is what Alan Kay meant when he said, “Lisp isn't a language, it's a building material." I love how Paul Graham phrased the whole creative experience in his book On Lisp: “Language and program evolve together. Like the borders between two warring states, the boundary between language and program is drawn and redrawn, until eventually it comes to rest along the mountains and rivers, the natural frontiers of your problem.” 25 | 26 | F# can also be used in a language-oriented style by way of quotations. There's a chapter on the subject in Don Syme's Expert F#. Thomas Petricek has done some amazing work with [Web Tools using F# quotations](http://tomasp.net/blog/fswebtools-intro.aspx). We will be adding quotations to our Scheme implementation in this post. The difference between F# and Scheme quotations though is that in F# a quotation becomes an expression tree; an AST representation normally used internally as output from the parser and input to the compiler. It expresses all of the same basic concepts but in an entirely different structure to that of the surface syntax of the language in which you’re used to working. 27 | 28 | Scheme on the other hand can be thought of as having no surface syntax at all! It’s as if the syntax is just a straight serialization of the AST. This makes manipulating program structure just as natural as manipulating data structure. The structure is one and the same. In Scheme, program fragments and data are both just lists and can be tinkered with equally using [`cons`, `car` and `cdr`](lists.md). This one-to-one correspondence between internal program representation and syntactic representation is termed homoiconicity and is a key distinguishing feature of the Lisp family. [McCarthy](http://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)) originally planned to add [M-expression](http://en.wikipedia.org/wiki/M-expression) syntax on top of S-expressions but never did and honestly people who really use Lisp for serious work don’t want any syntax. People [poke fun at all of the parenthesis](http://xkcd.com/297/) but they’re there for good reason. 29 | 30 | ## Quotations 31 | 32 | To enable working with program fragments as data, we’ll first introduce ‘quote’ which will be a special form that returns its argument as plain data without evaluating. Additionally, we’ll allow for marking pieces within a quoted structure with ‘unquote’ which will cause just those pieces to be evaluated within. 33 | 34 | ``` fsharp 35 | and Quote env = 36 | let rec unquote = function 37 | | List([Symbol("unquote"); e]) -> eval env e 38 | | List(Symbol("unquote") :: _) -> failwith "Malformed 'unquote'." // too many args 39 | | List(h :: t) -> List(h :: (List.map unquote t)) // recurse 40 | | e –> e 41 | function [e] -> unquote e | _ -> failwith "Malformed 'quote'." 42 | ``` 43 | 44 | This lets us do things like `(quote (* 2 (- 5 2)))` and instead of getting `6`, we get nested list structure containing symbols and numbers; literally: `(* 2 (- 5 2))`. If we want to evaluation just part of it we could say `(quote (* 2 (unquote (- 5 2))))` and get `(* 2 3)`. Notice that `unquote` is more like a keyword that only has meaning within a quoted expression. 45 | 46 | Using `quote` and `unquote` is extremely common and could use a more optimized syntax. Yes, I did just say “syntax.” This is one place where we will consider breaking the homoiconicity and introduce special syntax. Internally (in the AST), we will still have `quote` and `unquote` symbols in function application form (and you’re free to stick with that), but the syntax will be simplified to an apostrophe or comma preceding expressions to be quoted or unquoted respectively. This can be handled at lexing/parsing time. First add to the tokenizer: 47 | 48 | ``` fsharp 49 | type Token = 50 | | Open | Close 51 | | Quote | Unquote // <-- added 52 | | Number of string 53 | | String of string 54 | | Symbol of string 55 | let rec tokenize' acc = function 56 | ... 57 | | ',' :: t -> tokenize' (Unquote :: acc) t // <-- added 58 | | '"' :: t -> // start of string // <-- added 59 | ``` 60 | 61 | Then in the parser, we factor our the `Open` match a bit and reuse for `Quote :: Open` and `Unquote :: Open`: 62 | 63 | ``` fsharp 64 | let rec list f t acc = 65 | let e, t' = parse' [] t 66 | parse' (List(f e) :: acc) t' 67 | and parse' acc = function 68 | ... 69 | | Quote :: Open :: t -> list (fun e -> [Symbol("quote"); List(e)]) t acc 70 | | Quote :: h :: t -> parse' (List([Symbol("quote"); map h]) :: acc) t 71 | | Unquote :: Open :: t -> list (fun e -> [Symbol("unquote"); List(e)]) t acc 72 | | Unquote :: h :: t -> parse' (List([Symbol("unquote"); map h]) :: acc) t 73 | ``` 74 | 75 | Now instead of the heavy looking `(quote (* 2 (unquote (- 5 2))))`, we can say `‘(* 2 ,(- 5 2))` and yield the same `(* 2 3)`. Or how about `(let ((x 'rain) (y 'spain) (z 'plain)) '(the ,x in ,y falls mainly on the ,z))`. What do you think that resolves to? It’s much nicer looking syntax indeed. 76 | 77 | ## Eval 78 | 79 | Quotations are useful for manipulating program structure as lists but then we need a way of explicitly executing our manipulations. This is where `eval` comes in. The semantics are a little tricky. First it evaluates a quotation against the passed in environment (the call-time environment). The result of that will be the literal expression (with unquoted pieces resolved). Then that is evaluated again but in the global environment: 80 | 81 | ``` fsharp 82 | and Eval env = function [args] -> args |> eval env |> eval environment | _ -> failwith "Malformed 'eval'." 83 | ``` 84 | 85 | So now we can say `(eval ‘(* 2 ,(- 5 2)))` and get `6`. 86 | 87 | ## Macros 88 | 89 | Now for the grand finale. We want to be able to introduce our own special forms into the language; to program the programming language. It is true that [`lambda`](lambda.md) allows us to add things that look like first class primitives; nearly indistinguishable from built-in functions. But there are limitations. 90 | 91 | For example, let’s try to build `and` which will take two arguments and return true (`1`) if both are true. The catch is that it should have the same semantics of the real `and` which would be a special form that doesn’t evaluate its arguments up front but instead “short-circuits” in that it shouldn’t bother to evaluate the second argument if the first argument evaluates to false. This would be partially for efficiency but more importantly to maintain workable semantics. What if, for example, the second argument were a recursive call and the `and` expression was the base case? Eagerly evaluating could cause an infinite loop! 92 | 93 | Using only `lambda`, we could try `(let ((and (lambda (a b) (if a (if b 1 0) 0)))) (and 0 BOOM))`. Oops, we see an error (No binding for 'BOOM'.) even though it should not have touched that, once it realized that the first argument in `(and 0 BOOM)` is false (`0`). The `if` is indeed a special form and short-circuits, but the issue is that all of the arguments to `and` are evaluated before calling the lambda. That’s the nature of applicative order languages (lazy languages like Haskell need no “special form” distinction). 94 | 95 | Let’s try again. This time we’ll quote the arguments and explicitly eval them within the lambda: `(let ((and (lambda (a b) (if (eval a) (if (eval b) 1 0) 0)))) (and '0 'BOOM))`. Well that works (we get `0` and no complaint about missing bindings) but how very annoying to expose the implementation details to the callers. We’d much rather the quoting at the call site to be implicit. Enter `macro`: 96 | 97 | ``` fsharp 98 | and Macro env = function 99 | | [List(parameters); body] –> 100 | let closure env' args = 101 | // bind parameters to actual arguments (but unevaluated, unlike lambda) 102 | let bindings = List.zip parameters args 103 | let bind = function Symbol(p), a -> p, ref a | _ -> failwith "Malformed 'macro' parameter." 104 | let env'' = List.map bind bindings |> extend env // extend the captured definition-time environment 105 | eval env'' body |> eval env' // eval against bound args, then again in the caller's environment 106 | Special(closure) 107 | | _ -> failwith "Malformed 'macro'." 108 | ``` 109 | 110 | This is much the same as Lambda but it binds the arguments in a new environment frame without evaluating them in the calling environment. It then evaluates the body in that extended environment as usual. Then finally evaluates the result against the captured environment. In other words, a macro’s arguments are treated as implicitly quoted and are bound as literals (without evaluating). The result of evaluating the body is expected to be a modified program structure that’s then evaluated again to get a result. 111 | 112 | Now we can rewrite our `and` as `(let ((and (macro (a b) '(if ,a (if ,b 1 0) 0)))) (and 0 BOOM))`. Notice that the macro uses quoting and unquoting internally but callers can treat `and` exactly as if it were a primitive special form. By the way, we can define `or` as well with similar short-circuit semantics: `(let ((or (macro (a b) '(if ,a 1 (if ,b 1 0))))) (or 1 BOOM))`. Pretty neat stuff. 113 | 114 | One of the super-grand finales of the whole series will be to come back and replace a bunch of our primitive special forms (written in F#) with a library written in FScheme itself! 115 | 116 | # Tests 117 | 118 | ``` fsharp 119 | case "(quote (* 2 3))" "(* 2 3)" // quote primitive 120 | case "'(* 2 3)" "(* 2 3)" // quote primitive with sugar 121 | case "(eval '(* 2 3))" "6" // eval quoted expression 122 | case "(quote (* 2 (- 5 2)))" "(* 2 (- 5 2))" // quote nested 123 | case "(quote (* 2 (unquote (- 5 2))))" "(* 2 3)" // quote nested unquote 124 | case "'(* 2 ,(- 5 2))" "(* 2 3)" // quote nested unquote with sugar 125 | case "(quote (quote 1 2 3))" "(quote 1 2 3)" // quote special form 126 | case "(let ((x 'rain) (y 'spain) (z 'plain)) '(the ,x in ,y falls mainly on the ,z))" 127 | "(the rain in spain falls mainly on the plain)" // quote/unquote 128 | case "(let ((* -)) (eval '(* 3 3)))" "9" // eval uses top-level environment 129 | case "(let ((or (macro (a b) '(if ,a 1 (if ,b 1 0))))) (or 1 BOOM))" "1" // macro as special form 130 | case "(let ((and (macro (a b) '(if ,a (if ,b 1 0) 0)))) (and 0 BOOM))" "0" // macro as special form 131 | ``` 132 | 133 | ## Next: [Oh, The Humanity!](mutation.md) 134 | -------------------------------------------------------------------------------- /Docs/mutation.md: -------------------------------------------------------------------------------- 1 | # Oh, The Humanity! 2 | 3 | This is the eighth in the fourteen part series: 4 | 5 | * [Scheme in F#](intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * [Rinse and Recurse](recurse.md) 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * Oh, The Humanity! 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | ## set, begin, define 21 | 22 | Here we implement chapters 10 and 11 of [Bill Hails’ book](http://billhails.net/Book/). We’re about to do something hideous and horrible to the language (and to our interpreter). We’re about to add assignment. This is a travesty to a pure functional programmer, but this whole thing is a learning experience and we’re not going to shy away from exploring many paradigms (I can’t wait to get to Prolog-like logic programming later). 23 | 24 | ## Assignment 25 | 26 | Destructive assignment is done with ‘set!’ (“set bang”) in Scheme. Given a symbol and an expression, it finds the symbol in the environment (potentially walking the frames) and assigns the symbol a new value. It’s the first primitive we have that actually doesn’t return any useful value. To a pure functional programmer this is a very strange function. It takes arguments and returns nothing useful! It’s called for its side effects. In our case, we’ll have it return a dummy value, just for display in the REPL. 27 | 28 | ``` fsharp 29 | and Set env = function 30 | | [Symbol(s); e] –> 31 | (lookup env s) := eval env e 32 | Dummy(sprintf "Set %s" s) 33 | | _ -> failwith "Malformed 'set!'." 34 | ``` 35 | 36 | With this we can say `(set! x 5)` or `(set! x (- 7 2))` and `x` will be bound to `5`; replacing any previous binding. It’s not like nested `let`s with which you can shadow the name `x` with a new binding. This is destructively replacing the binding in place. It’s won’t be restored when the current scope is popped. 37 | 38 | ## Sequencing 39 | 40 | With assignment we are entering the imperative programming paradigm. To a “normal” C# or Java dev, this style of executing a sequence of statements for their side effects seems perfectly natural. In C#, for example, there is no special syntax to construct sequences of statements. Simple juxtaposition implies it, or I suppose you could say the simicolon is kind of the “sequencing operator”. To a pure functional programmer statements (as opposed to expressions) are the most unnatural thing in the world. In Scheme we’ll have to use an explicit sequencing operation called ‘begin’. 41 | 42 | ``` fsharp 43 | and Begin env = List.fold (fun _ e -> eval env e) (Dummy("Empty 'begin'")) 44 | ``` 45 | 46 | It takes a list of statements/expressions and evaluates them in turn. The return value of the whole expression is that of the last expression in the list. For example, `(begin (* 2 3) (* 4 5))` will yield `20`. What happens to the `(* 2 3)`? Well, it’s evaluated but the `6` is dropped on the floor; no use in that. It’s meant to be used with side-effecting statements like ‘set!’ that are called for their effects, not their values. For example, `(let ((a 1)) (begin (set! a 2) (* a 10)))` yields `20`. 47 | 48 | ## Definitions 49 | 50 | While `set!` works to update existing bindings, `define` is used to update existing environment frames themselves with entirely new name/value pairs. It’s primarily used in top-level code to define a set of library functions. It’s nice also at the REPL to be able to define things and then use them rather that constructing these giant `let` expressions to set everything up for the body to make sense. To allow updating whole frames in place, we had to change them to refs and patch up a few things (the extend and lookup functions, etc.). I’m skipping some details. 51 | 52 | ``` fsharp 53 | and Define (env : Environment) = function 54 | | [Symbol(s); e] –> 55 | let def = ref (Dummy("Dummy 'define'")) 56 | env.Head := Map.add s def env.Head.Value 57 | def := eval env e 58 | Dummy(sprintf "Defined %s" s) 59 | | _ -> failwith "Malformed 'define'." 60 | ``` 61 | 62 | Something like `letrec`, it first populates the current environment frame with a dummy value, then evaluates the bound expression against this, then swaps in the result. This allows for recursive definitions like `(define fac (lambda (x) (if x (* x (fac (- x 1))) 1)))`. 63 | 64 | That’s about it for this set of horrible features. With them we’ve enabled imperative programming but we’ve broken [referential transparency](http://en.wikipedia.org/wiki/Referential_transparency_(computer_science)) and ruined the language. Congrats! :-) 65 | 66 | ## Tests 67 | 68 | ``` fsharp 69 | case "(let ((a 1)) (begin (set! a 2) a))" "2" // begin and assign 70 | case "(let* ((a 5) (dummy (set! a 10))) a)" "10" // re-assign after let 71 | case "(begin (define fac (lambda (x) (if x (* x (fac (- x 1))) 1))) (fac 7))" "5040" // define recursive 72 | case "(begin (define square (lambda (x) (* x x))) (square 4))" "16" // global def 73 | case "(let ((x 4)) (begin (define y 8) (* x y))))" "32" // local def 74 | ``` 75 | 76 | ## Next: [Language vs. Library](library.md) 77 | -------------------------------------------------------------------------------- /Docs/recurse.md: -------------------------------------------------------------------------------- 1 | # Rinse and Recurse 2 | 3 | This is the fourth in the fourteen part series: 4 | 5 | * [Scheme in F#](Docs/intro.md) 6 | * [Just 'let' Me Be!](let.md) 7 | * [Lambda the Ultimate!](lambda.md) 8 | * Rinse and Recurse 9 | * [What 'letrec' Can't Do](letstar.md) 10 | * [What's Lisp Without Lists?!](lists.md) 11 | * [No Wait, Macro the Ultimate!](macros.md) 12 | * [Oh, The Humanity!](mutation.md) 13 | * [Language vs. Library](library.md) 14 | * [Turning Your Brain Inside Out With Continuations](continuations.md) 15 | * [Playing Dice with the Universe](amb.md) 16 | * [Functional I/O (or at least "O")](functional_o.md) 17 | * [Functional I/O (including "I" this time)](functional_i.md) 18 | * [Historical Debugging](debugging.md) 19 | 20 | ## Recursive ‘let’ 21 | 22 | Normal `let` can’t be used to bind names to recursive expressions (ones referring back to the names) because the expressions end up being evaluated in the calling environment before it’s been extended with the names themselves. It’s something of a chicken and egg problem. There are a few ways to solve it but the technique I’ve seen often (in [SICP](http://mitpress.mit.edu/sicp/), [LispKit](http://www.amazon.com/gp/product/0133315797/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0133315797&linkCode=as2&tag=bookporn-20&linkId=EMSSTA7BLP7RGXTR), [Lisp in Small Pieces](http://www.amazon.com/gp/product/0521545668/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0521545668&linkCode=as2&tag=bookporn-20&linkId=KKJXEREGJXRVJZKW), and this [Bill Hails book](http://www.amazon.com/gp/product/0521545668/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0521545668&linkCode=as2&tag=bookporn-20&linkId=KKJXEREGJXRVJZKW) we’re following) is to: 23 | 24 | 1. Create a dummy environment with names but unusable values 25 | 2. Eval expressions in this dummy environment 26 | 3. Swap out the dummy values with these actual results 27 | 28 | This trick works as long as the expressions don’t actually crack open the dummy values. That is exactly the case with `lambda`. Remember it creates a new function; capturing the environment but not actually using it until the function is called. So we’ll make a new ‘letrec’ for binding recursive lambda expressions. 29 | 30 | ``` fsharp 31 | and LetRec env = function 32 | | [List(bindings); body] –> 33 | let dummy = Function(fun _ -> failwith "Cannot evaluate dummy values.") 34 | let bind = function List([Symbol(s); _]) -> s, ref dummy | _ -> failwith "Malformed 'letrec' binding." 35 | let env' = List.map bind bindings |> extend env 36 | // now update dummy env - assumes dummy env will be captured but not actually accessed (e.g. lambda) 37 | let update = function List([Symbol(s); e]) -> (env'.Head.Item s) := (eval env' e) 38 | | _ -> failwith "Malformed 'letrec' binding." 39 | List.iter update bindings 40 | eval env' body 41 | | _ -> failwith "Malformed LetRec." 42 | ``` 43 | 44 | Notice that in `update` we have to do a destructive update (with `:=`) to swap out the dummy values! Maybe we’ll come back later and redo without resorting to mutation… 45 | 46 | ## Mutable Environment 47 | 48 | As much as I hate mutation, I think we need it to accomplish the swapping out in step 3 above. There are ways to do it without mutation but it’s by far the most straight forward and will come in handy when we later add ‘set!’ to the language itself. First we’ll go through and change the environment bindings to refs, patch up a couple places to expect that and go ahead and add our new `letrec` to the global environment (note the addition of `ref` through): 49 | 50 | ``` fsharp 51 | type Expression = 52 | | Number of BigInteger 53 | | String of string 54 | | Symbol of string 55 | | List of Expression list 56 | | Function of (Expression list -> Expression) 57 | | Special of (Map list -> Expression list -> Expression) 58 | and Let env = function 59 | | [List(bindings); body] –> 60 | let bind = function List([Symbol(s); e]) -> s, ref (eval env e) | _ -> failwith "Malformed 'let' binding." 61 | let env' = List.map bind bindings |> extend env 62 | eval env' body 63 | | _ -> failwith "Malformed Let." 64 | 65 | and Lambda env = function 66 | | [List(parameters); body] –> 67 | let closure env' args = 68 | // bind parameters to actual arguments (evaluated in the caller's environment) 69 | let bindings = List.zip parameters args 70 | let bind = function Symbol(p), a -> p, ref (eval env' a) | _ -> failwith "Malformed 'lambda' parameter." 71 | let env'' = List.map bind bindings |> extend env // extend the captured definition-time environment 72 | eval env'' body 73 | Special(closure) 74 | | _ -> failwith "Malformed Lambda." 75 | 76 | and environment = 77 | extend [] [ 78 | "*", ref (Function(Multiply)) 79 | "-", ref (Function(Subtract)) 80 | "if", ref (Special(If)) 81 | "let", ref (Special(Let)) 82 | "letrec", ref (Special(LetRec)) 83 | "lambda", ref (Special(Lambda))] 84 | 85 | and eval env expression = 86 | match expression with 87 | | Expression.Number(_) as lit –> lit 88 | | Expression.String(_) as lit –> lit 89 | | Expression.Symbol(s) -> (lookup env s).Value 90 | | Expression.List(h :: t) –> 91 | match eval env h with 92 | | Function(f) -> apply env f t 93 | | Special(f) -> f env t 94 | | _ -> failwith "Malformed expression." 95 | | _ -> failwith "Malformed expression." 96 | ``` 97 | 98 | ## Tests 99 | 100 | All the old tests still pass. Just one new one: 101 | 102 | ``` fsharp 103 | case "(letrec ((factorial (lambda (n) (if n (* n (factorial (- n 1))) 1)))) (factorial 4))" "24" // letrec and recursion 104 | ``` 105 | 106 | ## Next: [What 'letrec' Can't Do](letstar.md) 107 | -------------------------------------------------------------------------------- /FScheme.fs: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Ashley Nathan Feniello 2 | 3 | module FScheme 4 | 5 | open System 6 | open System.Numerics 7 | open System.IO 8 | 9 | type Token = 10 | | Open | Close 11 | | Quote | Unquote 12 | | Number of string 13 | | String of string 14 | | Symbol of string 15 | 16 | let tokenize source = 17 | let rec string acc = function 18 | | '\\' :: '"' :: t -> string (acc + "\"") t // escaped quote becomes quote 19 | | '\\' :: 'b' :: t -> string (acc + "\b") t // escaped backspace 20 | | '\\' :: 'f' :: t -> string (acc + "\f") t // escaped formfeed 21 | | '\\' :: 'n' :: t -> string (acc + "\n") t // escaped newline 22 | | '\\' :: 'r' :: t -> string (acc + "\r") t // escaped return 23 | | '\\' :: 't' :: t -> string (acc + "\t") t // escaped tab 24 | | '\\' :: '\\' :: t -> string (acc + "\\") t // escaped backslash 25 | | '"' :: t -> acc, t // closing quote terminates 26 | | c :: t -> string (acc + (c.ToString())) t // otherwise accumulate chars 27 | | _ -> failwith "Malformed string." 28 | let rec comment = function 29 | | '\r' :: t | '\n' :: t -> t // terminated by line end 30 | | [] -> [] // or by EOF 31 | | _ :: t -> comment t 32 | let rec token acc = function 33 | | (')' :: _) as t -> acc, t // closing paren terminates 34 | | w :: t when Char.IsWhiteSpace(w) -> acc, t // whitespace terminates 35 | | [] -> acc, [] // end of list terminates 36 | | c :: t -> token (acc + (c.ToString())) t // otherwise accumulate chars 37 | let rec tokenize' acc = function 38 | | w :: t when Char.IsWhiteSpace(w) -> tokenize' acc t // skip whitespace 39 | | '(' :: t -> tokenize' (Open :: acc) t 40 | | ')' :: t -> tokenize' (Close :: acc) t 41 | | '\'' :: t -> tokenize' (Quote :: acc) t 42 | | ',' :: t -> tokenize' (Unquote :: acc) t 43 | | ';' :: t -> comment t |> tokenize' acc // skip over comments 44 | | '"' :: t -> // start of string 45 | let s, t' = string "" t 46 | tokenize' (Token.String(s) :: acc) t' 47 | | '-' :: d :: t when Char.IsDigit(d) -> // start of negative number 48 | let n, t' = token ("-" + d.ToString()) t 49 | tokenize' (Token.Number(n) :: acc) t' 50 | | '+' :: d :: t | d :: t when Char.IsDigit(d) -> // start of positive number 51 | let n, t' = token (d.ToString()) t 52 | tokenize' (Token.Number(n) :: acc) t' 53 | | s :: t -> // otherwise start of symbol 54 | let s, t' = token (s.ToString()) t 55 | tokenize' (Token.Symbol(s) :: acc) t' 56 | | [] -> List.rev acc // end of list terminates 57 | tokenize' [] source 58 | 59 | type Expression = 60 | | Number of BigInteger 61 | | String of string 62 | | Symbol of string 63 | | List of Expression list 64 | | Function of (Continuation -> Expression list -> Expression) 65 | | Special of (Continuation -> Environment -> Expression list -> Expression) 66 | | Current of Continuation 67 | | Dummy of string 68 | and Continuation = Expression -> Expression 69 | and Frame = Map ref 70 | and Environment = Frame list 71 | 72 | let parse source = 73 | let map = function 74 | | Token.Number(n) -> Expression.Number(BigInteger.Parse(n)) 75 | | Token.String(s) -> Expression.String(s) 76 | | Token.Symbol(s) -> Expression.Symbol(s) 77 | | _ -> failwith "Syntax error." 78 | let rec list f t acc = 79 | let e, t' = parse' [] t 80 | parse' (List(f e) :: acc) t' 81 | and parse' acc = function 82 | | Open :: t -> list id t acc 83 | | Close :: t -> (List.rev acc), t 84 | | Quote :: Open :: t -> list (fun e -> [Symbol("quote"); List(e)]) t acc 85 | | Quote :: h :: t -> parse' (List([Symbol("quote"); map h]) :: acc) t 86 | | Unquote :: Open :: t -> list (fun e -> [Symbol("unquote"); List(e)]) t acc 87 | | Unquote :: h :: t -> parse' (List([Symbol("unquote"); map h]) :: acc) t 88 | | h :: t -> parse' ((map h) :: acc) t 89 | | [] -> (List.rev acc), [] 90 | let result, _ = parse' [] (tokenize source) 91 | result 92 | 93 | let rec print = function 94 | | List(Dummy(_) :: _) -> "" // don't print accumulated statement dummy values 95 | | List(list) -> "(" + String.Join(" ", (List.map print list)) + ")" 96 | | String(s) | Symbol(s) -> s 97 | | Number(n) -> n.ToString() 98 | | Function(_) | Special(_) | Current(_) -> "Function" 99 | | Dummy(_) -> "" // sometimes useful to emit value for debugging, but normally we ignore 100 | 101 | let malformed n e = sprintf "Malformed '%s': %s" n (print (List([e]))) |> failwith 102 | 103 | let math ident unary op name cont = function 104 | | [] -> Number(ident) |> cont // (op) == 0 105 | | [Number(n)] -> Number(unary * n) |> cont // (op a) == -a or +a 106 | | Number(n) :: ns -> // (op a b c) == a op b op c 107 | let op' a = function Number(b) -> op a b | m -> malformed (sprintf "%s arg" name) m 108 | Number(List.fold op' n ns) |> cont 109 | | m -> malformed name (List(m)) 110 | 111 | let Add = math 0I 1I (+) "addition" 112 | let Subtract = math 0I -1I (-) "subtraction" 113 | let Multiply = math 1I 1I (*) "multiplication" 114 | let Divide = math 1I 1I (/) "division" 115 | let Modulus = math 1I 1I (%) "modulus" 116 | 117 | let compare pred name cont = function 118 | | [Number(a); Number(b)] -> (if pred a b then Number(1I) else Number(0I)) |> cont 119 | | m -> malformed name (List(m)) 120 | 121 | let Equal = compare (=) "equality" 122 | let Greater = compare (>) "greater" 123 | let Less = compare (<) "less" 124 | 125 | let extend env bindings = (ref (Map.ofList bindings) :: env) 126 | let lookup env symbol = 127 | match List.tryPick (fun (frame : Frame) -> Map.tryFind symbol frame.Value) env with 128 | | Some(e) -> e 129 | | None -> sprintf "No binding for '%s'." symbol |> failwith 130 | 131 | let zip args parameters = 132 | let args' = // passing more args than params results in last param treated as list 133 | let plen = List.length parameters 134 | if List.length args = plen then args else 135 | let split ts = ts (plen - 1) args |> List.ofSeq 136 | split Seq.take @ [List(split Seq.skip)] 137 | List.zip parameters args' 138 | 139 | let mutable backtrack = [] // ambivalent back stack 140 | 141 | let rec If cont env = function 142 | | [condition; t; f] -> 143 | eval (function 144 | | List([]) | String("") -> eval cont env f // empty list or empty string is false 145 | | Number(n) when n = 0I -> eval cont env f // zero is false 146 | | _ -> eval cont env t) env condition // everything else is true 147 | | m -> malformed "if" (List(m)) 148 | 149 | and Let cont env = function 150 | | [List(bindings); body] -> 151 | let rec mapbind acc = function 152 | | List([Symbol(s); e]) :: t -> eval (fun x -> mapbind ((s, ref x) :: acc) t) env e 153 | | [] -> 154 | let frame = List.rev acc 155 | let env' = extend env frame 156 | eval cont env' body 157 | | _ -> failwith "Malformed 'let' binding." 158 | mapbind [] bindings 159 | | m -> malformed "let" (List(m)) 160 | 161 | and LetRec cont env = function 162 | | [List(bindings); body] -> 163 | let bind = function List([Symbol(s); _]) -> s, ref (Dummy("Dummy 'letrec'")) | m -> malformed "letrec binding" m 164 | let env' = List.map bind bindings |> extend env 165 | let frame = env'.Head.Value 166 | // now update dummy env - assumes dummy env will be captured but not actually accessed (e.g. lambda) 167 | let rec mapupdate = function 168 | | List([Symbol(s); e]) :: t -> eval (fun x -> (frame.Item s) := x; mapupdate t) env' e 169 | | [] -> eval cont env' body 170 | | _ -> failwith "Malformed 'let' binding." 171 | mapupdate bindings 172 | | m -> malformed "letrec" (List(m)) 173 | 174 | and LetStar cont env = function 175 | | [List(bindings); body] -> 176 | let rec foldbind env' = function 177 | | List([Symbol(s); e]) :: t -> eval (fun x -> foldbind ([s, ref x] |> extend env') t) env' e 178 | | [] ->eval cont env' body 179 | | _ -> failwith "Malformed 'let*' binding." 180 | foldbind env bindings 181 | | m -> malformed "let*" (List(m)) 182 | 183 | and Lambda cont env = function 184 | | [List(parameters); body] -> 185 | let closure cont' env' args = 186 | // bind parameters to actual arguments (evaluated in the caller's environment) 187 | let rec mapbind acc = function 188 | | (Symbol(p), a) :: t -> eval (fun x -> mapbind ((p, ref x) :: acc) t) env' a 189 | | [] -> 190 | let env'' = List.rev acc |> extend (env @ env') // extend the captured definition-time environment 191 | eval cont' env'' body 192 | | _ -> failwith "Malformed lambda param." 193 | mapbind [] (zip args parameters) 194 | Special(closure) |> cont 195 | | m -> malformed "lambda" (List(m)) 196 | 197 | and Cat cont = function [List(a); List(b)] -> List(a @ b) |> cont | m -> malformed "cat" (List(m)) 198 | and Cons cont = function [h; List(t)] -> (List(h :: t)) |> cont | m -> malformed "cons" (List(m)) 199 | and Car cont = function [List(h :: _)] -> h |> cont | m -> malformed "car" (List(m)) 200 | and Cdr cont = function [List(_ :: t)] -> List(t) |> cont | m -> malformed "cdr" (List(m)) 201 | 202 | and Quote cont env = 203 | let rec unquote cont' = function 204 | | List([Symbol("unquote"); e]) -> eval cont' env e 205 | | List(Symbol("unquote") :: _) as m -> malformed "unquote (too many args)" m 206 | | List(lst) -> 207 | let rec mapunquote acc = function 208 | | h' :: t' -> 209 | unquote (fun x -> mapunquote (x :: acc) t') h' 210 | | [] -> List(List.rev acc) 211 | mapunquote [] lst |> cont' 212 | | e -> cont' e 213 | function [e] -> unquote cont e | m -> malformed "quote" (List(m)) 214 | 215 | and Eval cont env = function [args] -> args |> eval (eval cont env) env | m -> malformed "eval" (List(m)) 216 | 217 | and Macro cont env = function 218 | | [List(parameters); body] -> 219 | let closure cont' env' args = 220 | // bind parameters to actual arguments (but unevaluated, unlike lambda) 221 | let bind = function Symbol(p), a -> p, ref a | _, m -> malformed "macro parameter" m // bound unevaluated 222 | let env'' = zip args parameters |> List.map bind |> extend env // extend the captured definition-time environment 223 | eval (eval cont' env') env'' body 224 | Special(closure) |> cont 225 | | m -> malformed "macro" (List(m)) 226 | 227 | and Set cont env = function 228 | | [Symbol(s); e] -> eval (fun x -> (lookup env s) := x; Dummy(sprintf "Set %s" s) |> cont) env e 229 | | m -> malformed "set!" (List(m)) 230 | 231 | and Begin cont env = 232 | let rec foldeval last = function 233 | | h :: t -> eval (fun x -> foldeval x t) env h 234 | | [] -> last |> cont 235 | foldeval (Dummy("Empty 'begin'")) 236 | 237 | and Define cont (env : Environment) = function 238 | | [Symbol(s); e] -> 239 | let def = ref (Dummy("Dummy 'define'")) 240 | env.Head := Map.add s def env.Head.Value 241 | eval (fun x -> def := x; Dummy(sprintf "Defined %s" s) |> cont) env e 242 | | m -> malformed "define" (List(m)) 243 | 244 | and load file = Load (fun _ -> Dummy("")) [String(file)] |> ignore 245 | and Load cont = function 246 | | [String(file)] -> 247 | (File.OpenText(file)).ReadToEnd() |> List.ofSeq |> parse |> List.iter (eval (fun _ -> Dummy("Dummy 'load'")) environment >> ignore) 248 | Symbol(sprintf "Loaded '%s'." file) |> cont 249 | | m -> malformed "load" (List(m)) 250 | 251 | and Display cont = function 252 | | [e] -> print e |> printf "%s"; Dummy("Dummy 'display'") |> cont 253 | | m -> malformed "display" (List(m)) 254 | 255 | and CallCC cont env = function 256 | | [callee] -> eval (function Special(fn) -> fn cont env [Current(cont)] | m -> malformed "call/cc" m) env callee 257 | | m -> malformed "call/cc" (List(m)) 258 | 259 | and Ambivalent cont env args = 260 | match args with 261 | | choice :: t -> 262 | backtrack <- (fun () -> Ambivalent cont env t) :: backtrack 263 | eval cont env choice 264 | | [] -> 265 | match backtrack with 266 | | back :: t -> backtrack <- t; back () 267 | | [] -> printfn "No more solutions."; Dummy("Unsolvable") |> cont 268 | 269 | and environment = 270 | [ref (Map.ofList 271 | ["*", ref (Function(Multiply)) 272 | "/", ref (Function(Divide)) 273 | "%", ref (Function(Modulus)) 274 | "+", ref (Function(Add)) 275 | "-", ref (Function(Subtract)) 276 | "=", ref (Function(Equal)) 277 | ">", ref (Function(Greater)) 278 | "<", ref (Function(Less)) 279 | "if", ref (Special(If)) 280 | "let", ref (Special(Let)) 281 | "letrec", ref (Special(LetRec)) 282 | "let*", ref (Special(LetStar)) 283 | "lambda", ref (Special(Lambda)) 284 | "cat", ref (Function(Cat)) 285 | "cons", ref (Function(Cons)) 286 | "car", ref (Function(Car)) 287 | "cdr", ref (Function(Cdr)) 288 | "quote", ref (Special(Quote)) 289 | "eval", ref (Special(Eval)) 290 | "macro", ref (Special(Macro)) 291 | "set!", ref (Special(Set)) 292 | "begin", ref (Special(Begin)) 293 | "define", ref (Special(Define)) 294 | "load", ref (Function(Load)) 295 | "display", ref (Function(Display)) 296 | "call/cc", ref (Special(CallCC)) 297 | "amb", ref (Special(Ambivalent)) 298 | ])] 299 | 300 | and eval cont env expression = 301 | match expression with 302 | | Number(_) | String(_) | Current(_) as lit -> lit |> cont 303 | | Symbol(s) -> (lookup env s).Value |> cont 304 | | List(h :: t) -> 305 | eval (function 306 | | Function(f) -> apply cont env f t 307 | | Special(f) -> f cont env t 308 | | Current(f) -> match t with [rtn] -> f rtn | m -> malformed "call/cc args" (List(m)) 309 | | m -> malformed "expression" m) env h 310 | | Dummy(s) -> sprintf "Cannot evaluate dummy value: %s" s |> failwith 311 | | _ -> failwith "Malformed expression." 312 | 313 | and apply cont env fn args = 314 | let rec mapeval acc = function 315 | | h :: t -> eval (fun a -> mapeval (a :: acc) t) env h 316 | | [] -> fn cont (List.rev acc) 317 | mapeval [] args 318 | 319 | let rep env = 320 | let eval' = function 321 | | Symbol("?") -> 322 | match backtrack with 323 | | h :: t -> backtrack <- t; h () 324 | | [] -> printfn "No current problem."; Dummy("No problem") 325 | | e -> eval id env e 326 | List.ofSeq >> parse >> List.head >> eval' >> print 327 | 328 | let rec repl output = 329 | printf "%s\n> " output 330 | try Console.ReadLine() |> rep environment |> repl 331 | with ex -> repl ex.Message 332 | 333 | let test () = 334 | let case source expected = 335 | try 336 | let output = rep environment source 337 | if output <> expected then 338 | printf "TEST FAILED: %s [Expected: %s, Actual: %s]" source expected output 339 | with ex -> printf "TEST CRASHED: %s [%s]" ex.Message source 340 | case "\"hello\"" "hello" // strings 341 | case "\"\\\"\"" "\"" // return char 342 | case "\"\\b\"" "\b" // return char 343 | case "\"\\f\"" "\f" // return char 344 | case "\"\\n\"" "\n" // return char 345 | case "\"\\r\"" "\r" // return char 346 | case "\"\\t\"" "\t" // return char 347 | case "\"\\\\\"" "\\" // return char 348 | case "1" "1" // numbers 349 | case "+1" "1" // explicit positive numbers 350 | case "-1" "-1" // negative numbers 351 | case "(*)" "1" // multiplication 352 | case "(* 2)" "2" // multiplication 353 | case "(* 2 3)" "6" // multiplication 354 | case "(* 2 3 4)" "24" // multiplication 355 | case "(/)" "1" // division 356 | case "(/ 2)" "2" // division 357 | case "(/ 9 2)" "4" // division 358 | case "(/ 12 2 3)" "2" // division 359 | case "(%)" "1" // modulus 360 | case "(% 2)" "2" // modulus 361 | case "(% 9 2)" "1" // modulus 362 | case "(% 8 2)" "0" // modulus 363 | case "(% 26 7 3)" "2" // modulus 364 | case "(+)" "0" // strange addition case 365 | case "(+ 10)" "10" // explicit positive 366 | case "(+ 10 2)" "12" // addition 367 | case "(+ 10 2 3)" "15" // addition 368 | case "(-)" "0" // strange subtraction case 369 | case "(- 10)" "-10" // negation 370 | case "(- 10 2)" "8" // subtraction 371 | case "(- 10 2 3)" "5" // subtraction 372 | case "(if (* 0 1) 10 20)" "20" // if 373 | case "(if (* 1 1) 10 20)" "10" // if 374 | case "(if (* 1 1) 10 bomb)" "10" // if (special form) 375 | case "(* 1234567890987654321 1234567890987654321)" "1524157877457704723228166437789971041" // bigint math 376 | case "(let ((x 2)) x)" "2" // simple let 377 | case "(let ((a 00) (b 10) (c 20)) (if a b c))" "20" // conditional eval 378 | case "(let ((square (lambda (x) (* x x)))) (square 4))" "16" // lambda 379 | case "(let ((square (lambda (x) (* x x)))) square)" "Function" // print lambda 380 | case "(let ((times3 (let ((n 3)) (lambda (x) (* n x))))) (times3 4))" "12" // closure 381 | case "(let ((times3 (let ((makemultiplier (lambda (n) (lambda (x) (* n x))))) (makemultiplier 3)))) (times3 5))" "15" // higher order functions 382 | case "(letrec ((factorial (lambda (n) (if n (* n (factorial (- n 1))) 1)))) (factorial 4))" "24" // letrec and recursion 383 | case "(let ((a 1) (b 2)) (let ((a b) (b a)) b))" "1" // let binds in parallel (should work in earlier versions too) 384 | case "(let ((a 1) (b 2)) (let* ((a b) (b a)) b))" "2" // let* binds sequentially 385 | case "(let ((a 5)) (let ((b (* a 2))) (let ((c (- b 3))) c)))" "7" // poor-man's sequential expressions 386 | case "(let* ((a 5) (b (* a 2)) (c (- b 3))) c)" "7" // let* sequential expressions 387 | case "(list 1 2 3)" "(1 2 3)" // list 388 | case "(car (list 1 2 3))" "1" // car 389 | case "(cdr (list 1 2 3))" "(2 3)" // cdr 390 | case "(cat '(1 2) '(a b))" "(1 2 a b)" // cat 391 | case "(cat '(1 2) '())" "(1 2)" // cat 392 | case "(cat '() '(1 2))" "(1 2)" // cat 393 | case "(cons 1 (list 2 3))" "(1 2 3)" // cons 394 | case "(cons 1 (cons 2 (cons 3 nil)))" "(1 2 3)" // cons x3 395 | case "(let ((a 1) (b 2) (c 3)) (list a b c))" "(1 2 3)" // list 396 | case "(let ((a (list 1 2 3))) (car a))" "1" // car 397 | case "(let ((a (list 1 2 3))) (cdr a))" "(2 3)" // cdr 398 | case "(quote (* 2 3))" "(* 2 3)" // quote primitive 399 | case "'(* 2 3)" "(* 2 3)" // quote primitive with sugar 400 | case "(eval '(* 2 3))" "6" // eval quoted expression 401 | case "(quote (* 2 (- 5 2)))" "(* 2 (- 5 2))" // quote nested 402 | case "(quote (* 2 (unquote (- 5 2))))" "(* 2 3)" // quote nested unquote 403 | case "'(* 2 ,(- 5 2))" "(* 2 3)" // quote nested unquote with sugar 404 | case "(quote (quote 1 2 3))" "(quote 1 2 3)" // quote special form 405 | case "(let ((x 'rain) (y 'spain) (z 'plain)) '(the ,x in ,y falls mainly on the ,z))" 406 | "(the rain in spain falls mainly on the plain)" // quote/unquote 407 | case "(let ((or (macro (a b) '(if ,a 1 (if ,b 1 0))))) (or 1 BOOM))" "1" // macro as special form 408 | case "(let ((and (macro (a b) '(if ,a (if ,b 1 0) 0)))) (and 0 BOOM))" "0" // macro as special form 409 | case "(let ((a 1)) (begin (set! a 2) a))" "2" // begin and assign 410 | case "(let* ((a 5) (dummy (set! a 10))) a)" "10" // re-assign after let 411 | case "(begin (define fac (lambda (x) (if x (* x (fac (- x 1))) 1))) (fac 7))" "5040" // define recursive 412 | case "(begin (define square (lambda (x) (* x x))) (square 4))" "16" // global def 413 | case "(let ((x 4)) (begin (define y 8) (* x y))))" "32" // local def 414 | case "(and 0 0)" "0" // or (false) 415 | case "(and 1 0)" "0" // or (false) 416 | case "(and 0 1)" "0" // or (false) 417 | case "(and 1 1)" "1" // or (true) 418 | case "(or 0 0)" "0" // or (false) 419 | case "(or 1 0)" "1" // or (true) 420 | case "(or 0 1)" "1" // or (true) 421 | case "(or 1 1)" "1" // or (true) 422 | case "(not? 0)" "1" // or (true) 423 | case "(not? 1)" "0" // or (false) 424 | case "(xor 0 0)" "0" // xor (false) 425 | case "(xor 1 0)" "1" // xor (true) 426 | case "(xor 0 1)" "1" // xor (true) 427 | case "(xor 1 1)" "0" // xor (false) 428 | case "(let ((square (lambda (x) (* x x)))) (map square '(1 2 3 4 5 6 7 8 9)))" "(1 4 9 16 25 36 49 64 81)" // mapping 429 | case "(let ((square (lambda (x) (* x x)))) (map square '(9)))" "(81)" // mapping single 430 | case "(let ((square (lambda (x) (* x x)))) (map square '()))" "()" // mapping empty 431 | case "(fold * 1 '(2 3 4 5))" "120" // fold 432 | case "(reverse '(1 2 3))" "(3 2 1)" // reverse 433 | case "(call/cc (lambda (c) (c 10)))" "10" // super-simple call/cc 434 | case "(call/cc (lambda (c) (if (c 10) 20 30)))" "10" // call/cc bailing out of 'if' 435 | case "(+ 8 (call/cc (lambda (k^) (* (k^ 5) 100))))" "13" // call/cc bailing out of multiplication 436 | case "(* (+ (call/cc (lambda (k^) (/ (k^ 5) 4))) 8) 3)" "39" // call/cc nesting 437 | -------------------------------------------------------------------------------- /FScheme.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {5fb07b89-9fdd-4595-b591-7ae282411e30} 9 | Exe 10 | FScheme 11 | FScheme 12 | v4.0 13 | Client 14 | FScheme 15 | 16 | 17 | true 18 | full 19 | false 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | 3 24 | x86 25 | 26 | 27 | pdbonly 28 | true 29 | true 30 | bin\Release\ 31 | TRACE 32 | 3 33 | x86 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | PreserveNewest 42 | 43 | 44 | Always 45 | 46 | 47 | Always 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 66 | -------------------------------------------------------------------------------- /FScheme.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FScheme", "FScheme.fsproj", "{5FB07B89-9FDD-4595-B591-7AE282411E30}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|x86 = Debug|x86 9 | Release|x86 = Release|x86 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {5FB07B89-9FDD-4595-B591-7AE282411E30}.Debug|x86.ActiveCfg = Debug|x86 13 | {5FB07B89-9FDD-4595-B591-7AE282411E30}.Debug|x86.Build.0 = Debug|x86 14 | {5FB07B89-9FDD-4595-B591-7AE282411E30}.Release|x86.ActiveCfg = Release|x86 15 | {5FB07B89-9FDD-4595-B591-7AE282411E30}.Release|x86.Build.0 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Main.fs: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Ashley Nathan Feniello 2 | 3 | open FScheme 4 | 5 | load "Prelude.scm" 6 | 7 | World.init () 8 | 9 | test () 10 | repl "Welcome to FScheme" -------------------------------------------------------------------------------- /Pong.scm: -------------------------------------------------------------------------------- 1 | (define pongworld (lambda (fn world) 2 | (let ((ball (car world)) 3 | (mouse (cadr world))) 4 | (fn (car ball) (cadr ball) ; position x/y 5 | (caddr ball) (cadddr ball) ; delta x/y 6 | (cadr mouse))))) ; paddle position y 7 | 8 | (define bounce (lambda (i di) 9 | (if (or (and (> di 0) (> i 30)) ; heading toward right/bottom and off edge 10 | (and (> 0 di) (> 1 i))) ; or heading toward left/top and off edge 11 | (- di) di))) ; reverse direction 12 | 13 | (define paddle (lambda (x y color) (map 14 | (lambda (d) '((,x ,(+ y d)) ,color)) 15 | '(-2 -1 0 1 2)))) 16 | 17 | (define init (lambda (_) '(1 14 1 1 14))) 18 | 19 | (define tick (lambda (world) 20 | (pongworld (lambda (x y dx dy p) 21 | (if (and (> x 30) ; off player's edge? 22 | (or (< (+ y 3) p) (> (- y 3) p))) ; not hitting paddle? 23 | '(1 14 1 1 ,p) ; reset - except p, else move ball as usual... 24 | '(,(+ x dx) ,(+ y dy) ; increment position 25 | ,(bounce x dx) ,(bounce y dy) ; bounce off edges 26 | ,p 27 | ))) world))) 28 | 29 | (define draw (lambda (world) 30 | (let ((red '(255 0 0)) 31 | (green '(0 255 0)) 32 | (blue '(0 0 255)) 33 | (p (caddddr (car world)))) 34 | (pongworld (lambda (x y _ _ _) (cat (cat 35 | '(((,x ,y) ,green)) ; ball 36 | (paddle 0 y red)) ; computer's paddle 37 | (paddle 31 p blue) ; player's paddle 38 | )) world)))) -------------------------------------------------------------------------------- /Prelude.scm: -------------------------------------------------------------------------------- 1 | ; logical 'and', 'or', 'not', 'xor' 2 | (define and (macro (a b) '(if ,a (if ,b 1 0) 0))) 3 | (define or (macro (a b) '(if ,a 1 (if ,b 1 0)))) 4 | (define not? (lambda (x) (if x 0 1))) 5 | (define xor (lambda (a b) (and (or a b) (not? (and a b))))) 6 | 7 | (define nil '()) 8 | 9 | ; map function (f) over list (xs) 10 | (define map (lambda (f xs) ; apply f to each element of xs 11 | (if xs ; if not empty then 12 | (cons (f (car xs)) ; cons f of the head... 13 | (map f (cdr xs))) ; onto result of recursing down the tail 14 | nil))) ; otherwise return empty 15 | 16 | (define list (macro (xs) '(map eval (quote ,xs)))) 17 | 18 | ; fold function (f) over list (xs) while accumulating (a) 19 | (define fold (lambda (f a xs) 20 | (if (not? xs) a 21 | (fold f (f (car xs) a) (cdr xs))) 22 | )) 23 | 24 | (define reverse (lambda (xs) (fold cons nil xs))) 25 | 26 | (define newline (lambda () (display "\r\n"))) 27 | 28 | (define while 29 | (macro (test body) 30 | '(letrec 31 | ((loop 32 | (lambda () 33 | (if ,test 34 | (begin ,body (loop)) 35 | nil)))) 36 | (loop)))) 37 | 38 | ; simple continuation to top-level 39 | (define escape nil) 40 | (call/cc (lambda (c) (set! escape c))) 41 | 42 | ; error mechanism - print message and break out to top-level 43 | (define error (lambda (msg) (begin (display msg) (escape nil)))) 44 | 45 | (define sum (lambda (xs) (fold + 0 xs))) 46 | 47 | (define odd? (lambda (x) (% x 2))) 48 | (define even? (lambda (x) (not? (odd? x)))) 49 | 50 | (define require (lambda (e) (if e e (amb)))) 51 | 52 | (define member? (lambda (item lst) 53 | (if lst 54 | (if (= item (car lst)) 55 | 1 56 | (member? item (cdr lst))) 57 | 0))) 58 | 59 | (define distinct? (lambda (lst) 60 | (if lst 61 | (if (member? (car lst) (cdr lst)) 62 | 0 63 | (distinct? (cdr lst))) 64 | 1))) 65 | 66 | (define exclude (lambda (items lst) 67 | (if lst 68 | (if (member? (car lst) items) 69 | (exclude items (cdr lst)) 70 | (cons (car lst) (exclude items (cdr lst)))) 71 | ()))) 72 | 73 | (define >= (lambda (a b) (or (> a b) (= a b)))) 74 | (define <= (lambda (a b) (or (< a b) (= a b)))) 75 | 76 | (define cadr (lambda (xs) (car (cdr xs)))) 77 | (define caddr (lambda (xs) (cadr (cdr xs)))) 78 | (define cadddr (lambda (xs) (caddr (cdr xs)))) 79 | (define caddddr (lambda (xs) (cadddr (cdr xs)))) 80 | (define cadddddr (lambda (xs) (caddddr (cdr xs)))) 81 | (define caddddddr (lambda (xs) (cadddddr (cdr xs)))) 82 | 83 | (define fst car) 84 | (define snd cadr) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FScheme 2 | ======= 3 | 4 | Scheme interpreter in F# 5 | 6 | See [~~blog series~~](http://blogs.msdn.com/b/ashleyf/archive/tags/fscheme/) - Blog series moved here to GitHub: 7 | 8 | * [Scheme in F#](Docs/intro.md) 9 | * [Just 'let' Me Be!](Docs/let.md) 10 | * [Lambda the Ultimate!](Docs/lambda.md) 11 | * [Rinse and Recurse](Docs/recurse.md) 12 | * [What 'letrec' Can't Do](Docs/letstar.md) 13 | * [What's Lisp Without Lists?!](Docs/lists.md) 14 | * [No Wait, Macro the Ultimate!](Docs/macros.md) 15 | * [Oh, The Humanity!](Docs/mutation.md) 16 | * [Language vs. Library](Docs/library.md) 17 | * [Turning Your Brain Inside Out With Continuations](Docs/continuations.md) 18 | * [Playing Dice with the Universe](Docs/amb.md) 19 | * [Functional I/O (or at least "O")](Docs/functional_o.md) 20 | * [Functional I/O (including "I" this time)](Docs/functional_i.md) 21 | * [Historical Debugging](Docs/debugging.md) 22 | 23 | Setup 24 | 25 | Everything is written in F# and uses solution (`.sln`) and project (`.fsproj`) files compatible with Visual Studio, Xamarin or plain `xbuild`. I personally have been using plain Vim (with the [excellent F# bindings](https://github.com/fsharp/fsharpbinding)). Here's setup steps for Ubuntu: 26 | 27 | **Install F#** 28 | 29 | sudo apt-get update 30 | sudo apt-get install mono-complete 31 | sudo apt-get install fsharp 32 | 33 | **Pull down the project** 34 | 35 | git clone http://github.com/AshleyF/FScheme 36 | 37 | **Build** 38 | 39 | xbuild FScheme.sln 40 | 41 | This produces an executable (`FScheme.exe`) within `bin/` 42 | 43 | **Have fun!** 44 | -------------------------------------------------------------------------------- /World.fs: -------------------------------------------------------------------------------- 1 | module World 2 | 3 | open System.Drawing 4 | open System.Windows.Forms 5 | open System.Numerics 6 | open System.Threading 7 | open System.Collections.Generic 8 | open FScheme 9 | 10 | let mutable history = new LinkedList() 11 | let mutable current = new LinkedListNode(Symbol("Dummy world")) 12 | 13 | let mouseX = ref 0 14 | let mouseY = ref 0 15 | let events () = List([Number(bigint mouseX.Value); Number(bigint mouseY.Value)]) 16 | 17 | let eval' name = eval id environment (List([Symbol(name); List([Symbol("quote"); List([current.Value; events ()])])])) 18 | 19 | let w = 32 20 | let h = 32 21 | let s = 8 // pixel size 22 | let bmp = new Bitmap(w * s, h * s) 23 | let paint () = lock bmp (fun () -> 24 | use gc = Graphics.FromImage(bmp) 25 | gc.Clear(Color.Black) |> ignore 26 | match eval' "draw" with 27 | | List(pixels) -> 28 | let fill = function 29 | | List([List([Number(x); Number(y)]); List([Number(r); Number(g); Number(b)])]) -> 30 | gc.FillRectangle(new SolidBrush(Color.FromArgb(0xFF, int r, int g, int b)), int x * s, int y * s, s - 1, s - 1) 31 | | _ -> () 32 | List.iter fill pixels 33 | | _ -> failwith "Malformed graphical output.") 34 | 35 | type Form() = 36 | inherit System.Windows.Forms.Form() 37 | override x.OnPaintBackground _ = () // no flicker 38 | let form () = 39 | let f = new Form(Text = "Canvas", Width = w * s + 16 - 1, Height = h * s + 38 - 1, Visible = true) 40 | let running = ref false 41 | let time slice = 42 | running := false 43 | if slice <> null then 44 | current <- slice 45 | paint (); f.Refresh() 46 | let debug = function 47 | | Keys.S -> running := false 48 | | Keys.G -> running := true 49 | | Keys.Left -> time current.Previous 50 | | Keys.Right -> time current.Next 51 | | Keys.W -> print current.Value |> printfn "World: %s" 52 | | _ -> () 53 | let t = new Thread(new ThreadStart(fun () -> 54 | while true do 55 | if running.Value then 56 | current <- history.AddAfter(current, eval' "tick") 57 | paint (); f.Refresh() 58 | Thread.Sleep(33))) 59 | t.Start() 60 | f.Paint.Add(fun a -> lock bmp (fun () -> a.Graphics.DrawImage(bmp, 0, 0))) 61 | f.MouseMove.Add(fun a -> mouseX := a.X / s; mouseY := a.Y / s) 62 | f.KeyDown.Add(fun a -> debug a.KeyCode) 63 | f.Closing.Add(fun _ -> t.Abort()) 64 | current <- history.AddFirst(eval' "init") 65 | running := true 66 | f 67 | 68 | let run cont _ = Application.Run(form ()); Dummy("Dummy 'run'.") |> cont 69 | 70 | let init () = environment.Head := environment.Head.Value.Add("run", ref (Function(run))) -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | --------------------------------------------------------------------------------