Hi! My name is Eduardo Ochs.
I'm the author of an Emacs package
called eev - its home page is here...
and this video is about this thing here, `eeks',
that plays a series of `k'eys "`s'paced in time",
like a pianola... like here.
If I execute this thing here with `M-e'
it waits a bit, then it simulates that the user is
typing RET, then RET again, then "Hello".
Let me demonstrate it.
That's it. Let me undo this thing.
This thing, `eeks', is not very interesting in itself,
but the implementation is very interesting,
and the video is going to be about it.
Now let me make a longer introduction.
Eev is a very obscure Emacs package
that treats Emacs in a very weird way...
I mean, it treats Emacs not as an editor,
but as something that started as a Lisp environment,
and then became an editor.
So, the best way to start learning Emacs is by
starting by learning the basics of Lisp.
The tutorial of Eev starts by this...
It starts by explaining how these Elisp expressions
work... and then it shows how to use Elisp
expressions as hyperlinks...
and then it explains that some things like this one
work like hyperlinks, but not really hyperlinks
that go to somewhere else... they work like
browser buttons that perform an action.
For example, if I execute this thing here, `eek',
it acts as if the user had pressed a series of keys -
like this...
I can run it several times...
and I can undo its output...
anyway, let me go back...
So, this, `eek', is one of the first things that
appears in the tutorial...
and this, `eek2', is a variant that does exactly
the same thing, but is implemented in a different way...
and this thing here, `eeks', is based on this new implementation.
I said that these two expressions here do the same thing...
but they do it in different ways.
The first thing that this function here, `eek2', does,
is that it receives an argument,
and if that argument is a string it converts that argument
to a list of events.
The conversion is done by this function here, `ee-eek2',
and when `ee-eek2' receives this string here
it returns this series of events here -
note that each event is a pair...
The `t' is not important now, but the rest of the event is...
in this case, the rest of the event is always a number.
So, the RET becomes 13, the uppercase H becomes a 72, etc...
This function here, `eek2', can receive either a string
or a series of events in this internal format...
In most of my examples, I am going to use this representation
here, in which the events are numbers, instead of this
representation here, in which the events are written in a
way that is more readable for humans.
For example, this thing here is "sort of clearly" a RET,
this thing here is clearly an uppercase H, and so on....
Let's test... this thing here inserts a "Hello"...
and this thing here also inserts a "Hello".
And note that I can split this series of events into a series
of calls to `eek2'... so this `progn' here is going to do
the same thing as this big `eek2' here. Let me try... ta-da.
This is a copy of the expression in the previous slide...
and let's now change it to something that uses timers.
This first `run-at-time' here makes this small `eek2' here
be executed at some time in the future...
Then this second `run-at-time' does the same thing
for this small `eek2' here, and so on...
and, so... the first expression, the first (eek2 ...),
is run at this position in time, 0.25,
the second one is run at this position, 0.75,
the other one is run at this position, 1.25, and so on.
let's test...
And this last expression here, (eeks ...), does the same thing,
but it is written in a much more compact form. Let's test...
Now let's see how this expression, (eeks ...),
generates this (progn ...) here.
This (progn ...) here does exactly the same thing as this
(eeks ...) - it plays a series of keys spaced in time.
Let's test.
And, by the way, one of my favorite ways of explaining
eev to people is in this page here...
it is here, in the section "Taking apart",
and it is this sentence:
eev is more like a toy that is _slightly interesting_
if you play with it for a few seconds,
and _much more interesting_ if you open it and
take its pieces apart to see how everything works.
So... this is something that is not very useful yet,
but the implementation is very interesting,
so let's take everything apart.
I will need to shrink the font a bit.
Here is the source code. I'm interested in this part here
of the source code, that explains a big macro...
Let me scroll it to center it on the screen...
These kinds of macros, my `let*-macro's, are
explained here - in this section of a tutorial...
but the explanation is very technical.
Note that it uses `cl-loop' at a few points...
Beginners with Lisp probably have never seen `cl-loop',
so if they want to learn it...
I added a section about `cl-loop' in one of my tutorials...
it is here - It has lots of examples...
Let me explain what this macro does.
It expects three arguments, here, that are usually a number,
another number, and a string, and then some "code".
What it does is that it It generates a big `let*', here...
and it runs the "code" in the context in which
not only these three variables are defined, t0, dt, and str,
but also these other things here are defined.
Let me show how this works. Let's try this thing here...
When I run this, `t0' is going to be 0.25,
`dt' is going to be 0.5,
and `str' is going to be "RET Hello",
and the "code" is just `tevents'.
And `tevents' is this thing here...
the "code" is going to be `tevents',
so the code is going to return the value
of the variable `tevents', that is defined here.
Let's try...
This line here shows that the value of `tevents'
is this sequence of pairs
made of a `t' and an event...
This thing here does something similar, but `ppushs'
is something that starts with a `progn'
and then it has a series of "pushes"... let's see...
Well, I think of these things as "pushes", but whatever...
And this thing here - with `prats' -
generates this thing here, that starts with a `progn'
and then it's a series of `run-at-time's.
I abbreviated run-at-time as R-A-T,
so "rats" is `run-at-time's.
This thing here, with (length tevents)...
is going to show the length of the variable `tevents'...
`tevents' is a list, and this shows the length of that list.
This thing here, with `find-eppp'...
is a trick for pretty-printing the value of `ppushs'...
if I run this, it creates a temporary buffer
with `ppushs' pretty-printed in a certain way.
`ppushs' is here, is a program with a series of pushes,
and this temporary buffer is read-write...
For example, I can test this thing here by typing M-e,
and it runs a RET, and then it types "Hello" immediately -
It doesn't wait.
And if I run this - with (find-eppp prats ...) -
it also creates a temporary buffer, but now with a
`progn' and a series of `run-at-time's.
And if I test this `progn', I get this.
The program simply creates a series of timers that,
well, Emacs stores those timers and run those timers at the
right time, so the `progn' returns immediately...
you can see the result here...
take a look here - at the echo area -
it is going to disappear very quickly...
and then Emacs executes the little `(eek2 ...)'s
that are stored in the timers.
So these (ee-let*-macro-eeks ...) give us ways
to explore all these temporary variables here,
in the `let*'...
And remember that the value of `prats'
is a list made of a `progn' and a series of `run-at-time's -
this one here...
And if instead of displaying `prats' with a pretty-printer
I eval `prats'...
I execute this `progn' here
instead of executing the program in the temporary buffer...
Let's try... one, two, three, go -
And this is a demo of something that I'm going to show later...
that is what happens when instead of giving a string to...
sorry, instead of sending a string to the variable `str',
we send a nil...
And, spoiler -
when str is nil,
then this (if (not str) ...) is executed here...
it calculates a certain distance,
and then it calculates a number of f8s,
and it generates a string made of a series of f8s.
Let's see...
`dist' is 12, `nf8s' is 11,
and `f8s' is this thing here.
Now let's go down a bit.
No, sorry, let's go up first.
This is the beginning of this file.
It first defines the "macro" here,
then it defines the function "eeks",
that plays a series of keys spaced in time,
and then it has some tests here - in "test-eepitch".
Let me scroll down...
Here is the definition of `eeks'...
It says that it's like `eek' and `eek2', but it
plays the events spaced in time, blablablah...
And the definition is just this thing here.
It calls the big macro above, `ee-let*-macro-eeks',
with a certain value for t0,
a certain value for dt,
and the str is _usually_ a string holding a series of keys...
then it simply runs (eval prats) here,
exactly like in this
demonstration here...
remember that the value of `prats' is here
and if we `(eval prats)' we get this...
So it has this `(eval prats)' here
and it has this `(list (length events) 'events)',
that gives a better return value.
Let's test it here...
Let me make everything start a bit later
so that we can see the return value here - in the echo area...
let's try...
It says "(6 events)" and then it runs the events.
So that's it.
I mentioned that when we don't give a string to `eeks'
it does something special. It calculates a certain distance,
distance, it calculates a certain number of f8s, and it
plays this series of f8s.
This is... anyway, no, sorry, I'm not going to explain
why this is useful to me - I'm just going to demonstrate it.
Let's create a test block here...
sorry, I need a smaller font...
sorry, let me use a very small font here...
Let me create a target buffer running a shell...
this is a comment because it starts with two red stars...
and it says "Type f8 on the line below and wait".
Let's do that - f8 here.
It generates a series of f8s spaced in time
and it "presses", between quotes,
these `f8's, and it executes this thing here.
So instead of...
Usually, I have to press all the `f8's,
but in certain occasions, I want to make the computer
"press these `f8's itself" like a pianola in a certain rhythm,
and this is what this `(eeks 0.5 0.5)' does.
Let's try it again...
Oops - If I execute this with f8...
Ah - sorry. Whatever.
And that's it!
Please do try this at home!...
The instructions are in this page here.
This page is going to have all the instructions,
the subtitles of the video, etc...
And the easiest way to try the code
without downloading anything...
of course I'm supposing that you have eev...
but the easiest way is to just execute this exp,
that is in that page...
it downloads a local copy - no, sorry,
it downloads this page here, this elisp file here,
to a temporary buffer,
and then you can execute it with just `M-x eval-buffer'
and then you can try all these tests here.
And that's it! Thanks...
Actually, a) thanks for your attention
and b) thanks to Bruno Macedo, because many things
in this video were inspired by our conversations.
That's it. Bye!