[00:00.000 --> 00:11.040] Welcome to my talk, Lisp Scheme, Powerful Introspection and Extensibility. [00:11.040 --> 00:16.320] My name is Jakub Tienkiewicz, you can find me online with my handle Jacobik, I am a [00:16.320 --> 00:18.560] senior software developer from Poland. [00:18.560 --> 00:23.200] I focus mostly on JavaScript language, I am open source developer and Polish Wikipedia [00:23.200 --> 00:24.200] editor. [00:24.200 --> 00:26.680] I am also a mentor and a teacher. [00:26.680 --> 00:33.840] We will talk about Lisp and Scheme history, we will do quick introduction to Scheme, next [00:33.840 --> 00:40.160] we will talk about Lisp Scheme history and the most important part of the talk is about [00:40.160 --> 00:42.480] Lisp Scheme, how it works. [00:42.480 --> 00:46.800] To get most out of this talk you need to know basic of JavaScript. [00:46.800 --> 00:53.000] Lisp was presented in 1960s by John McCarthy and his famous paper Recursive Functions of [00:53.000 --> 00:57.720] Symbolic Expression and Direct Computation by Machine Part 1. [00:57.720 --> 01:03.960] Part 2 was never created, it was based on Lambda Calculus by Alonzo Tertz and the paper [01:03.960 --> 01:08.840] explained an eval function that was written in the Lisp itself. [01:08.840 --> 01:14.120] One of McCarthy's students, Steve Russell, decided to implement the eval function on [01:14.120 --> 01:19.800] the IBM 704 and it was first interpreter of the Lisp language. [01:19.800 --> 01:24.120] The syntax of the interpreter was a little bit different than the one described in the [01:24.120 --> 01:30.120] paper because of the limitations of the keyboard on the IBM mainframe. [01:30.120 --> 01:32.280] Lisp stands for Lisp Processing. [01:32.280 --> 01:37.440] The most important thing about the language is that it's homoiconic, which means that [01:37.440 --> 01:41.880] the Lisp code is represented by Lisp, the main data structure. [01:41.880 --> 01:47.960] It was heavily used by AI research at the beginning and it was great source of inspiration [01:48.040 --> 01:50.360] for most modern programming languages. [01:50.360 --> 01:54.760] There are few so-called dialects of Lisp which are used today. [01:54.760 --> 01:58.680] Scheme, Clojure, MX Lisp, Racket and Common Lisp. [01:59.240 --> 02:06.440] Scheme was invented in 1970s at MIT by Guy L. Steele and Gerald J. Sassman [02:06.440 --> 02:08.840] when they investigated the actor model. [02:08.840 --> 02:15.960] The language is defined by specifications RNRS, which stands for Revisited Report on Language Scheme. [02:15.960 --> 02:19.400] Where number indicates how many times it was revisited. [02:19.400 --> 02:26.280] Second version was Revisited Revisited, so it used power off to make the name shorter. [02:26.280 --> 02:32.280] There are also official extensions to the language, SRFI, Scheme Request for Implementations, [02:32.280 --> 02:34.680] which adds new language features. [02:34.680 --> 02:38.840] The official website for the language is Scheme.org. [02:38.840 --> 02:41.400] Now let's talk about basic of Scheme. [02:41.400 --> 02:47.000] In most modern programming languages, when you have a function call, you use syntax like this, [02:47.000 --> 02:53.080] where you have a function name and in parentheses there are arguments separated by a comma. [02:53.080 --> 02:58.600] In List and Scheme on the other hand, the code is created from S-expressions. [02:58.600 --> 03:05.960] A list created by parentheses, where first element is a function and arguments separated by a space. [03:05.960 --> 03:07.320] And you can mess those lists. [03:07.320 --> 03:11.880] What is important with this expression is that those are not operators. [03:11.880 --> 03:16.280] They are plus and aesthetic symbols, which are names of the functions. [03:16.280 --> 03:18.360] So they are in fact function calls. [03:18.360 --> 03:21.720] So they are written in the same way as a sign function. [03:22.360 --> 03:26.200] As I've mentioned, code and data use the same data structures. [03:26.200 --> 03:29.080] So it's important to distinguish data from code. [03:29.720 --> 03:31.560] This is done by quotations. [03:31.560 --> 03:36.520] The first expression is code and the second is data, a list of numbers. [03:36.520 --> 03:39.160] To define variables in Scheme, you use define. [03:39.880 --> 03:42.840] That can also be used to define a function. [03:42.840 --> 03:46.360] And let is used to create local variables. [03:46.360 --> 03:53.160] And this is how you define an if statement that will print a message depending on a Boolean expression. [03:53.800 --> 04:00.680] Define if and let expressions are special syntax which works differently than the normal functions. [04:00.680 --> 04:04.920] And you can define your own syntax like this by using macros. [04:04.920 --> 04:10.440] For example, we can define macrofrop that when passing expression with infix notation [04:10.440 --> 04:13.240] will sum the numbers using prefix notation. [04:13.240 --> 04:15.640] In Scheme, there are two types of macros. [04:15.640 --> 04:23.080] First are list macros that accept code as data and return new list that will be evaluated. [04:23.080 --> 04:26.760] And the second are hygienic macros that use pattern matching syntax. [04:27.320 --> 04:30.680] These macros are used probably by all list dialects. [04:31.160 --> 04:36.360] But hygienic macros are specific to Scheme and dialect based on Scheme. [04:36.360 --> 04:39.000] To learn more about Scheme, I suggest a book, [04:39.000 --> 04:41.320] Sketchy Scheme by Nils M. Horm. [04:42.040 --> 04:45.880] You can find the older version of the book on Internet Archive [04:45.880 --> 04:48.920] by a suggest to get the latest version from this link. [04:50.040 --> 04:52.120] The main topic of this talk is leaps. [04:52.680 --> 04:55.560] Scheme in implementation written in JavaScript. [04:55.560 --> 04:58.440] So let's quickly talk about history of this project. [04:59.240 --> 05:02.920] It started on KotPen as a list based on Scheme. [05:03.880 --> 05:09.720] I wanted to create Emacs in browser and wanted to have something like EmacsList. [05:10.680 --> 05:15.560] That's why leaps from the beginning have an optional dynamic scope [05:15.560 --> 05:18.200] that is a characteristic feature of EmacsList. [05:19.160 --> 05:23.160] Fpcat Scheme because it's much simpler than other dialects. [05:23.880 --> 05:28.200] You can still find the first version of the interpreter on KotPen. [05:28.920 --> 05:35.720] Leaps was inspired by EmacsList and Python, mostly about the introspection features [05:35.720 --> 05:39.320] and that all functions have documentation inside the code, [05:39.320 --> 05:41.560] which you can access from the REPL. [05:41.560 --> 05:46.520] The last version of leaps that you can access from the NPM repository [05:46.520 --> 05:50.760] as a stable release is version 0.20.3. [05:51.400 --> 05:57.320] But on a certain point, I decided that I want a full Scheme implementation, [05:57.320 --> 05:59.560] not only leaps based on Scheme. [06:00.200 --> 06:03.640] And I've started working on the code on the devil branch. [06:03.640 --> 06:07.800] But at one point, it turns out that there are way too many [06:07.800 --> 06:10.120] breaking changes to release the next version. [06:10.120 --> 06:16.200] That's why I released it as 1.0 beta and the latest version is 16. [06:17.000 --> 06:20.200] At the beginning, the whole code was written in JavaScript. [06:20.200 --> 06:24.440] But when I was making an effort toward full Scheme implementation, [06:24.440 --> 06:26.520] more and more code was written in Scheme. [06:27.880 --> 06:30.440] Now almost half of the leaps code is Scheme. [06:31.560 --> 06:33.480] And now there is a time for the demo. [06:35.640 --> 06:38.440] This is the official website for the leaps project. [06:38.440 --> 06:42.120] And what's cool about this is that here you have a bookmarklet [06:42.680 --> 06:45.720] and you can drag this link to your bookmarks [06:46.920 --> 06:49.000] and execute it on a different page. [06:49.000 --> 06:51.880] For instance, here there is a first lecture [06:51.880 --> 06:54.920] of the structure of the interpretation of the program. [06:54.920 --> 06:58.520] A classic video lectures from MIT. [07:02.280 --> 07:12.520] You can evaluate Scheme code that you see on the screen. [07:20.600 --> 07:23.880] The feature of the rebel is that there are syntax high-liking [07:23.880 --> 07:28.760] and parenthesis matching and also each macro and functions [07:28.760 --> 07:31.400] have documentation if you hover over the name. [07:32.200 --> 07:34.520] Here you have documentation for define. [07:35.240 --> 07:39.960] Here you have documentation for asterisk multiplication operator. [07:41.320 --> 07:44.760] You can also undock the panel with the rebel [07:46.520 --> 07:50.120] and use it inside the window that you can drag and drop on the page. [07:54.120 --> 08:00.840] Another cool feature of the rebel is that you can execute it on PDF files. [08:04.040 --> 08:06.760] But I've tested this only on Chrome browser. [08:08.120 --> 08:11.160] This PDF document is Scheme language specification. [08:12.680 --> 08:16.120] But it often gives problems if you try to execute code [08:16.120 --> 08:18.920] that is inside this document in the rebel. [08:19.400 --> 08:24.680] For instance, on page 12, there's this quotations. [08:24.680 --> 08:27.160] If you try to execute this code in the rebel, [08:28.360 --> 08:30.760] you give it this kind of warning. [08:32.920 --> 08:36.120] But you can fix this error by executing this code. [08:40.440 --> 08:46.200] You execute it, suddenly you can evaluate this expression. [08:47.160 --> 08:53.800] This is a special kind of function that creates syntax extension. [08:54.600 --> 08:57.560] Here you can have documentation for this function. [08:59.880 --> 09:03.240] Syntax extensions allow to define new syntax [09:03.240 --> 09:07.400] similar to the one defined in JavaScript, like those quotations. [09:08.360 --> 09:12.680] Here you can see vector literals, defined by hash sign. [09:13.240 --> 09:16.440] Vectors are also created as syntax extensions. [09:18.040 --> 09:21.560] And Scheme vectors are just JavaScript R writes. [09:22.520 --> 09:27.640] Similar syntax extension is ampersand that define JavaScript object literals. [09:34.840 --> 09:40.120] Here we can see that representation of object literals looks the same as the code. [09:41.080 --> 09:44.280] This is another feature of Libs that allow to define [09:44.280 --> 09:46.760] new representation for different instances. [09:47.400 --> 09:50.280] Scheme vectors are also defined in the same way. [09:52.440 --> 09:58.280] You can use both features to define homo-iconic data types. [10:10.600 --> 10:12.600] You can use both features to define homo-iconic data types. [10:30.840 --> 10:35.320] Records are the way to define new data types in Scheme [10:35.320 --> 10:39.000] that is defined in the specification on page 27. [10:40.280 --> 10:56.280] You can define syntax extension for this record. [11:04.600 --> 11:07.320] The third argument to set special indicates [11:07.880 --> 11:11.960] how the makePerson function should receive the arguments. [11:11.960 --> 11:14.520] The list or as a normal arguments. [11:15.240 --> 11:18.920] This feature may be removed in the future to simplify the code. [11:19.560 --> 11:23.320] The dot notation in the last argument is taken from JavaScript [11:23.320 --> 11:26.680] to simplify interaction with the hosting language. [11:26.680 --> 11:32.600] Libs is a global object that you can inspect with the dir function inspired by Python. [11:37.800 --> 11:47.800] In the same way, you can access any JavaScript object or a function. [11:47.800 --> 12:04.440] By let's go back to our record example. [12:05.480 --> 12:06.680] A person is a class. [12:10.840 --> 12:14.760] And you can create an instance of that class with a new macro. [12:18.200 --> 12:33.720] Or with makePerson function created by Scheme record type. [12:37.960 --> 12:45.720] We can also use our syntax extension to create a new person object. [12:47.800 --> 13:03.720] Now let's add a representation of this new data type. [13:17.800 --> 13:43.720] And now we can evaluate the code and have the same representation. [13:47.800 --> 14:00.760] The queue parameter indicates if the result should be quoted or not. [14:00.760 --> 14:13.720] In the wrapper, the strings are quoted because they use Scheme write function. [14:14.680 --> 14:25.640] But you can use display function that don't use quotations. [14:25.640 --> 14:39.640] But with setRapper you can make representation of the records without the new syntax. [14:43.720 --> 14:51.640] And you can use display function to make representation of the records without the new syntax. [14:51.640 --> 15:01.640] And you can use display function to make representation of the records without the new syntax. [15:01.960 --> 15:23.480] With this feature, you can easily serialize and deserialize custom data types. [15:23.480 --> 15:27.320] For instance, when saving in browser local storage. [15:53.480 --> 16:14.120] We use that eval because readReturnsList as data that needs to be evaluated to get the [16:14.120 --> 16:21.640] instance of the person object. To get the property of the speaker object, you can use dot special macro. [16:24.200 --> 16:29.000] Or you can use JavaScript dot notation. [16:33.240 --> 16:40.920] The next feature I want to discuss is introspection. You can use upper post function to search the [16:40.920 --> 16:52.440] environment. This is a list of functions and macros that match a given string. [16:52.440 --> 17:05.400] In this case, vector. You can also use regular expressions to make the search more specific. [17:05.960 --> 17:22.040] And this is a list of typed vectors. Each of those constructors have also [17:22.120 --> 17:38.680] the syntax extension that allow to create those vectors according to scheme specification. [17:38.920 --> 17:46.600] Each scheme type at vector is in fact JavaScript type at RI. And each of those RIs have its own [17:46.600 --> 17:52.680] representation. So they look like the code that defines them. You can access the documentation [17:52.680 --> 18:00.120] and the source code of the every function macro and fiber defined by lips. You can access the name, [18:00.280 --> 18:08.760] the documentation [18:11.960 --> 18:19.720] and the source code of this function. What's cool about the code is that it's live object that you [18:19.720 --> 18:31.320] can modify. [18:43.320 --> 18:48.840] The double underscore syntax is inspired by Python magic properties. You can also [18:48.840 --> 19:00.440] inspect the internals of other lips objects like symbols [19:11.000 --> 19:12.440] or numbers. [19:18.840 --> 19:29.880] Lips support full numerical tower. Here's a complex number, but it's not yet fully unit tested, [19:29.880 --> 19:38.360] so there are no guarantees that everything works correctly by 100%. You can also inspect the list [19:38.360 --> 19:49.960] objects. [19:52.680 --> 19:59.560] The instance of function use JavaScript instance of operator to check if argument is instance of [19:59.560 --> 20:08.360] the object. Here it check if x is lips list and it written true. You can use the standard [20:08.360 --> 20:11.160] list function to get the third element of the list. [20:11.400 --> 20:26.760] But you can also access internal pair objects defined by lips. [20:26.760 --> 20:44.760] The proposed function that I've showed a few moments ago allows to search the environment, [20:45.560 --> 20:51.640] but in lips you can also access environment objects themselves and do cool things with them. [20:51.880 --> 21:01.240] You can inspect them. [21:05.880 --> 21:13.240] As you can see there is double underscore nth property that you can read. [21:21.880 --> 21:29.880] Object keys is a JavaScript function that returns r i of strings, so it's represented as a scheme [21:29.880 --> 21:37.240] vector. Those are all objects defined inside the repo, including lips internals. You can see [21:37.240 --> 21:43.240] our person class and you can access it using environment object. [22:22.040 --> 22:30.040] Make person was our representation of the person instance using set wrapper. [22:30.040 --> 22:36.040] Inside the environment object there is also another double underscore property parent, [22:36.040 --> 22:42.440] which allows you to access a lexical scope time. [23:06.040 --> 23:26.440] You can access both x fibers from the scope 10 inside a one expression. [23:36.040 --> 23:42.440] You can also access both x fibers from the scope 10. [24:06.840 --> 24:16.440] You can also modify the scope inside the chain. [24:16.600 --> 24:40.840] Set is a generic macro that allows to modify any JavaScript object, not only lips internals. [24:41.400 --> 24:45.720] Another feature of lips is that you can act as stack frames of function calls. [24:47.000 --> 24:50.680] They are also environment objects inspired by the air [24:50.680 --> 24:55.320] programming language that has a lot of lips under the hood. [25:50.680 --> 25:57.880] Here the function test can modify the scope outside the function call. [25:57.880 --> 26:04.280] Another function similar to parent frame is plural parent frames, which returns a list [26:04.280 --> 26:12.040] of stack frames. With both parent frames and underscore code access you can modify the function [26:12.040 --> 26:16.600] that call a given function anywhere inside the call stack chain. [26:42.680 --> 27:07.240] The long arrow is a macro for invoking methods and the cally is lips object similar to [27:07.320 --> 27:13.160] JavaScript object with the same name. The long arrow macro is a convenient method [27:13.160 --> 27:15.720] to create the chain of method calls. [27:37.800 --> 27:47.240] This code demonstrates another fundamental feature of lips, [27:48.040 --> 27:53.160] where everything is automatically assigned away by default when needed. [27:53.160 --> 27:59.720] Fetch is a javascript function that returns a promise which results to a resource object. [27:59.720 --> 28:04.120] That object has a text method that also returns a promise. [28:04.120 --> 28:08.920] Here we can skip them which makes the code simpler than the javascript equivalent. [28:09.480 --> 28:14.760] The match in the code is a string method and the number one returns first group from the [28:14.760 --> 28:20.440] regular expression. The whole expression returns the main header of the lips website, [28:20.440 --> 28:24.840] but when needed you can quote the promise and use it as an object. [28:34.120 --> 28:35.500] you [29:04.840 --> 29:15.720] Now let's back to our presentation for a final thought. [29:17.560 --> 29:23.640] As you was able to see from the demo, lips is pretty flexible and powerful, [29:23.640 --> 29:30.840] but it has its limitations. One of the limitations is that macro are on time. There is no macro [29:30.920 --> 29:37.640] expansion time. There are also some performance issues. One of the reasons for it may be the lack [29:37.640 --> 29:43.720] of macro expansion time, but you can fix those issues by embedding javascript code in tight [29:43.720 --> 29:50.200] lips. The most important things that are missing are first call continuations and tail call [29:50.200 --> 29:57.400] optimizations and also the syntax rules scheme hygienic macro system is not working exactly [29:57.400 --> 30:03.960] as it should. All this can be improved in the future. Thank you for listening to my presentation.