Transcript

[[!template new="1" text="""Intro""" start="00:00:00.000" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""Hi everyone, this is EmacsConf 2024. I'm Colin, and today""" start="00:00:00.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""I'll be talking about transducers.""" start="00:00:10.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""After introducing them, I'll share a bit of history about""" start="00:00:17.320" video="mainVideo-transducers" id="subtitle"]] [[!template text="""transducers and the problems that they solve, some basics""" start="00:00:21.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""about how we can use them, how they work, like how they're""" start="00:00:25.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""implemented, some demonstrations of how we can actually""" start="00:00:28.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""use them in the wild, and then some other discussions about""" start="00:00:32.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""issues that they have.""" start="00:00:36.960" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""What are transducers?""" start="00:00:41.520" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""Okay, let's get right in. What are transducers?""" start="00:00:41.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Transducers are a way to do streaming iteration with a""" start="00:00:46.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""modern API.""" start="00:00:49.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Who are transducers for, and thereby, who is""" start="00:00:55.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""this talk for? Well, it's for people who want to do streamed""" start="00:01:00.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""data processing in Emacs. It's for people who perhaps""" start="00:01:05.600" video="mainVideo-transducers" id="subtitle"]] [[!template text="""aren't satisfied with the existing APIs, for example, the""" start="00:01:10.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""seq API, or some other common libraries that provide""" start="00:01:14.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""similar functionality. Maybe you're not a fan of the loop""" start="00:01:19.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""macro. Some people find it difficult to understand. Or""" start="00:01:23.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""maybe you've done a bunch of Clojure before, and you'd like""" start="00:01:29.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""more aspects of Clojure in your Emacs Lisp. Or maybe you're""" start="00:01:32.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""just interested in transducers in general, because the""" start="00:01:36.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""pattern has now been ported to multiple different Lisps.""" start="00:01:40.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""So I'm Colin. I'm fosskers on everything online, and I do""" start="00:01:48.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""mainly back-end programming work and a lot of open source""" start="00:01:55.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""software. I wrote Haskell for a long time, both as a hobbyist""" start="00:01:58.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""and professionally. Since the COVID years, I've been""" start="00:02:05.160" video="mainVideo-transducers" id="subtitle"]] [[!template text="""writing Rust, both open source and professionally. But now""" start="00:02:09.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""I find that in my spare time, I'm mostly writing Common Lisp.""" start="00:02:13.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Some things I learned from my years of Haskell was that a lot""" start="00:02:19.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""of programming is just altering the shape of data. You know,""" start="00:02:22.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""sometimes we work through our algorithm line by line. We're""" start="00:02:27.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""trying to just tell the computer exactly what to do. But if we""" start="00:02:31.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""step back, a lot of the time we're just getting in data of some""" start="00:02:36.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""shape, changing it, and then passing it along. A lot of""" start="00:02:39.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""these patterns are common, identified""" start="00:02:44.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""decades ago. For instance, we have some collection, and we""" start="00:02:49.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""want to transform every element of that collection and then""" start="00:02:53.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""pass it on. Or maybe we're trying to filter out bad elements""" start="00:02:57.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""in that collection. Or maybe we're looking for a specific""" start="00:03:01.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""element in that collection. Yes, you could write all that""" start="00:03:04.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""with for loops, but these kind of common patterns were""" start="00:03:07.760" video="mainVideo-transducers" id="subtitle"]] [[!template text="""identified and given names decades ago. So why not use them?""" start="00:03:11.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""They say that there are two major problems in computer""" start="00:03:18.560" video="mainVideo-transducers" id="subtitle"]] [[!template text="""science, one being cache validation and the other being""" start="00:03:21.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""naming things.""" start="00:03:25.760" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""Common issues""" start="00:03:27.590" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""I've identified five other problems that""" start="00:03:27.590" video="mainVideo-transducers" id="subtitle"]] [[!template text="""come up when we're trying to deal with collections of data,""" start="00:03:29.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""or big streams of data. One is that if we were trying to""" start="00:03:33.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""load a file all into memory all at once and process the whole""" start="00:03:40.600" video="mainVideo-transducers" id="subtitle"]] [[!template text="""thing, sometimes we can have memory problems. You've""" start="00:03:45.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""probably seen out-of-memory errors or such things.""" start="00:03:48.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""A second issue that comes up is that if we were looking at a""" start="00:03:55.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""giant for loop, in particular a nested for loop or such""" start="00:03:58.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""things, it can be hard to tell just by looking at the code what""" start="00:04:01.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""it's trying to do, what it intends. If we don't go character""" start="00:04:06.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""by character or line by line, it can be hard to understand it.""" start="00:04:11.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Furthermore, and this is particularly an issue with Emacs""" start="00:04:16.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Lisp, is that if one call, for instance, to seq-map, then""" start="00:04:20.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""piped into seq-filter, for instance, will have an""" start="00:04:26.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""intermediate allocation, the map will take the source""" start="00:04:29.320" video="mainVideo-transducers" id="subtitle"]] [[!template text="""container, allocate a new one, and then the filter will""" start="00:04:33.600" video="mainVideo-transducers" id="subtitle"]] [[!template text="""operate over the second one. This is wasteful.""" start="00:04:37.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Furthermore, it can often be difficult to abort a stream.""" start="00:04:40.320" video="mainVideo-transducers" id="subtitle"]] [[!template text="""For instance, if we were filtering through our collection,""" start="00:04:48.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""but we knew we only wanted to go halfway, for instance, for""" start="00:04:53.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""some reason, we have no way to stop it halfway through. We""" start="00:04:57.320" video="mainVideo-transducers" id="subtitle"]] [[!template text="""just have to process the whole thing, even if we know we don't""" start="00:05:01.760" video="mainVideo-transducers" id="subtitle"]] [[!template text="""need to. Another issue is that for languages that have""" start="00:05:05.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""traits, or in Haskell they're called type classes, if you""" start="00:05:11.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""are defining what it means to map over something, you often""" start="00:05:18.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""have to redefine that for every kind of container or thing""" start="00:05:22.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that you're iterating over. Wouldn't it be nice if we could""" start="00:05:27.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""define things like map just once and then reuse them""" start="00:05:31.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""everywhere? Now, transducers solve all five of these,""" start="00:05:34.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""without the addition of new language features, and with""" start="00:05:39.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""little more than plain old function composition.""" start="00:05:44.040" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""Transducers""" start="00:05:47.280" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""If this is your first time hearing of transducers, yeah,""" start="00:05:47.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""no problem. They were originally invented in Clojure by""" start="00:05:53.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Rich Hickey, and this is a quote from him. He thinks""" start="00:05:57.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""transducers are a fundamental primitive that decouple""" start="00:06:01.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""critical logic from list or sequence processing, and if he""" start="00:06:05.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""had to do Clojure all over, he'd put them at the bottom, at the""" start="00:06:10.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""very bottom of all the fundamental primitives. Now, that's""" start="00:06:14.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Rich speaking quite highly of them. And I think he has a point""" start="00:06:19.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""here.""" start="00:06:24.600" video="mainVideo-transducers" id="subtitle"]] [[!template text="""They were invented originally in Clojure. In more""" start="00:06:25.160" video="mainVideo-transducers" id="subtitle"]] [[!template text="""recent years, they were brought over to Scheme""" start="00:06:32.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""via SRFI 171. That's where I found them""" start="00:06:34.773" video="mainVideo-transducers" id="subtitle"]] [[!template text="""when I was learning the Guile language.""" start="00:06:38.775" video="mainVideo-transducers" id="subtitle"]] [[!template text="""In the process of submitting a patch, I realized""" start="00:06:41.522" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that there were other things to be improved. So I ported the""" start="00:06:43.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""pattern to Common Lisp, then Fennel, and then more""" start="00:06:48.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""recently, Emacs Lisp. The Common Lisp and Emacs Lisp APIs""" start="00:06:51.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""are identical. And the Fennel one is not identical, but""" start="00:06:56.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""fairly similar. Overall, everywhere you find""" start="00:07:01.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""transducers, they should basically be fairly uniform.""" start="00:07:05.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""When I originally made the Common Lisp variant first, I""" start="00:07:10.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""sampled the APIs from a number of different languages and""" start="00:07:15.760" video="mainVideo-transducers" id="subtitle"]] [[!template text="""came up with what I believed to be a representative sample of""" start="00:07:18.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""what most people would want out of such a library. I gave""" start="00:07:23.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""functions their common modern names. For instance, map""" start="00:07:27.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""is map and filter is filter and so on.""" start="00:07:32.440" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""Using transducers""" start="00:07:35.280" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""What does the usage of transducers look like? Well,""" start="00:07:35.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""these examples will all be the Emacs Lisp variant, but the""" start="00:07:42.600" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Common Lisp will look basically exactly the same, minus""" start="00:07:48.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""this little t- prefix.""" start="00:07:52.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Running transducers requires three things. It requires a""" start="00:07:54.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""source. This could be an obvious thing like a list or a""" start="00:08:00.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""vector, but it could be other things like a file, or in Emacs""" start="00:08:06.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""list in particular, a buffer.""" start="00:08:11.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""A reducer is a function. It's something like""" start="00:08:16.349" video="mainVideo-transducers" id="subtitle"]] [[!template text="""the + operator or the * operator,""" start="00:08:20.113" video="mainVideo-transducers" id="subtitle"]] [[!template text="""or certain constructors of various containers.""" start="00:08:22.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""It takes values and collates them into some final version.""" start="00:08:26.786" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Now, finally, we have what we're calling here""" start="00:08:32.126" video="mainVideo-transducers" id="subtitle"]] [[!template text="""a transducer chain. This could be one transducer function""" start="00:08:33.947" video="mainVideo-transducers" id="subtitle"]] [[!template text="""or it could be multiple composed together. These are the""" start="00:08:37.568" video="mainVideo-transducers" id="subtitle"]] [[!template text="""functions that actually take data and transform them""" start="00:08:43.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""somehow. For instance, this. We have a list of three""" start="00:08:47.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""elements. We want to reduce it into a vector. How we are""" start="00:08:55.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""going to transform the elements along the way: we are doing""" start="00:09:04.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""plus one to each of them. If this syntax is new to you, just""" start="00:09:07.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""know that this #' just means that this thing that""" start="00:09:13.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""comes after it is the name of the function. In Common Lisp and""" start="00:09:18.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Emacs Lisp, this is necessary, but for Clojure and Scheme,""" start="00:09:22.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""it is not. So we can see here that just this example is not much""" start="00:09:26.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""different than any other normal map call you might see made,""" start="00:09:32.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""but if nothing else, it's a handy way to convert a list to a""" start="00:09:36.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""vector or anything else. There are many, many reducers""" start="00:09:40.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""available and many different forms that we can""" start="00:09:45.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""collate the final value into.""" start="00:09:48.240" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""A more involved example with comp""" start="00:09:52.625" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""Let's see a more involved example.""" start="00:09:52.625" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Okay, now we've got some more meat here.""" start="00:09:55.087" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Here we can see usage of the comp function""" start="00:09:58.050" video="mainVideo-transducers" id="subtitle"]] [[!template text="""and a custom source, ints.""" start="00:10:01.773" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Ints is an infinite generator of integer values. That's not""" start="00:10:05.256" video="mainVideo-transducers" id="subtitle"]] [[!template text="""like a list or a file. It will generate infinitely.""" start="00:10:11.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Comp is letting us compose multiple transducer functions""" start="00:10:14.784" video="mainVideo-transducers" id="subtitle"]] [[!template text="""together. Notice that this is the opposite order of what""" start="00:10:19.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we'd usually be used to from a function like comp. The order""" start="00:10:23.760" video="mainVideo-transducers" id="subtitle"]] [[!template text="""here is top to bottom, basically, so that the map goes first,""" start="00:10:28.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""then the filter, and then the take. So effectively is what""" start="00:10:32.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we're doing is taking all the integers that exist,""" start="00:10:37.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""positive, adding one to them, filtering out only the even""" start="00:10:40.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""ones, but then just taking 10. Cons here is a function that""" start="00:10:45.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""just produces the ending result as a list. So what happens""" start="00:10:50.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""here specifically is how we are avoiding intermediate""" start="00:10:57.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""allocations. First, the number 0 will come through.""" start="00:11:00.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""It will be pulled out of this source internally by transduce.""" start="00:11:04.239" video="mainVideo-transducers" id="subtitle"]] [[!template text="""It will make its way into the map. The map will add it. Then it""" start="00:11:07.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""will immediately go into this filter step. So it's not like""" start="00:11:10.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""all the maps occur, and then all the filters occur. We do""" start="00:11:15.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""everything for each element. So the 0 comes in, now it's 1.""" start="00:11:19.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""The filter would occur. Well, it's going to fail that""" start="00:11:24.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""because it's not even, so it will just bail there. Now we'll""" start="00:11:27.560" video="mainVideo-transducers" id="subtitle"]] [[!template text="""go to the next one. Now 1 will come, it will become 2, then""" start="00:11:31.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""it will be saved by this evenp call, and then the take will""" start="00:11:35.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""capture it, because we only want 10 values here. You can""" start="00:11:39.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""see 2, 4, 6, 8, and so on is the result that we""" start="00:11:42.600" video="mainVideo-transducers" id="subtitle"]] [[!template text="""expect. So let's play around a little bit.""" start="00:11:45.240" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""In Emacs""" start="00:11:49.333" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""Let's jump into Emacs and see what we can do.""" start="00:11:49.333" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Alright, you should see my Emacs screen here.""" start="00:11:53.337" video="mainVideo-transducers" id="subtitle"]] [[!template text="""These are the actual notes for the actual""" start="00:11:58.501" video="mainVideo-transducers" id="subtitle"]] [[!template text="""presentation done in Org Mode. I'll boost that up in size for""" start="00:12:04.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""a little bit. That should be more than big enough for you.""" start="00:12:08.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Just by changing the reducer, we can change the result.""" start="00:12:12.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Okay, now it's a vector. Well, what else can we do to it? Well,""" start="00:12:17.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""let's just add up the results. Maybe we just want to count the""" start="00:12:21.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""results. Oh, indeed, there were 10. What if we want to find""" start="00:12:25.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""the average of the results? What if we want to find the median""" start="00:12:30.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""of the results? And so on. Here's some more interesting""" start="00:12:36.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""things that we could do. We could add different steps. So""" start="00:12:40.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""here we have all the integers. Let's add, hmm, okay, we'll""" start="00:12:45.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""keep that. We're going to add t-enumerate. What enumerate does""" start="00:12:51.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""is for each item that comes through, it is""" start="00:12:57.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""going to add a sort of index to it and make it a pair. In this""" start="00:13:00.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""case, it's going to be equal to what came in here. Well, we can""" start="00:13:06.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""change it. If we start this at 1, now it will be different.""" start="00:13:08.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""1 will be paired with 0, and then 2 would be paired""" start="00:13:12.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""with 1, and so on. We'll accept that the even call will change""" start="00:13:15.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that a little bit. Why we're doing this is because we want""" start="00:13:19.560" video="mainVideo-transducers" id="subtitle"]] [[!template text="""to form a hash table. Let's move that down to 3, maybe""" start="00:13:24.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we'll get a better result. What do we see? Okay, here now the""" start="00:13:27.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""result is a hash table. What are its values? Well, 0 seems""" start="00:13:31.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""to have... The key of 0 seems to be paired with 2, the key of""" start="00:13:37.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""1 seems to be paired with 4,""" start="00:13:40.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""and 2 seems to be paired with 6.""" start="00:13:42.910" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Maybe let's jazz that up even a little bit more.""" start="00:13:47.412" video="mainVideo-transducers" id="subtitle"]] [[!template text="""We're going to start from a string""" start="00:13:51.294" video="mainVideo-transducers" id="subtitle"]] [[!template text="""and we'll call it hello.""" start="00:13:52.974" video="mainVideo-transducers" id="subtitle"]] [[!template text="""That's not going to work anymore""" start="00:13:57.944" video="mainVideo-transducers" id="subtitle"]] [[!template text="""and neither is that, but what we could do is""" start="00:13:59.565" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we could say t-map #'string.""" start="00:14:02.586" video="mainVideo-transducers" id="subtitle"]] [[!template text="""I believe we'll do that.""" start="00:14:05.499" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Let's see if that works. It did. So that's""" start="00:14:08.628" video="mainVideo-transducers" id="subtitle"]] [[!template text="""going to convert a character into a string.""" start="00:14:08.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Let's just go two""" start="00:14:13.590" video="mainVideo-transducers" id="subtitle"]] [[!template text="""just to make it a little easier. Now you can see that we've""" start="00:14:14.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""constructed a hash table here. The key of 0 is mapped to the""" start="00:14:18.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""string of h and 1 is mapped to e. Now, I really like having""" start="00:14:21.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""this reducer in particular.""" start="00:14:27.080" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""Hash tables""" start="00:14:29.469" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""Know that hash tables are""" start="00:14:29.469" video="mainVideo-transducers" id="subtitle"]] [[!template text="""also legal sources. I find that both in Emacs Lisp and in""" start="00:14:30.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Common Lisp, dealing with hash tables--like creating them""" start="00:14:34.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""and altering them--can be a bit of a pain. Having them""" start="00:14:37.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""immediately available like this with transducers is very""" start="00:14:41.600" video="mainVideo-transducers" id="subtitle"]] [[!template text="""handy, I find. We can work with something that wasn't a hash""" start="00:14:45.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""table. We can construct it in a way that makes it amenable to""" start="00:14:49.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that, and then reduce it down into a hash table, and here you""" start="00:14:53.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""go. Very handy.""" start="00:14:56.200" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""Clarity""" start="00:14:58.040" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""One last point is that you can see very clearly what""" start="00:14:58.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""this is attempting to do, as opposed to, say, a for loop. It's""" start="00:15:06.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""very clear what that step is doing, and then you can see what""" start="00:15:10.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that is doing, and you know that the result is going to be two.""" start="00:15:12.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Each line is kind of its own declarative step, and it should""" start="00:15:15.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""be clear, just by staring at this, basically what you're""" start="00:15:18.560" video="mainVideo-transducers" id="subtitle"]] [[!template text="""going to get out. This is one main difference from other""" start="00:15:22.160" video="mainVideo-transducers" id="subtitle"]] [[!template text="""languages that have things--say, for instance, Rust's""" start="00:15:25.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""iterator API--is the difference between the transducers""" start="00:15:29.600" video="mainVideo-transducers" id="subtitle"]] [[!template text="""and the reducers. If we go up here, for example, the""" start="00:15:35.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""difference between the transducers and the reducers and""" start="00:15:41.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""the sources is not explicitly laid out, whereas with""" start="00:15:44.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""transducers, it is. You have to be aware of how these things""" start="00:15:48.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""are different. I think that that helps clarity.""" start="00:15:53.120" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""How do transducers work?""" start="00:15:55.800" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""Moving on. How do transducers work? Well,""" start="00:15:55.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we want to go see the README.""" start="00:16:02.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""So, what we're going to do is""" start="00:16:09.858" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we're going to go to here.""" start="00:16:11.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""You should still be able to see this.""" start="00:16:19.103" video="mainVideo-transducers" id="subtitle"]] [[!template text="""This is the CL example, actually.""" start="00:16:21.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Let's go to transducers.el.""" start="00:16:28.584" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Their APIs and READMEs are the same,""" start="00:16:32.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""but just for the sake of it, we will go see""" start="00:16:37.745" video="mainVideo-transducers" id="subtitle"]] [[!template text="""how this looks on the Emacs side,""" start="00:16:39.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""just so that nothing is a surprise.""" start="00:16:45.727" video="mainVideo-transducers" id="subtitle"]] [[!template text="""But recall that the APIs are essentially the same""" start="00:16:48.047" video="mainVideo-transducers" id="subtitle"]] [[!template text="""between the two. If you go to this section, writing your""" start="00:16:50.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""own primitives, you can read about how transducers are""" start="00:16:53.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""actually formed, whether or not you want to write them""" start="00:16:56.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""yourself or not. We can see here t-map. We accept the""" start="00:17:01.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""function that you want to operate with. Then you've got""" start="00:17:06.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""this extra little lambda here that's coming in, and it's""" start="00:17:10.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""receiving a thing that is named reducer. Now, while here""" start="00:17:13.320" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we're calling it reducer, it's actually the chain of all the""" start="00:17:17.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""composed functions together. It's all those main""" start="00:17:20.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""transducer steps. Finally, it's the reducer all""" start="00:17:25.160" video="mainVideo-transducers" id="subtitle"]] [[!template text="""composed together with normal function composition.""" start="00:17:28.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""That will matter very soon. Now here's the actual meat.""" start="00:17:31.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""We can see the accumulative result that's coming in with the""" start="00:17:35.878" video="mainVideo-transducers" id="subtitle"]] [[!template text="""current element. Now we need to operate on this.""" start="00:17:40.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Were it normally mapped, we would see us""" start="00:17:45.740" video="mainVideo-transducers" id="subtitle"]] [[!template text="""applying the F to the input.""" start="00:17:47.841" video="mainVideo-transducers" id="subtitle"]] [[!template text="""But here, you can see us applying the F to the input and then""" start="00:17:49.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""continuing on. So us calling the rest of the composed chain""" start="00:17:53.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""here is the effect of, in the previous slide, moving to the""" start="00:17:58.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""next step. We could ignore this line for now.""" start="00:18:03.160" video="mainVideo-transducers" id="subtitle"]] [[!template text="""If you're curious, please read the README in detail.""" start="00:18:07.157" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Now, what about reducers?""" start="00:18:13.820" video="mainVideo-transducers" id="subtitle"]] [[!template text="""What do those look like? Well, let's just scroll""" start="00:18:15.580" video="mainVideo-transducers" id="subtitle"]] [[!template text="""down here. Recall that a reducer is a function that's""" start="00:18:18.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""consuming a stream, right? Zoom that up for you a little bit.""" start="00:18:22.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Now, in the case of count, recall that this is how it's""" start="00:18:26.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""working, how we saw a moment ago. So clearly this list of five""" start="00:18:33.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""elements only has five things in it. Well, a reducer by""" start="00:18:37.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""structure is a function of two, one, or zero arguments. So we""" start="00:18:42.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""can see here in the case of two, this is the normal iterative""" start="00:18:47.600" video="mainVideo-transducers" id="subtitle"]] [[!template text="""case. We don't care about the input for count, we just care""" start="00:18:50.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""about the current accumulated count that we're doing, and""" start="00:18:54.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we add one to it, and that's it. This then goes back to""" start="00:18:58.560" video="mainVideo-transducers" id="subtitle"]] [[!template text="""the loop and the whole process starts again with the next""" start="00:19:02.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""element. In this kind of done case, this is used internal to""" start="00:19:06.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that sort of the supervising function transduce. It's just""" start="00:19:10.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""confirming the final result. Sometimes some""" start="00:19:16.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""post-processing is necessary here, but in the case of""" start="00:19:19.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""count, as it is so simple, that is not necessary. And now""" start="00:19:21.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""here's the base case. This is also used within that""" start="00:19:26.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""supervising transduce function at the very top. Well, if""" start="00:19:29.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""you're counting, you have to start from somewhere, right?""" start="00:19:34.320" video="mainVideo-transducers" id="subtitle"]] [[!template text="""In this case, well, what you're starting with is zero.""" start="00:19:36.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""In the case of cons, you'd be starting with an empty list.""" start="00:19:37.350" video="mainVideo-transducers" id="subtitle"]] [[!template text="""In the case of vector, you'd be starting""" start="00:19:40.252" video="mainVideo-transducers" id="subtitle"]] [[!template text="""with an empty vector and so on.""" start="00:19:44.435" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Once again, if you are more curious, please take a look at""" start="00:19:54.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""the README.""" start="00:19:56.800" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""Transducers in the wild - CSV""" start="00:20:00.520" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""Okay, transducers in the wild. Well, let's go take a look at""" start="00:20:00.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""processing some CSV data.""" start="00:20:06.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""We're going to open up a new Emacs Lisp bracket here. So I have""" start="00:20:07.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""a file. And in this file, let's just go look at C-x b right""" start="00:20:21.320" video="mainVideo-transducers" id="subtitle"]] [[!template text="""there, you will see that we've got some bank transaction""" start="00:20:28.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""information. It's got these transactions from a whole""" start="00:20:34.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""bunch of different people into different accounts,""" start="00:20:37.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""whether it's money coming in, money going out, and then a""" start="00:20:40.200" video="mainVideo-transducers" id="subtitle"]] [[!template text="""basic description. How's your Latin? But for this little""" start="00:20:43.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""test, what we want to do is we want to find Bob's final bank""" start="00:20:47.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""balance. Let's get on to it. First of all, let's""" start="00:20:53.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""just confirm, let's do some basic stuff.""" start="00:20:59.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""with-current-buffer, find-file-noselect.""" start="00:21:04.445" video="mainVideo-transducers" id="subtitle"]] [[!template text="""What's the name of that file?""" start="00:21:10.845" video="mainVideo-transducers" id="subtitle"]] [[!template text="""This is pre-organized, so you""" start="00:21:15.543" video="mainVideo-transducers" id="subtitle"]] [[!template text="""will just see it right here.""" start="00:21:17.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""t-transduce and t-comp. We don't know what we're going to comp""" start="00:21:20.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""yet. Actually, I'll just pass to show you. And then we will""" start="00:21:27.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""see, let's just do a little t-count just to confirm. What's""" start="00:21:33.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""our source? Well, our source is a buffer, t-buffer-read.""" start="00:21:37.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""And note that because we're using with-current-buffer,""" start="00:21:45.113" video="mainVideo-transducers" id="subtitle"]] [[!template text="""if we go like this, if we go current-buffer, this will just work. So""" start="00:21:50.154" video="mainVideo-transducers" id="subtitle"]] [[!template text="""now let's... Well, that was odd. I should have done it like""" start="00:21:55.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that. There we go. So now we should make that a little smaller""" start="00:21:59.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""so you can see what it is. Now if we hit RET, we should get the""" start="00:22:02.160" video="mainVideo-transducers" id="subtitle"]] [[!template text="""right result. Okay, so there are 50,001 lines in this file,""" start="00:22:04.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""but the one extra one is the name of the headers, right?""" start="00:22:09.560" video="mainVideo-transducers" id="subtitle"]] [[!template text="""We want to process this file in more detail. So how can we do""" start="00:22:13.517" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that? Well, let's start by just automatically""" start="00:22:18.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""interpreting the results as CSV. If we do that, okay, well""" start="00:22:22.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""now we only have 50,000 entries as we expected, right?""" start="00:22:28.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Because it's going to pull out the header line. If we now say""" start="00:22:31.560" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we want to just filter out, you know, We only want Bob, right?""" start="00:22:36.760" video="mainVideo-transducers" id="subtitle"]] [[!template text="""So if... gethash, it was in the row of name. Each line here is""" start="00:22:42.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""made into, at least by default, is made into a hash map. So if""" start="00:22:53.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we go like this, we should see that. Okay, so 12,000 of these""" start="00:22:57.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""lines or thereabout belong to Bob.""" start="00:23:02.760" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Let's just move that over a little bit. Actually, I suppose we don't even""" start="00:23:05.640" video="mainVideo-transducers" id="subtitle"]] [[!template text="""need that anymore. I'll just keep that full size for you.""" start="00:23:13.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Okay, so all right, there's about 12,000 results for Bob of""" start="00:23:17.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""the 50,000. What's next? Well, we want to confirm,""" start="00:23:24.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we want to pull out everything,""" start="00:23:32.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""all of the in and the out entries.""" start="00:23:40.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Thank you. So, string to number, because we know that""" start="00:23:43.080" video="mainVideo-transducers" id="subtitle"]] [[!template text="""everything came in as strings. Unfortunately, the from-csv""" start="00:23:56.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""doesn't try to be smart at all, it's just pulling everything""" start="00:24:01.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""in as string values. If you want actual things to be""" start="00:24:03.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""numbers or whatever, that is up to you to do the parsing""" start="00:24:09.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""yourself. Okay, so we have those two values now. We know""" start="00:24:13.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that we saw from the data just a moment ago that you're only""" start="00:24:20.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""going to have a value in one column or the other. It's either""" start="00:24:23.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""going to be 0 in the empty one, or you're going to have some""" start="00:24:27.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""number in the other. So we know that we can just naively add""" start="00:24:29.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""them. If it was in, it would always be positive. So we'll just""" start="00:24:32.160" video="mainVideo-transducers" id="subtitle"]] [[!template text="""add that. But in the negative case, we want to just make it""" start="00:24:35.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""negative really briefly before we add them all together.""" start="00:24:41.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""let's now just prove to ourselves that we are sane here. What""" start="00:24:45.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""we're going to do is we're going to quickly go say take""" start="00:24:50.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""5 just to convince ourselves, and we'll go cons, and let's""" start="00:24:52.480" video="mainVideo-transducers" id="subtitle"]] [[!template text="""see if we get kind of results that make sense. Okay, these""" start="00:24:57.040" video="mainVideo-transducers" id="subtitle"]] [[!template text="""sort of make sense. It looks like you know Bob's got some big""" start="00:24:59.840" video="mainVideo-transducers" id="subtitle"]] [[!template text="""expenses here. If we take say 15, does it look any better?""" start="00:25:02.800" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Okay, looks like he had a payday. All right, good job Bob.""" start="00:25:07.680" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Let's get back in there. Now we only really care about""" start="00:25:10.320" video="mainVideo-transducers" id="subtitle"]] [[!template text="""adding the final result, right? So there we go. Add that all""" start="00:25:15.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""together and we'll see what we get in a moment. Okay, wow,""" start="00:25:20.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Bob's rich. Okay, so it looks like in his 12,000""" start="00:25:24.560" video="mainVideo-transducers" id="subtitle"]] [[!template text="""transaction, Bob has an overall net worth of $8.5 million.""" start="00:25:27.520" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Looking pretty good.""" start="00:25:32.280" video="mainVideo-transducers" id="subtitle"]] [[!template text="""So here's an example of how you can, particularly in Emacs""" start="00:25:34.440" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Lisp, how you can very easily just get a file, consider it the""" start="00:25:39.000" video="mainVideo-transducers" id="subtitle"]] [[!template text="""current buffer, and then just do whatever you want to it.""" start="00:25:42.960" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Note that there is sort of first-class support for both CSV""" start="00:25:45.880" video="mainVideo-transducers" id="subtitle"]] [[!template text="""and JSON, and then you have, and both of those bring in their""" start="00:25:50.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""values as hash maps, and then you're just free to do whatever""" start="00:25:54.360" video="mainVideo-transducers" id="subtitle"]] [[!template text="""you want and process them, potentially both writing them""" start="00:25:57.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""back out as CSV or JSON once again.""" start="00:26:00.440" video="mainVideo-transducers" id="subtitle"]]
[[!template new="1" text="""Issues and next steps""" start="00:26:03.240" video="mainVideo-transducers" id="subtitle"]]
[[!template text="""Some issues with transducers that can come up is""" start="00:26:03.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that one, a zip operator is missing, but I'm working on it.""" start="00:26:10.720" video="mainVideo-transducers" id="subtitle"]] [[!template text="""Two is that performance, particularly in Emacs Lisp, isn't""" start="00:26:14.920" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that great. It could be due to the sort of nested lambda calls""" start="00:26:19.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that have to occur internally, but the common Lisp""" start="00:26:24.120" video="mainVideo-transducers" id="subtitle"]] [[!template text="""implementation is quite good. and there's yet no support""" start="00:26:27.760" video="mainVideo-transducers" id="subtitle"]] [[!template text="""for parallelism. You can imagine that a lot of those steps""" start="00:26:32.240" video="mainVideo-transducers" id="subtitle"]] [[!template text="""you could potentially perform in parallel depending on the""" start="00:26:35.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""platform, but research has not yet gotten that far. Okay,""" start="00:26:38.560" video="mainVideo-transducers" id="subtitle"]] [[!template text="""that's all. Thank you very much. If you have any questions,""" start="00:26:44.400" video="mainVideo-transducers" id="subtitle"]] [[!template text="""please contact me.""" start="00:26:47.640" video="mainVideo-transducers" id="subtitle"]]
Captioner: sachac

Q&A transcript (unedited)

[[!template text="""Hopefully the internet goes well. It's a nice Monday""" start="00:00:00.000" video="qanda-transducers" id="subtitle"]] [[!template text="""morning here in Tokyo.""" start="00:00:07.560" video="qanda-transducers" id="subtitle"]] [[!template text="""Are we connected all right?""" start="00:00:32.000" video="qanda-transducers" id="subtitle"]] [[!template text="""Okay, I seem to be struggling still with my audio. 1 2nd""" start="00:00:37.880" video="qanda-transducers" id="subtitle"]] [[!template text="""calling. Yeah, you were muted for a moment there. Okay,""" start="00:00:40.880" video="qanda-transducers" id="subtitle"]] [[!template text="""there we are. Okay. All right. Sorry about that. I got a mute""" start="00:00:44.520" video="qanda-transducers" id="subtitle"]] [[!template text="""out my, my back office chatter. That's kind of distracting""" start="00:00:49.960" video="qanda-transducers" id="subtitle"]] [[!template text="""me a little bit. All right. Sorry. I may have lost the plot a""" start="00:00:55.120" video="qanda-transducers" id="subtitle"]] [[!template text="""little bit. I think I did. However, find the 1st question.""" start="00:00:58.080" video="qanda-transducers" id="subtitle"]] [[!template text="""I got pretty distracted by conversation backstage. Yeah,""" start="00:01:04.920" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: When I tried comparing transducers.el to cl-lib and dash (benchmark-compiled), I got the following results""" start="00:01:09.920" video="qanda-transducers" id="subtitle"]]
[[!template text="""no problem. So the first question here, someone's asking,""" start="00:01:09.920" video="qanda-transducers" id="subtitle"]] [[!template text="""when they first tried comparing transducers.el, the cl-lib""" start="00:01:15.880" video="qanda-transducers" id="subtitle"]] [[!template text="""and Dash bookmark compiled, and they give some detailed""" start="00:01:22.280" video="qanda-transducers" id="subtitle"]] [[!template text="""results we're sharing on the stream. Um, they expected""" start="00:01:27.960" video="qanda-transducers" id="subtitle"]] [[!template text="""transducers to be slower than CL loop, but faster than CL lib""" start="00:01:32.480" video="qanda-transducers" id="subtitle"]] [[!template text="""or dash. However, this isn't the case, any idea why. And so""" start="00:01:36.680" video="qanda-transducers" id="subtitle"]] [[!template text="""I'll, I'll come back into their data to show there's they're""" start="00:01:41.120" video="qanda-transducers" id="subtitle"]] [[!template text="""showing, um, you know, there's not a lot of detail on the, on""" start="00:01:43.640" video="qanda-transducers" id="subtitle"]] [[!template text="""the, on the use case here. We could certainly click through""" start="00:01:48.280" video="qanda-transducers" id="subtitle"]] [[!template text="""it, do it.""" start="00:01:52.200" video="qanda-transducers" id="subtitle"]] [[!template text="""Oh, I should've waited to zoom until I find my spot here.""" start="00:02:02.560" video="qanda-transducers" id="subtitle"]] [[!template text="""There we are.""" start="00:02:07.000" video="qanda-transducers" id="subtitle"]] [[!template text="""All right, so there's our example.""" start="00:02:13.640" video="qanda-transducers" id="subtitle"]] [[!template text="""Looks like we are doing a simple map and a sum.""" start="00:02:18.600" video="qanda-transducers" id="subtitle"]] [[!template text="""Mm-hmm. Yeah, that's right. Yeah, question about""" start="00:02:23.760" video="qanda-transducers" id="subtitle"]] [[!template text="""performance. So a case like this, a simple, I just want to rip""" start="00:02:29.240" video="qanda-transducers" id="subtitle"]] [[!template text="""through a collection of numbers and sum them all. That's a""" start="00:02:36.280" video="qanda-transducers" id="subtitle"]] [[!template text="""case where basically loop is always going to win because""" start="00:02:40.280" video="qanda-transducers" id="subtitle"]] [[!template text="""loop is optimized. This is true in both Emacs Lisp and in""" start="00:02:44.680" video="qanda-transducers" id="subtitle"]] [[!template text="""Common Lisp. For a case like this where you're not really""" start="00:02:51.320" video="qanda-transducers" id="subtitle"]] [[!template text="""doing two nested of chained calls, like you don't have many""" start="00:02:56.040" video="qanda-transducers" id="subtitle"]] [[!template text="""sort of what I was compositional steps. If you're just""" start="00:03:02.400" video="qanda-transducers" id="subtitle"]] [[!template text="""ripping through a collection of numbers, loop is always""" start="00:03:05.840" video="qanda-transducers" id="subtitle"]] [[!template text="""going to win. Transducers kind of shines when you have to do""" start="00:03:10.000" video="qanda-transducers" id="subtitle"]] [[!template text="""things that loop can't in terms of expressing yourself. So""" start="00:03:15.560" video="qanda-transducers" id="subtitle"]] [[!template text="""there are lots of different transducers that you can chain""" start="00:03:19.640" video="qanda-transducers" id="subtitle"]] [[!template text="""together. And in that case, you're kind of prioritizing""" start="00:03:22.560" video="qanda-transducers" id="subtitle"]] [[!template text="""developer time and developer happiness because you're""" start="00:03:27.080" video="qanda-transducers" id="subtitle"]] [[!template text="""able to yourself more clearly, whereas sometimes those""" start="00:03:33.040" video="qanda-transducers" id="subtitle"]] [[!template text="""kind of algorithms can get very hairy if you're just using""" start="00:03:36.400" video="qanda-transducers" id="subtitle"]] [[!template text="""loop. Now that sounds like I'm moving the goalposts, and""" start="00:03:40.680" video="qanda-transducers" id="subtitle"]] [[!template text="""there's really no excuse for these things not being as""" start="00:03:45.400" video="qanda-transducers" id="subtitle"]] [[!template text="""performant as possible. In this specific case, my guess is""" start="00:03:48.640" video="qanda-transducers" id="subtitle"]] [[!template text="""that the transducers is slower because it has to do a whole""" start="00:03:54.560" video="qanda-transducers" id="subtitle"]] [[!template text="""bunch of like inner function calls in order to actually do""" start="00:03:57.760" video="qanda-transducers" id="subtitle"]] [[!template text="""the adding and the collecting. So there's a lot of stuff that""" start="00:04:03.240" video="qanda-transducers" id="subtitle"]] [[!template text="""just the raw loop doesn't have to do, which transducers""" start="00:04:09.240" video="qanda-transducers" id="subtitle"]] [[!template text="""does. And so in this case, that's why it would be slower.""" start="00:04:12.120" video="qanda-transducers" id="subtitle"]] [[!template text="""All right, makes sense.""" start="00:04:20.440" video="qanda-transducers" id="subtitle"]] [[!template text="""Um... I cannot comment against Dash. And also a reminder""" start="00:04:29.080" video="qanda-transducers" id="subtitle"]] [[!template text="""that transducers both in CL and in Emacs Lisp here doesn't""" start="00:04:36.240" video="qanda-transducers" id="subtitle"]] [[!template text="""attempt to do any, you know, fun, you know, inner rewriting""" start="00:04:40.160" video="qanda-transducers" id="subtitle"]] [[!template text="""or, you know, what's called an Haskell fusion. Like if you""" start="00:04:44.920" video="qanda-transducers" id="subtitle"]] [[!template text="""have two different map steps, like in a row, it's not gonna""" start="00:04:48.240" video="qanda-transducers" id="subtitle"]] [[!template text="""see that and somehow fuse them internally. It's a fairly, in""" start="00:04:51.360" video="qanda-transducers" id="subtitle"]] [[!template text="""that sense, the implementation is just as is.""" start="00:04:55.160" video="qanda-transducers" id="subtitle"]] [[!template text="""to make it you know as raw fast as possible. The idea being""" start="00:04:59.680" video="qanda-transducers" id="subtitle"]] [[!template text="""that ergonomics is more important up front. Yeah, that's""" start="00:05:04.160" video="qanda-transducers" id="subtitle"]] [[!template text="""kind of a whole fascinating sub-panel, right? My theme this""" start="00:05:12.840" video="qanda-transducers" id="subtitle"]] [[!template text="""conference has been, oh, all these different things we""" start="00:05:17.520" video="qanda-transducers" id="subtitle"]] [[!template text="""should try to get sub-panels going for and use that. Maybe""" start="00:05:19.800" video="qanda-transducers" id="subtitle"]] [[!template text="""fill in the dev track or even have a third track or whatever.""" start="00:05:24.040" video="qanda-transducers" id="subtitle"]] [[!template text="""I'm not that concerned about the logistics of squeezing""" start="00:05:29.040" video="qanda-transducers" id="subtitle"]] [[!template text="""into the schedule so much. But anyway, interesting, I mean,""" start="00:05:31.520" video="qanda-transducers" id="subtitle"]] [[!template text="""to say.""" start="00:05:38.520" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: Do you know of any theoretical texts on transducers?""" start="00:05:40.840" video="qanda-transducers" id="subtitle"]]
[[!template text="""Did we already speak to theoretical texts? No, right? No,""" start="00:05:40.840" video="qanda-transducers" id="subtitle"]] [[!template text="""let's continue. Okay, so another question from the group.""" start="00:05:47.800" video="qanda-transducers" id="subtitle"]] [[!template text="""Do you know of any theoretical texts on transducers? My""" start="00:05:53.400" video="qanda-transducers" id="subtitle"]] [[!template text="""readme, particularly of the Common Lisp implementation,""" start="00:05:58.880" video="qanda-transducers" id="subtitle"]] [[!template text="""is the theoretical text on transducers. Rich Hickey has""" start="00:06:01.960" video="qanda-transducers" id="subtitle"]] [[!template text="""some YouTube videos which also come close. I mean, he""" start="00:06:06.160" video="qanda-transducers" id="subtitle"]] [[!template text="""invented the things. But in terms of having a full""" start="00:06:10.440" video="qanda-transducers" id="subtitle"]] [[!template text="""explanation of everything, it's my readme and it's also""" start="00:06:14.800" video="qanda-transducers" id="subtitle"]] [[!template text="""the...""" start="00:06:21.560" video="qanda-transducers" id="subtitle"]] [[!template text="""The info manual of Guile Scheme, their documentation on""" start="00:06:23.320" video="qanda-transducers" id="subtitle"]] [[!template text="""Surfy 171 is what I used to learn transducers and to""" start="00:06:28.560" video="qanda-transducers" id="subtitle"]] [[!template text="""re-implement them in other LISPs. So if you just want like a""" start="00:06:34.200" video="qanda-transducers" id="subtitle"]] [[!template text="""document explaining them, MyReadMe is actually the""" start="00:06:38.400" video="qanda-transducers" id="subtitle"]] [[!template text="""clearest that I've found. Awesome. Okay, next question.""" start="00:06:41.640" video="qanda-transducers" id="subtitle"]] [[!template text="""And I'm sorry, you gave a name, you referred to somebody's""" start="00:06:46.960" video="qanda-transducers" id="subtitle"]] [[!template text="""videos. Rich Hickey, the inventor of Clojure. Rich Hickey,""" start="00:06:50.120" video="qanda-transducers" id="subtitle"]] [[!template text="""thank you. Hope I got the spelling right, and maybe somebody""" start="00:06:55.440" video="qanda-transducers" id="subtitle"]] [[!template text="""can catch that and fix it. If not, I'll reach on. Thank you.""" start="00:07:00.400" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: Did you think about [compiler features, macros] viz your cl, fennel, elisp, porting of your transducers?""" start="00:07:04.720" video="qanda-transducers" id="subtitle"]]
[[!template text="""Reach on to the next question. Waters (Lazy Series in""" start="00:07:04.720" video="qanda-transducers" id="subtitle"]] [[!template text="""Lisp, late 70s) said this should have been done as an""" start="00:07:08.240" video="qanda-transducers" id="subtitle"]] [[!template text="""additional compiler feature in compilers, but if not, must""" start="00:07:12.800" video="qanda-transducers" id="subtitle"]] [[!template text="""be a macro package. Do you think about that vis your CL,""" start="00:07:16.800" video="qanda-transducers" id="subtitle"]] [[!template text="""Fennel, Elisp, porting of transducers? I think that""" start="00:07:21.440" video="qanda-transducers" id="subtitle"]] [[!template text="""there's definitely""" start="00:07:27.520" video="qanda-transducers" id="subtitle"]] [[!template text="""some Galaxy Brain Lisp author out there is probably smart""" start="00:07:28.520" video="qanda-transducers" id="subtitle"]] [[!template text="""enough to turn a bunch of this stuff into macros. I believe""" start="00:07:36.520" video="qanda-transducers" id="subtitle"]] [[!template text="""that's how the common Lisp library series works. It sees""" start="00:07:40.600" video="qanda-transducers" id="subtitle"]] [[!template text="""that you were calling map or whatever, and it actually knows""" start="00:07:47.120" video="qanda-transducers" id="subtitle"]] [[!template text="""that that's a special macro key. in order to be fast. I did not""" start="00:07:52.080" video="qanda-transducers" id="subtitle"]] [[!template text="""do that. The implementation as I have it is very simple and""" start="00:07:56.640" video="qanda-transducers" id="subtitle"]] [[!template text="""simplicity shouldn't be underestimated.""" start="00:08:01.840" video="qanda-transducers" id="subtitle"]] [[!template text="""I love it. What a nice succinct answer. Even I can manage to""" start="00:08:05.760" video="qanda-transducers" id="subtitle"]] [[!template text="""type that out as I scroll us to the next question.""" start="00:08:13.560" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: Does t-buffer-read provide a lazy stream that\'s linewise, or charwise, or do something else entirely?""" start="00:08:16.579" video="qanda-transducers" id="subtitle"]]
[[!template text="""So, does t-buffer-read provide a lazy stream""" start="00:08:16.579" video="qanda-transducers" id="subtitle"]] [[!template text="""that's line-wise or character-wise or do something else""" start="00:08:24.080" video="qanda-transducers" id="subtitle"]] [[!template text="""entirely?""" start="00:08:28.360" video="qanda-transducers" id="subtitle"]] [[!template text="""Okay, there are two functions. I showed""" start="00:08:29.019" video="qanda-transducers" id="subtitle"]] [[!template text="""t-buffer-read. There's also one called t-file-read,""" start="00:08:31.588" video="qanda-transducers" id="subtitle"]] [[!template text="""which does that. You actually have the buffer open,""" start="00:08:35.074" video="qanda-transducers" id="subtitle"]] [[!template text="""it's much more clever.""" start="00:08:38.683" video="qanda-transducers" id="subtitle"]] [[!template text="""t-buffer-read, I believe, is simpler. As long as you have an""" start="00:08:40.240" video="qanda-transducers" id="subtitle"]] [[!template text="""Emacs list, what is called the current buffer active. I'm""" start="00:08:46.000" video="qanda-transducers" id="subtitle"]] [[!template text="""fairly sure you're able to just call next-line on it. I don't""" start="00:08:52.080" video="qanda-transducers" id="subtitle"]] [[!template text="""believe that I'm doing anything fancy there, looking for""" start="00:08:56.680" video="qanda-transducers" id="subtitle"]] [[!template text="""line ends. I believe I'm just grabbing the next line and then""" start="00:08:59.480" video="qanda-transducers" id="subtitle"]] [[!template text="""processing that line-wise. Very good.""" start="00:09:04.000" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: Can the Elisp library be combined with the stream.el API or seq in general?""" start="00:09:09.424" video="qanda-transducers" id="subtitle"]]
[[!template text="""Can the Elisp library be combined with the stream.el API""" start="00:09:09.424" video="qanda-transducers" id="subtitle"]] [[!template text="""or seq in general? I would say that these libraries""" start="00:09:17.304" video="qanda-transducers" id="subtitle"]] [[!template text="""are completely orthogonal. You saw that everything""" start="00:09:22.831" video="qanda-transducers" id="subtitle"]] [[!template text="""was prefixed by t-.""" start="00:09:27.597" video="qanda-transducers" id="subtitle"]] [[!template text="""Basically, transducer is its own zone. However, one thing""" start="00:09:29.280" video="qanda-transducers" id="subtitle"]] [[!template text="""that I do in the common lisp, which is theoretically""" start="00:09:36.880" video="qanda-transducers" id="subtitle"]] [[!template text="""possible for the Emacs Lisp as well, is kind of like little""" start="00:09:40.240" video="qanda-transducers" id="subtitle"]] [[!template text="""shim libraries. So I provide, at least for Common Lisp, for a""" start="00:09:44.360" video="qanda-transducers" id="subtitle"]] [[!template text="""number of, you know, popular sort of third-party""" start="00:09:48.920" video="qanda-transducers" id="subtitle"]] [[!template text="""collection types, I provide an ability to use them as""" start="00:09:51.800" video="qanda-transducers" id="subtitle"]] [[!template text="""sources. Maybe that's what you mean. Like""" start="00:09:55.240" video="qanda-transducers" id="subtitle"]] [[!template text="""the built-in containers for Emacs Lisp are already""" start="00:09:59.560" video="qanda-transducers" id="subtitle"]] [[!template text="""supported. So, you know, a vector hash table and so on.""" start="00:10:04.440" video="qanda-transducers" id="subtitle"]] [[!template text="""make sense so i think what i heard there is yeah go ahead""" start="00:10:06.520" video="qanda-transducers" id="subtitle"]] [[!template text="""please sorry in terms of mixing like you know like for""" start="00:10:13.720" video="qanda-transducers" id="subtitle"]] [[!template text="""instance you know like seek dash map used in transducers""" start="00:10:17.880" video="qanda-transducers" id="subtitle"]] [[!template text="""we'll put it that way""" start="00:10:22.600" video="qanda-transducers" id="subtitle"]] [[!template text="""i was just gonna say i think it um it it sounds like you're""" start="00:10:28.120" video="qanda-transducers" id="subtitle"]] [[!template text="""saying Yeah, probably they are actually. We don't know yet""" start="00:10:31.880" video="qanda-transducers" id="subtitle"]] [[!template text="""about any places where they don't play nicely together. So""" start="00:10:37.200" video="qanda-transducers" id="subtitle"]] [[!template text="""quite possibly so. We can use sequence and transducers""" start="00:10:41.240" video="qanda-transducers" id="subtitle"]] [[!template text="""together, for example. As a source potentially, yeah. It's""" start="00:10:45.400" video="qanda-transducers" id="subtitle"]] [[!template text="""very easy because that just uses def generic. As long as you""" start="00:10:49.960" video="qanda-transducers" id="subtitle"]] [[!template text="""have a new, like if you have a new collection type, as long as""" start="00:10:54.160" video="qanda-transducers" id="subtitle"]] [[!template text="""you implement a def method for it somewhere, it'll just""" start="00:10:57.720" video="qanda-transducers" id="subtitle"]] [[!template text="""magically work with this library. That's the magic of...""" start="00:11:01.520" video="qanda-transducers" id="subtitle"]] [[!template text="""Yeah, as an Emacs user enjoying, you know, sort of the""" start="00:11:12.160" video="qanda-transducers" id="subtitle"]] [[!template text="""renaissance of new features it's had, or sorry, Emacs ERC""" start="00:11:18.440" video="qanda-transducers" id="subtitle"]] [[!template text="""user for chat. I've seen a lot of awesome stuff get done in the""" start="00:11:21.960" video="qanda-transducers" id="subtitle"]] [[!template text="""last couple of years with generic set. JP never was working""" start="00:11:27.800" video="qanda-transducers" id="subtitle"]] [[!template text="""on that. And like, that's just making me my eyes pop and go,""" start="00:11:32.120" video="qanda-transducers" id="subtitle"]] [[!template text="""wow, that does make a whole lot of things simpler, doesn't""" start="00:11:36.680" video="qanda-transducers" id="subtitle"]] [[!template text="""it? I think we're a lot of us running into generics and how""" start="00:11:39.280" video="qanda-transducers" id="subtitle"]] [[!template text="""that solves problems in Emacs.""" start="00:11:44.280" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: How does one debug a t-comp expression? Can you single step and see intermediate results of the different statements you declare?""" start="00:11:47.543" video="qanda-transducers" id="subtitle"]]
[[!template text="""How does one debug a t-comp""" start="00:11:47.543" video="qanda-transducers" id="subtitle"]] [[!template text="""expression? Can you talk in terms of single step,""" start="00:11:50.280" video="qanda-transducers" id="subtitle"]] [[!template text="""step-by-step, intermediate results of the different""" start="00:11:55.120" video="qanda-transducers" id="subtitle"]] [[!template text="""statements you declare? Yes. So in Common Lisp, this is""" start="00:11:58.480" video="qanda-transducers" id="subtitle"]] [[!template text="""and sly stickers and things like that. In Emacs Lisp, it's a""" start="00:12:08.760" video="qanda-transducers" id="subtitle"]] [[!template text="""little bit, shall we say, more difficult. For step""" start="00:12:12.920" video="qanda-transducers" id="subtitle"]] [[!template text="""debugging,""" start="00:12:19.560" video="qanda-transducers" id="subtitle"]] [[!template text="""so what comp does is comp internally, it should be a macro,""" start="00:12:20.480" video="qanda-transducers" id="subtitle"]] [[!template text="""but currently it's not, although there's work to improve""" start="00:12:25.680" video="qanda-transducers" id="subtitle"]] [[!template text="""that. It's doing an internal reduce and it's turning into""" start="00:12:28.840" video="qanda-transducers" id="subtitle"]] [[!template text="""one giant kind of composed lambda inside. So I don't know if""" start="00:12:33.560" video="qanda-transducers" id="subtitle"]] [[!template text="""step debugging would work there. However, we do have one""" start="00:12:37.480" video="qanda-transducers" id="subtitle"]] [[!template text="""function called log, which lets you inspect intermediate""" start="00:12:43.000" video="qanda-transducers" id="subtitle"]] [[!template text="""results. So you could technically use that to inject""" start="00:12:47.440" video="qanda-transducers" id="subtitle"]] [[!template text="""yourself somewhere into the transduction chain and, you""" start="00:12:50.760" video="qanda-transducers" id="subtitle"]] [[!template text="""know, halt or, you know, inspect the current value, et""" start="00:12:54.280" video="qanda-transducers" id="subtitle"]] [[!template text="""cetera. So you get a bunch of questions lined up. I think""" start="00:12:57.240" video="qanda-transducers" id="subtitle"]] [[!template text="""we're coming up, uh, within our last five minutes, uh,""" start="00:13:01.120" video="qanda-transducers" id="subtitle"]] [[!template text="""before some declared, uh, reset time that we have""" start="00:13:04.200" video="qanda-transducers" id="subtitle"]] [[!template text="""internally to just roll our closing credits, so to speak.""" start="00:13:07.920" video="qanda-transducers" id="subtitle"]] [[!template text="""Um, not that I would want to cut the question and answer""" start="00:13:11.920" video="qanda-transducers" id="subtitle"]] [[!template text="""short, but I might have to step away personally. But, um, as""" start="00:13:14.840" video="qanda-transducers" id="subtitle"]] [[!template text="""we discussed before, you can just kind of run the QA, however""" start="00:13:18.400" video="qanda-transducers" id="subtitle"]] [[!template text="""you want here. Um, or, or take questions offline if you'd""" start="00:13:21.520" video="qanda-transducers" id="subtitle"]] [[!template text="""like to answer them off the pad. And I just want to say one more""" start="00:13:24.880" video="qanda-transducers" id="subtitle"]] [[!template text="""time. Kitt said it managed later. Thanks again for your talk""" start="00:13:28.000" video="qanda-transducers" id="subtitle"]] [[!template text="""for dedicating the time to this live QA. And I think we can see""" start="00:13:30.960" video="qanda-transducers" id="subtitle"]] [[!template text="""by the many questions that are here. So I'll try to kind of""" start="00:13:35.760" video="qanda-transducers" id="subtitle"]] [[!template text="""flip us through as many of them as I can with our last couple of""" start="00:13:40.280" video="qanda-transducers" id="subtitle"]] [[!template text="""minutes, if that sounds good. Alternately, this might be a""" start="00:13:42.960" video="qanda-transducers" id="subtitle"]] [[!template text="""good time if you have kind of wrap it up, final thoughts, as""" start="00:13:48.400" video="qanda-transducers" id="subtitle"]] [[!template text="""Leo Sopanda saying. By all means, have at. Sure, thanks a""" start="00:13:52.080" video="qanda-transducers" id="subtitle"]] [[!template text="""lot. I'd say that if you are still curious, check out the""" start="00:13:58.400" video="qanda-transducers" id="subtitle"]] [[!template text="""read-me's because those have a lot of information,""" start="00:14:01.640" video="qanda-transducers" id="subtitle"]] [[!template text="""including a full description of the API and everything""" start="00:14:05.160" video="qanda-transducers" id="subtitle"]] [[!template text="""that's available.""" start="00:14:09.520" video="qanda-transducers" id="subtitle"]] [[!template text="""Otherwise, just give them a shot. Using these things is the""" start="00:14:10.720" video="qanda-transducers" id="subtitle"]] [[!template text="""best way to learn them, of course. I use them everywhere,""" start="00:14:16.600" video="qanda-transducers" id="subtitle"]] [[!template text="""basically, all across my Emacs list and all across my common""" start="00:14:21.640" video="qanda-transducers" id="subtitle"]] [[!template text="""list now. They get a lot of mileage. All right. You're""" start="00:14:24.720" video="qanda-transducers" id="subtitle"]] [[!template text="""speaking our language now. As Emacs users, all our ears poke""" start="00:14:29.840" video="qanda-transducers" id="subtitle"]] [[!template text="""up when you say, I'm getting a lot of mileage. I'm using it""" start="00:14:33.640" video="qanda-transducers" id="subtitle"]] [[!template text="""across everything. Every Emacs user has a story that""" start="00:14:36.040" video="qanda-transducers" id="subtitle"]] [[!template text="""harmonizes with that, I think.""" start="00:14:39.880" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: Is there a path for transducers to enable elisp processing of otherwise overly large datasets as if just normal Emacs \"buffers\" (i.e. just pulling one thing at a time so essentially stream-like under the hood but buffer-like in interface), with none of the usual perf issues with a traditional buffer structure?""" start="00:14:42.495" video="qanda-transducers" id="subtitle"]]
[[!template text="""So our next question, is""" start="00:14:42.495" video="qanda-transducers" id="subtitle"]] [[!template text="""there a path for transducers to enable Elisp processing or""" start="00:14:44.520" video="qanda-transducers" id="subtitle"]] [[!template text="""otherwise overly large data sets as if just normal Emacs""" start="00:14:48.600" video="qanda-transducers" id="subtitle"]] [[!template text="""buffers, i.e. just pulling one thing at a time. So""" start="00:14:54.000" video="qanda-transducers" id="subtitle"]] [[!template text="""essentially stream like under the hood, but buffer like an""" start="00:14:56.960" video="qanda-transducers" id="subtitle"]] [[!template text="""interface. I think that makes sense to me. with none of the""" start="00:15:00.720" video="qanda-transducers" id="subtitle"]] [[!template text="""usual performance issues, like as if, you know, the history""" start="00:15:03.520" video="qanda-transducers" id="subtitle"]] [[!template text="""with long files is what that brings to mind, I guess. Yes, so""" start="00:15:07.800" video="qanda-transducers" id="subtitle"]] [[!template text="""as you saw before, the withBufferRead sort of stream""" start="00:15:11.400" video="qanda-transducers" id="subtitle"]] [[!template text="""function does have to have the actual buffer in memory, and""" start="00:15:15.800" video="qanda-transducers" id="subtitle"]] [[!template text="""then you can go really fast. But there's another one with""" start="00:15:19.880" video="qanda-transducers" id="subtitle"]] [[!template text="""file read. Now, again, I haven't tried to optimize that yet.""" start="00:15:22.680" video="qanda-transducers" id="subtitle"]] [[!template text="""But in theory, it is able to read right from the underlying""" start="00:15:26.840" video="qanda-transducers" id="subtitle"]] [[!template text="""file without having to open it as a buffer first.""" start="00:15:30.120" video="qanda-transducers" id="subtitle"]] [[!template text="""Awesome. Ari, the performance issues mentioned, and that""" start="00:15:32.840" video="qanda-transducers" id="subtitle"]] [[!template text="""popped up recently in the list and forums, to what extent""" start="00:15:39.200" video="qanda-transducers" id="subtitle"]] [[!template text="""does tail call optimization and other mechanisms like""" start="00:15:43.480" video="qanda-transducers" id="subtitle"]] [[!template text="""inlining, garbage collection friendliness, and so on,""" start="00:15:46.960" video="qanda-transducers" id="subtitle"]] [[!template text="""could these alleviate issues, enable their use at little to""" start="00:15:50.160" video="qanda-transducers" id="subtitle"]] [[!template text="""no extra costs? I feel like we're leading the witness here,""" start="00:15:55.160" video="qanda-transducers" id="subtitle"]] [[!template text="""but I'm sure you see where we're going. Yeah, no problem. So""" start="00:15:58.440" video="qanda-transducers" id="subtitle"]] [[!template text="""in terms of tail optimization, that's already happening""" start="00:16:01.280" video="qanda-transducers" id="subtitle"]] [[!template text="""because the internal loop mechanism is using CL labels. And""" start="00:16:03.800" video="qanda-transducers" id="subtitle"]] [[!template text="""in Emacs Lisp, CL labels is just a macro that is like""" start="00:16:09.200" video="qanda-transducers" id="subtitle"]] [[!template text="""extremely tail recursive. So that's very, very fast. It's""" start="00:16:12.200" video="qanda-transducers" id="subtitle"]] [[!template text="""not tail recursive, but it's using like goto. So it's""" start="00:16:16.080" video="qanda-transducers" id="subtitle"]] [[!template text="""extremely, extremely fast, like the raw looping of it. So,""" start="00:16:19.040" video="qanda-transducers" id="subtitle"]] [[!template text="""okay, well then where does the slowness come from? It's""" start="00:16:22.520" video="qanda-transducers" id="subtitle"]] [[!template text="""probably coming from those lambdas and it's probably""" start="00:16:24.360" video="qanda-transducers" id="subtitle"]] [[!template text="""coming from, uh, like extra consing, extra allocation""" start="00:16:26.440" video="qanda-transducers" id="subtitle"]] [[!template text="""somewhere, which is, um, sort of what you were, what you're""" start="00:16:32.400" video="qanda-transducers" id="subtitle"]] [[!template text="""referring to with the GC friendliness. So perhaps there's""" start="00:16:36.000" video="qanda-transducers" id="subtitle"]] [[!template text="""some, um, um, yeah, some, like some fusion that I can do to""" start="00:16:38.520" video="qanda-transducers" id="subtitle"]] [[!template text="""speed it up. Yeah, that just sounds fascinating endlessly.""" start="00:16:45.200" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: Is there an option to read a csv/json and produce an alist or plist instead of a hash table for an entry?""" start="00:16:51.200" video="qanda-transducers" id="subtitle"]]
[[!template text="""Are there options to like read from a CSV, JSON, produce an""" start="00:16:51.200" video="qanda-transducers" id="subtitle"]] [[!template text="""alist or plist instead of hash table? Absolutely.""" start="00:16:55.560" video="qanda-transducers" id="subtitle"]] [[!template text="""Yes, I need to double check that, but we can read both CSV and""" start="00:17:01.680" video="qanda-transducers" id="subtitle"]] [[!template text="""JSON, and you should be able to just turn on the plist option.""" start="00:17:06.240" video="qanda-transducers" id="subtitle"]] [[!template text="""I will double check, but there's fairly free conversion""" start="00:17:10.360" video="qanda-transducers" id="subtitle"]] [[!template text="""between those three types because hash table is not always""" start="00:17:14.160" video="qanda-transducers" id="subtitle"]] [[!template text="""what you want. And actually, I suspect that slowness that we""" start="00:17:18.040" video="qanda-transducers" id="subtitle"]] [[!template text="""saw in the demo before was because it was allocating hash""" start="00:17:22.040" video="qanda-transducers" id="subtitle"]] [[!template text="""tables for every, like, all of the 50,000 lines. And had it""" start="00:17:24.560" video="qanda-transducers" id="subtitle"]] [[!template text="""been a plist, it would have been faster. Interesting, so""" start="00:17:29.240" video="qanda-transducers" id="subtitle"]] [[!template text="""maybe there's opportunities even if you end up with hash""" start="00:17:32.600" video="qanda-transducers" id="subtitle"]] [[!template text="""lists, but then they're shared strategically and you pay""" start="00:17:35.400" video="qanda-transducers" id="subtitle"]] [[!template text="""the cost of a little extra layer in there that buckets them""" start="00:17:38.800" video="qanda-transducers" id="subtitle"]] [[!template text="""together the way that we might group files by the first four""" start="00:17:42.040" video="qanda-transducers" id="subtitle"]] [[!template text="""characters in the file name once we've got a million files.""" start="00:17:46.440" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: Is the common lisp version ready for 'production' use? Is it complete enough and the API stable enough?""" start="00:17:50.520" video="qanda-transducers" id="subtitle"]]
[[!template text="""Anyway, is the Common Lisp version ready for production""" start="00:17:50.520" video="qanda-transducers" id="subtitle"]] [[!template text="""use? Do you want to comment on API stability? I use it all the""" start="00:17:54.480" video="qanda-transducers" id="subtitle"]] [[!template text="""time. I'm writing a game in Common Lisp right now, and I'm""" start="00:17:59.960" video="qanda-transducers" id="subtitle"]] [[!template text="""using transducers everywhere in there, and it doesn't even""" start="00:18:04.160" video="qanda-transducers" id="subtitle"]] [[!template text="""make a dent in the frame rate, and I'm using them""" start="00:18:08.560" video="qanda-transducers" id="subtitle"]] [[!template text="""extensively. Okay, well, I'll just read from chat. Thanks""" start="00:18:11.120" video="qanda-transducers" id="subtitle"]] [[!template text="""so much for the answers.""" start="00:18:15.360" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: Do we need a pre-written \"t-\" version for every already existing reducing function like + or is there a function to construct them from already defined reducer 2-arg functions?""" start="00:18:17.477" video="qanda-transducers" id="subtitle"]]
[[!template text="""Do we need a pre-written or t-minus""" start="00:18:17.477" video="qanda-transducers" id="subtitle"]] [[!template text="""version for every already existing reducing function,""" start="00:18:20.440" video="qanda-transducers" id="subtitle"]] [[!template text="""plus, as an example? Or is there a function that constructs,""" start="00:18:24.960" video="qanda-transducers" id="subtitle"]] [[!template text="""in my, I'm going to add the word, auto-visualifies them""" start="00:18:30.240" video="qanda-transducers" id="subtitle"]] [[!template text="""already, auto-defines or something, or just generically""" start="00:18:33.560" video="qanda-transducers" id="subtitle"]] [[!template text="""wraps function calls some way? already defined. This is""" start="00:18:37.320" video="qanda-transducers" id="subtitle"]] [[!template text="""basically fold. Some built-in functions like plus already""" start="00:18:42.240" video="qanda-transducers" id="subtitle"]] [[!template text="""function like reducers. It's a coincidence that they do""" start="00:18:49.400" video="qanda-transducers" id="subtitle"]] [[!template text="""that. But there's an example in the README. Max is one that""" start="00:18:52.600" video="qanda-transducers" id="subtitle"]] [[!template text="""does not act like that. For instance, maybe I could screen""" start="00:18:56.800" video="qanda-transducers" id="subtitle"]] [[!template text="""share later, but if you just type in plus one, If you call plus""" start="00:19:00.560" video="qanda-transducers" id="subtitle"]] [[!template text="""one in Emacs or Common Lisp, you get back one. It actually""" start="00:19:06.480" video="qanda-transducers" id="subtitle"]] [[!template text="""only needs one argument. If you only type plus, it actually""" start="00:19:10.520" video="qanda-transducers" id="subtitle"]] [[!template text="""gives you zero. Plus and multiple satisfy the API of""" start="00:19:15.120" video="qanda-transducers" id="subtitle"]] [[!template text="""reducers. But if you have one that doesn't, like the max""" start="00:19:20.840" video="qanda-transducers" id="subtitle"]] [[!template text="""function, and similarly, just type in plus as a function""" start="00:19:24.760" video="qanda-transducers" id="subtitle"]] [[!template text="""call, just plus with nothing else, and you'll see. No, as a""" start="00:19:28.760" video="qanda-transducers" id="subtitle"]] [[!template text="""function. zero will come out. This basically means it""" start="00:19:32.360" video="qanda-transducers" id="subtitle"]] [[!template text="""satisfies the reducer API. But a function like max does not.""" start="00:19:37.200" video="qanda-transducers" id="subtitle"]] [[!template text="""If you just type in max and then one, it won't work. Pardon me,""" start="00:19:43.160" video="qanda-transducers" id="subtitle"]] [[!template text="""it did. But if you type in max with nothing else, it wouldn't""" start="00:19:48.400" video="qanda-transducers" id="subtitle"]] [[!template text="""work.""" start="00:19:54.240" video="qanda-transducers" id="subtitle"]] [[!template text="""Hence, we have to wrap it in something like fold. I would say""" start="00:19:55.240" video="qanda-transducers" id="subtitle"]] [[!template text="""go look at the fold function. Right, which that I won't do.""" start="00:19:58.600" video="qanda-transducers" id="subtitle"]] [[!template text="""I'm not that well enough prepped. Darn it. Leo would have""" start="00:20:01.920" video="qanda-transducers" id="subtitle"]] [[!template text="""been here, but oh, well, you got me. Yeah, no problem. But""" start="00:20:04.840" video="qanda-transducers" id="subtitle"]] [[!template text="""fold is sort of the ultimate reducer function. Great. So is""" start="00:20:08.400" video="qanda-transducers" id="subtitle"]] [[!template text="""there, where was I? Here we go. We're way past this, right? So""" start="00:20:16.880" video="qanda-transducers" id="subtitle"]]
[[!template new="1" text="""Q: Is the compelling argument for transducers is that it's a better abstraction?""" start="00:20:26.320" video="qanda-transducers" id="subtitle"]]
[[!template text="""is the compiling argument for transducers that it's a""" start="00:20:26.320" video="qanda-transducers" id="subtitle"]] [[!template text="""better abstraction? It seems like there are concerns,""" start="00:20:34.280" video="qanda-transducers" id="subtitle"]] [[!template text="""objections, while problematically valid focused on""" start="00:20:38.880" video="qanda-transducers" id="subtitle"]] [[!template text="""implementation. Can this abstraction allow for advances""" start="00:20:42.400" video="qanda-transducers" id="subtitle"]] [[!template text="""in implementation? Yes, what I've basically done is mostly""" start="00:20:45.680" video="qanda-transducers" id="subtitle"]] [[!template text="""followed the pattern of usage that exists in Clojure and in""" start="00:20:50.560" video="qanda-transducers" id="subtitle"]] [[!template text="""Schemes SERP 171. In theory, the service level API is the""" start="00:20:56.000" video="qanda-transducers" id="subtitle"]] [[!template text="""same no matter where you're using this, and that's the idea.""" start="00:21:01.160" video="qanda-transducers" id="subtitle"]] [[!template text="""If you learn them in one list, you should be able to use them""" start="00:21:05.000" video="qanda-transducers" id="subtitle"]] [[!template text="""everywhere. Then what it's actually doing under the hood is""" start="00:21:08.040" video="qanda-transducers" id="subtitle"]] [[!template text="""free for us to change around. My implementations are mostly""" start="00:21:12.880" video="qanda-transducers" id="subtitle"]] [[!template text="""based on the scheme with a few alterations here and there.""" start="00:21:18.360" video="qanda-transducers" id="subtitle"]] [[!template text="""And in the Common Lisp case, like adding some Common Lisp""" start="00:21:23.680" video="qanda-transducers" id="subtitle"]] [[!template text="""isms""" start="00:21:27.080" video="qanda-transducers" id="subtitle"]] [[!template text="""to improve usage like UX a little bit. But overall, we are""" start="00:21:27.960" video="qanda-transducers" id="subtitle"]] [[!template text="""free to do whatever we want internally to speed up""" start="00:21:34.760" video="qanda-transducers" id="subtitle"]] [[!template text="""performance. I just haven't done that work. Awesome.""" start="00:21:38.960" video="qanda-transducers" id="subtitle"]] [[!template text="""Awesome. So here's where I have to, where we're getting the""" start="00:21:42.440" video="qanda-transducers" id="subtitle"]] [[!template text="""hook. We've just been pulled off the stream. The viewers""" start="00:21:47.240" video="qanda-transducers" id="subtitle"]] [[!template text="""just saw the crawl by as it sent us over to the other pad where I""" start="00:21:50.080" video="qanda-transducers" id="subtitle"]] [[!template text="""get to jump on and get involved with that now. But I can't""" start="00:21:54.080" video="qanda-transducers" id="subtitle"]] [[!template text="""thank you enough, Colin. Would you like me to stop the""" start="00:21:57.920" video="qanda-transducers" id="subtitle"]] [[!template text="""recording here? Any other comments you'd like to make? Uh,""" start="00:22:00.360" video="qanda-transducers" id="subtitle"]] [[!template text="""yeah, sure. Like, I mean, I'll stick around for any more live""" start="00:22:03.800" video="qanda-transducers" id="subtitle"]] [[!template text="""questions. I'm looking at both IRC and, and, um, uh, big blue""" start="00:22:06.440" video="qanda-transducers" id="subtitle"]] [[!template text="""button here. So if people have more questions, I'll hang""" start="00:22:10.640" video="qanda-transducers" id="subtitle"]] [[!template text="""around for a bit. I'm going to leave the channel open. I see""" start="00:22:13.240" video="qanda-transducers" id="subtitle"]] [[!template text="""you do have a few people in here, so I'm just going to go ahead""" start="00:22:15.960" video="qanda-transducers" id="subtitle"]] [[!template text="""and leave the recording. We can always trim it. Um, trim it""" start="00:22:17.840" video="qanda-transducers" id="subtitle"]] [[!template text="""up. If you, uh, let us know, Hey, the last 10 minutes weren't""" start="00:22:20.840" video="qanda-transducers" id="subtitle"]] [[!template text="""anything, you know, or whatever. No, no pressure, no""" start="00:22:24.280" video="qanda-transducers" id="subtitle"]] [[!template text="""worries, no mistakes. Thank you. Really appreciate you.""" start="00:22:27.000" video="qanda-transducers" id="subtitle"]] [[!template text="""Yep. Thanks a lot.""" start="00:22:29.840" video="qanda-transducers" id="subtitle"]] [[!template text="""OK, does anyone else have some questions? I see Mohsen in the""" start="00:22:31.960" video="qanda-transducers" id="subtitle"]] [[!template text="""BigBlueButton chat is asking how I made the video. So the""" start="00:22:48.400" video="qanda-transducers" id="subtitle"]] [[!template text="""presentation itself was done with RevealJS from Org Mode.""" start="00:22:52.840" video="qanda-transducers" id="subtitle"]] [[!template text="""So as you saw, I had a raw Org Mode buffer, which was""" start="00:22:59.080" video="qanda-transducers" id="subtitle"]] [[!template text="""which was the presentation itself, which I then just""" start="00:23:03.640" video="qanda-transducers" id="subtitle"]] [[!template text="""exported with a few certain settings, a few""" start="00:23:09.320" video="qanda-transducers" id="subtitle"]] [[!template text="""customizations. And then for screen recording, I used OBS,""" start="00:23:11.760" video="qanda-transducers" id="subtitle"]] [[!template text="""which worked flawlessly on Arch Linux. I used Sway,""" start="00:23:15.920" video="qanda-transducers" id="subtitle"]] [[!template text="""Wayland, and all of that. So all of that just worked, which""" start="00:23:19.720" video="qanda-transducers" id="subtitle"]] [[!template text="""was very impressive. Where do the HTML host the""" start="00:23:23.160" video="qanda-transducers" id="subtitle"]] [[!template text="""presentation? I don't have that presentation hosted""" start="00:23:28.000" video="qanda-transducers" id="subtitle"]] [[!template text="""anywhere.""" start="00:23:51.960" video="qanda-transducers" id="subtitle"]] [[!template text="""I'll look at the.""" start="00:23:52.600" video="qanda-transducers" id="subtitle"]] [[!template text="""I don't see that.""" start="00:23:59.120" video="qanda-transducers" id="subtitle"]] [[!template text="""Here it is. So we've got the file here as well.""" start="00:24:00.080" video="qanda-transducers" id="subtitle"]] [[!template text="""Looks like that's it for questions, basically.""" start="00:24:08.160" video="qanda-transducers" id="subtitle"]] [[!template text="""Yep, and it looks like everyone's moved on for now. Let's""" start="00:24:11.000" video="qanda-transducers" id="subtitle"]] [[!template text="""see. I mean, it would be so this is answering lounge 81 on IRC.""" start="00:24:14.920" video="qanda-transducers" id="subtitle"]] [[!template text="""Yeah, like, if we really wanted to go that hardcore, maybe""" start="00:24:20.160" video="qanda-transducers" id="subtitle"]] [[!template text="""there's some like C level stuff that we could""" start="00:24:24.600" video="qanda-transducers" id="subtitle"]] [[!template text="""you know, significant demand for such a thing. You know, so""" start="00:24:29.440" video="qanda-transducers" id="subtitle"]] [[!template text="""far there hasn't been such demand, but maybe there will be in""" start="00:24:36.120" video="qanda-transducers" id="subtitle"]] [[!template text="""the future. Yeah, perhaps there's some custom stuff we""" start="00:24:39.240" video="qanda-transducers" id="subtitle"]] [[!template text="""could do.""" start="00:24:42.520" video="qanda-transducers" id="subtitle"]] [[!template text="""And otherwise, magic one.""" start="00:24:43.040" video="qanda-transducers" id="subtitle"]] [[!template text="""Well, it looks like some people are quite happy with this.""" start="00:24:48.600" video="qanda-transducers" id="subtitle"]] [[!template text="""All right. That's about what I've seen. So why don't we end it""" start="00:25:00.600" video="qanda-transducers" id="subtitle"]] [[!template text="""here? I think I can control the recording from my end. If I""" start="00:25:14.960" video="qanda-transducers" id="subtitle"]] [[!template text="""pause it, will that work? All right. Thank you, everyone.""" start="00:25:19.840" video="qanda-transducers" id="subtitle"]]
Questions or comments? Please e-mail [emacsconf-org-private@gnu.org](mailto:emacsconf-org-private@gnu.org?subject=Comment%20for%20EmacsConf%202023%20transducers%3A%20Transducers%3A%20finally%2C%20ergonomic%20data%20processing%20for%20Emacs%21)