<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Dmitry Petrov</title>
 <link href="https://can3p.github.io/atom.xml" rel="self"/>
 <link href="https://can3p.github.io"/>
 <updated>2022-10-27T10:45:44+02:00</updated>
 <id>https://can3p.github.io</id>
 <author>
   <name>Dmitry Petrov</name>
   <email>dpetroff@gmail.com</email>
 </author>

 
 
  
  
  <entry>
    <title>From the basics to complexity and back</title>
    <link href="https://can3p.github.io//blog/2022/10/26/no-html/"/>
    <updated>2022-10-26T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2022/10/26/no-html</id>
    <content type="html">&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;/h2&gt;

&lt;p&gt;Do you remember good old nineties? It was a time when websites looked, well,
like a mess on today’s standards and browsers were not really helping with
that. You could make a career on being able to craft round corners that looked
ok on netscape, opera and internet explorer, becase it wasn’t trivial at all.&lt;/p&gt;

&lt;p&gt;What happened next was a huge movement that gave us all the web standards we
love and use today. It was also the time of pixel perfect design.  I still
remember how I would alt-tab between the browser window and photoshop or use an
exported png as a background in order to fulfill designer’s expectations.&lt;/p&gt;

&lt;p&gt;Not only that, we were all busy crafting our home pages, making them look
unique and awesome. Or take MySpace or Livejournal.com where you could make
your journal unique by adjusting the templates.&lt;/p&gt;

&lt;p&gt;That was an era full of creativity but then it was gone and what’s interesting
many people haven’t noticed.&lt;/p&gt;

&lt;h2 id=&quot;facebook&quot;&gt;Facebook&lt;/h2&gt;

&lt;p&gt;Disclaimer - I was a livejournal.com engineer back in the days. It’s hard to
overestimate how much users cared about their styles. Every time a change
had to be introduced (e.g. fb like button) we had to carefully craft it in all
the different designs that were supported by the system, help out popular blogs
that had their own custom styles and still got it wrong once in a while.&lt;/p&gt;

&lt;p&gt;The same users who hated us for any changes on their beloved blogs and
eventually switched to facebook and other huge platforms that are there now
didn’t care a single bit about the fact that they had near zero level of
control on the presentation there. As it turned out, if the content is good and
all your friends are around you don’t need that much. And facebook is actually
not very extreme in this situation, you can craft your post in markdown while
other platforms don’t give users almost any way to control the layout and
everyone is just fine with it.&lt;/p&gt;

&lt;p&gt;Instagram users can only write plaintext only without any formatting and it
even doesn’t look nice on the phone and you have to insert an image even in
case you only want to post a message and you know what? Nobody cares. I can
only talk about russian speaking segment of the platform but the amount of
activity there is just crazy. People not only share cat photos there, but also
buy and sell stuff, write analytical articles, do workshops and trainings
without any problem. The fact that from time to time they have to literally
edit the photo and put a text overlay on top of it (and it often looks like
some of the worst myspace examples) doesn’t bother anyone. And all the
communication is solved with a restricted instagram account or a closed
whatsapp group.&lt;/p&gt;

&lt;p&gt;Or take twitter. Before we got threads some users posted images with the text
to work around the text limitation. That’s crazy, you cannot even copy/paste or
quote this thing. And no one cared. As it turns out, the main reason people are
on the internet is social interactions and if the design is good enough and,
most importantly, others can discover your content, the rest simply does not
matter that much.&lt;/p&gt;

&lt;p&gt;Do you remember how your friend’s website looks like? You don’t probably,
because it actually doesn’t matter that much and what even more probable they
don’t even have one. But you probably do remember some catchy text they wrote.
I’m a huge fan of Dan Luu’s blog and eventhough it’s literally just html in
it’s simplest form, just try to open &lt;a href=&quot;https://danluu.com/simple-architectures/&quot;&gt;one&lt;/a&gt; of his posts in the reader mode
in firefox and you’ll find out that it looks awesome with zero bloat you
usually get. It doesn’t even use react!&lt;/p&gt;

&lt;p&gt;Same goes for your favorite rss reader where all articles look nice even though
the author doesn’t have to do anything with it. You can go on github and open a
file with this very post and it will render just fine for you, zero html or
styling involved from my side.&lt;/p&gt;

&lt;h2 id=&quot;html&quot;&gt;HTML&lt;/h2&gt;

&lt;p&gt;Now let’s discuss what unites social platforms, rss readers and firefox reader
mode.&lt;/p&gt;

&lt;p&gt;If you compare all of them you will see that they all do not support any of the
complex markup of html or formatting of the css. In most cases users cannot
even chose where to put the images, it’s all decided by the platform.&lt;/p&gt;

&lt;p&gt;If you think about it all the platforms have become real browsers within a
browser and what makes them so effective is specifically that the users are
limited in the way the present content. Why so? Because simple markup makes it
trivial to render the content anywhere ranging from a desktop browser to a
fridge.  Not just that, it’s absolutely up to platforms how to render the
content in the first place. The photos are at the bottom now, but they can as
well be put anywhere else, page previews can be generated and a whole lot of
other stuff that would be simply not possible if the constraints were not
there.  It’s also important to note again that users usually don’t care too
much about the presentation of the information on the platform if it reaches
their audience, because this is the only point.&lt;/p&gt;

&lt;p&gt;If you still have any doubts, take Gemini proctocol as an example. The
&lt;a href=&quot;https://gemini.circumlunar.space/software/&quot;&gt;list&lt;/a&gt; of clients on the project page suggests that eventhough building a
proper web browser is no small task, the rendering part of it is definitely
trivial especially if you compare it to let’s say a multiyear effort by
Serenity OS community to get ther web browser to a proper level. They’re doing
wonders really, but the complexity and scale of specs and the variety of real
world use cases requires a tremendeous amount of work to get it right to an
extent that even bigger players like Microsoft and Opera have given up on
supporting their own rendering engines.&lt;/p&gt;

&lt;p&gt;Another angle to take is that there is another problem that’s being discussed
every now and then - as time goes by, the web is not getting any faster. We get
better connection only to get more bloated websites as a result. A basic
website can deliver megabytes and megabytes of styles and scripts just because
an unlucky dependency made webpack include a half of npm registry into the
build. You can fight against it, but that’s really an uphill battle, everything
is designed in a way to make including more stuff simpler, not the opposite.
The CSS design is fundamentally hostile to any cleanups, which most people who
tried to remove a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div p a&lt;/code&gt; selector from the code base would agree with.&lt;/p&gt;

&lt;p&gt;Just to make the final point: none of the dialects used by the platforms is
standardised. Markdown has a standard, but except from really basic stuff that
covers suprisingly big amount of cases, the rest is not really well supported
everywhere and again that doesn’t bother too many people.&lt;/p&gt;

&lt;h2 id=&quot;so-what&quot;&gt;So what?&lt;/h2&gt;

&lt;p&gt;Now that I’ve built up this argument, the question is also there - what can
we do with this observation?&lt;/p&gt;

&lt;p&gt;First and foremost we can start by acknowledging the fact that html, css and
js are gone as creativity tool for most of people - too much complexity and
bloat.&lt;/p&gt;

&lt;p&gt;Second, suprisingly small amount of features is needed for an ordinary person
to start producing amazing work. We’ve already been there with Macromedia
Flash.  If somebody still remembers Action script 1/2, it was a joke not only
because of the lacking features, but also because most of it was often embedded
into the frames in a very chaotic manner by people who barely knew how to code.
The most surprising fact is it still looked and worked amazingly. Every topic
on hacker news related to flash instantly gathers a lot of comments from users
longing for that content.&lt;/p&gt;

&lt;p&gt;The beauty of it was not only that there were tons of it, but also that it was
really amateur, anyone could do it and my stong belief lies in fact that no big
corp or data science can beat that. I think that’s what we’re missing now.&lt;/p&gt;

&lt;p&gt;You may also not admit it but you probably also think about good old days
when creating a website was as easy as copying an index.php file to a remote
ftp.&lt;/p&gt;

&lt;p&gt;We can leave web technologies for big guys who want to make amazon.com or google
docs, however we certainly need something else to unleash a new era of creativity
on the web that also doesn’t mean getting into another walled garden.&lt;/p&gt;

&lt;p&gt;My bet would be on the fat clients. They are pretty tough to evolve sometimes,
just remember how long we’ve been waiting for the official death of IE6,
but at the same time that’s the place where the real changes can happen and
rss, browser reader mode, gemini and flash proved it. My bet is that the
new thing should be simple again, yet complete enough to allow artistic
expression.&lt;/p&gt;

&lt;p&gt;Should it be one more flash generation or something else? I don’t know, but
what’s certain is that it doesn’t need to have that much to become the next
big thing. It will get big and standardized and bloated in time but before
that we will have some very fun years.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks a lot to Artur Burtsev and Artem Tyurin for the comments and ideas.&lt;/em&gt;&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Mcdonald's and web servers</title>
    <link href="https://can3p.github.io//blog/2022/10/04/mcdonalds/"/>
    <updated>2022-10-04T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2022/10/04/mcdonalds</id>
    <content type="html">&lt;p&gt;When you have a hammer, everything looks like a nail they say. That’s true to
an extent, but actually some patterns are so generic that both problems and
solutions seem to be the same. If you think otherwise just let me to tell you
a story of one fast food restaurant.&lt;/p&gt;

&lt;p&gt;It’s quite rare to find an inefficiently running place in the Netherlands and
almost impossible to get into a real mess and the whole restaurant part of
mcdonald’s business is based on the efficiency of the operations. Given this I
was quite surprised to get into a restaurant that literally almost collapsed
under the load. It’s worth saying that currently 99% of the orders are being
made via the terminals near the entrance.&lt;/p&gt;

&lt;p&gt;When I entered the place I has not been initially alarmed by a huge crowd near
the pick up area and I made my order only to realize that not only my order did
not show up on the screen but it looked like there were 13 orders before mine
waiting for their place on the screen.&lt;/p&gt;

&lt;p&gt;The kitchen seemed to be in a bad situation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There was a pile of hamburgers of all sorts ready for collection&lt;/li&gt;
  &lt;li&gt;A guy responsible for drinks kept doing his part and a tray in front of
him had drinks for something like 30 orders.&lt;/li&gt;
  &lt;li&gt;Orders were served in a very slow pace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given such a strange mixture I started inspecting the area in hope to find the
source if the problem. Soon it became clear that the root cause was a kids
party! What happened was that parents ordered something like 30 Happy meals and
fulfilling this order cause such a degradation in the throughput that the
restaurant was not able to keep up with new orders anymore.&lt;/p&gt;

&lt;p&gt;What made the problem worse was that since kitchen kept preparing orders that
it has not been able to serve in time, all these poor hamburgers caused a lot
of contention and penalized the throughput even more. Not only this, since a
lot of food got cold before it was served and customers waited for way too
long, the restaurant had to deal with chargebacks as well as replacements where
some of the food had to prepared again which meant a lot of waste being
generated instead of a useful output. And since the situation was quite
extraordinary, all the workers were in the kitchen with no staff to clean waste
bins and after they got overflowed it of course took considerably more time to
clean them.&lt;/p&gt;

&lt;p&gt;Apparently the workers were doing their job but the workers were doing their
job but the system itself was out of tune. If you think of it, the problem
could have been mitigated early:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If we’re not talking about ad hoc decision, the chain could have trained
workers to move quicker to achieve vertical scaling&lt;/li&gt;
  &lt;li&gt;Some of the workers could have moved to serving the requests instead of
completing them in order to increase the throughput&lt;/li&gt;
  &lt;li&gt;The problem could have been made less severe if the restaurant simply did not
allow to do such gigantic orders. If they were split into a set of smaller
ones the load could have been distributed more evenly.&lt;/li&gt;
  &lt;li&gt;Alternatively, they could have put rate limiting in place by disabling some
of the ordering terminals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What’s the morale there? If you do a web service on a bigger scale, make sure
that the rate-limiting is in place, requests are time-scoped and monitoring
is in place to let you know in advance that your service needs some optimization
or scaling. Have fun!&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>On bronze age collapse</title>
    <link href="https://can3p.github.io//blog_draft/2022/04/24/bronze-age-collapse/"/>
    <updated>2022-04-24T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog_draft/2022/04/24/bronze-age-collapse</id>
    <content type="html">&lt;hr /&gt;

&lt;p&gt;As it often happens, many patterns repeat themselves in many different
areas, maybe in slightly different forms and shapes. You can have a look
everywhere.&lt;/p&gt;

&lt;p&gt;One of the theories behind the Late Bronze Age Collapse is that the
civilization at time came into some sort of &lt;a href=&quot;https://www.youtube.com/watch?v=bRcu-ysocX4&quot;&gt;perfect storm&lt;/a&gt; - natural
disasters, hunger caused wars that broken trade links between the countries
and made it impossible to source all the components needed to made bronze
and the civilization fell apart with a frigthening speed. Fast forward to
late Rome scenario - historians still argue about the reasons of West Rome
demise and I’m by no means an expert in the area but one theories that
sounded plausible to me was that &lt;a href=&quot;https://acoup.blog/2022/02/11/collections-rome-decline-and-fall-part-iii-things/&quot;&gt;the efficient efficient trade was one
of the main reasons&lt;/a&gt; that elevated the Rome&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>What do you read?</title>
    <link href="https://can3p.github.io//blog/2020/04/24/what-do-you-read/"/>
    <updated>2020-04-24T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2020/04/24/what-do-you-read</id>
    <content type="html">&lt;p&gt;There are some habits that I developed during my life
that sometimes give me pure moments of joy. The reason
that happens is mostly due to the fact that I don’t
know exactly what I get and I don’t do it to achieve
something, it’s plain curiosity.&lt;/p&gt;

&lt;p&gt;One of them is browsing maps. Earlier that were paper
maps. now it’s Google Maps but the principle stays the
same - I just scroll through the map in any direction
and sometimes find some interesting stuff (like this
&lt;a href=&quot;https://upload.wikimedia.org/wikipedia/commons/2/29/Round_house1.jpg&quot;&gt;circular house&lt;/a&gt;
in Moscow).&lt;/p&gt;

&lt;p&gt;Another one is reading signs or just looking around
at cars and buildings. Here is &lt;a href=&quot;https://www.deepbv.nl/&quot;&gt;one&lt;/a&gt;
of my recent findings. How cool is that?&lt;/p&gt;

&lt;p&gt;Or here is one more - whenever I see someone reading
a book I immediately start googling the name and
in many cases I end up reading something &lt;a href=&quot;https://en.wikipedia.org/wiki/Plagues_and_Peoples&quot;&gt;fascinating
and new&lt;/a&gt;
to me.&lt;/p&gt;

&lt;p&gt;What unites all of this is pure randomness of the process -
you don’t know what you get. It can be total crap, it
can be a pearl, you don’t know and this is the essence.&lt;/p&gt;

&lt;p&gt;Anyone can name lot’s of random events that turned life
upside down and that was the best thing happened to them.
Moreover, in many cases it’s completely fair to say that
random events played a far more important role that
any specific plans.&lt;/p&gt;

&lt;p&gt;What also unites all this pure randomness is that it’s
impossible to optimize for, conversion is too low. Amazon
or Spotify will be able to give you most relevant &lt;em&gt;similar&lt;/em&gt;
items to what you already have, but noone will be able
to recommend you something that you don’t know you like,
it’s always a bubble of some sorts.&lt;/p&gt;

&lt;p&gt;It’s completely fine to be in such bubbles, since the
experience is predictable and enjoyable, but
you can already feel that you’re missing out and you
don’t know what. Every single time randomeness disappears
in one or the other area we gain something but at the
same time we lose.&lt;/p&gt;

&lt;p&gt;The closest to my heart is of course books. Since it’s
all the smarphones and e-readers now, you cannot really
say what people read, and that’s tragic, because some
of them can read a book that will change one’s life. And
no one will ask.&lt;/p&gt;

&lt;p&gt;What I think would be really cool is to reverse this trend.
Let every e-reader or smartphone to publish information
via bluetooth to nearby devices. There is no point in
that except feeding curiosity of others, but that already
sounds fascinating, right? To do something for no reason,
except letting others to experience a pure joy of randomness.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>On Web without HTML</title>
    <link href="https://can3p.github.io//blog_draft/2020/04/07/no-html/"/>
    <updated>2020-04-07T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog_draft/2020/04/07/no-html</id>
    <content type="html">&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;/h2&gt;

&lt;p&gt;Note: I use markdown throughout the text but you can replace it with you favorite comparable
markup language of choice.&lt;/p&gt;

&lt;p&gt;Internet is a very interesting place now. We have moved from a totally decentralized place
to the one with very few giants in all fields (Amazon, Facebook, Instagram, Reddit, Medium).
There is a lot of discussion about this state of affairs and I don’t want to discuss the
obvious.&lt;/p&gt;

&lt;p&gt;What I want to discuss is another wave, that came our way without too much publicity - simple
markup languages like markdown. One might wonder - what’s so interesting about it? I think
this wave is not accidental and goes along with big social platforms and decline of html/css
hype.&lt;/p&gt;

&lt;p&gt;Let’s start from the last point. Anyone who has been in web development comunity for a while
remembers good old days when the movement for web standards was strong and the community
itself was very vibrant. Lots of new hacks, tricks and technologies, a lot of discussions.
I might very well be out of the bubble now, but from my observations it’s all gone now.
People were obsessed with pixel perfect design back then, just remember all
possible ways to get perfect round corners or opacity or any other design element. Where
is it now? It’s still around us of course, but in many cases it lost it’s relevance. And
the reason is not only because we got flexbox and new features in css. Most notably,
content and community is the king, especially when visuals are good enough.&lt;/p&gt;

&lt;p&gt;I still have my active account on Livejournal.com where people were people were obsessed
with keeping their diary in their style and were very vocal about it. It was mainstream
back then, but it isn’t anymore. It appeared that once you have people around, design becomes
much less relevant. Instagram, Twitter and Facebook are good examples there. Instagram is
extreme in this case - only plain text is allowed, no customizations at all. On twitter
users used to post text as pictures to work around text limit. On Facebook there’s a bit
more freedom in sence that you can use markdown but that’s it. None of them allow to do
any significant customizations and all of them have lots of users that post valuable content,
connect to the loved ones or do business there. With zero css and not caring about pixel
perfect design a single moment. If you think about it, all these platforms became browsers
in browser and all the markup and styles are out of play there. The reason why it’s so
is because this form of content is much easier to handle. There are no special cases,
everything is under platform control. It’s much easier to do a mobile version,
it’s much easier to do an app on any type of hardware/software.&lt;/p&gt;

&lt;p&gt;Next, let’s have a look on Markdown. It’s not about it in isolation, of course. It’s rise
is only partially thanks to it’s simplicity, a lot of it is due to platforms like Facebook,
Github or Gitlab, that render it in a good way along with links, which means you can have
a site with hyperlinks for no cost and it will be accessible to users. As you see, it’s
the same here, no css involved, all is needed is a browser in browser, that converts the
markup to some tolerable form.&lt;/p&gt;

&lt;p&gt;And we also have a huge movement of static websites. HTML and CSS are still there, but underneath
it’s often the same markdown. We do design only once (like on this particular blog) and then
work only with markdown, because it’s easy and good enough and what happens underneath is
a conversion of markdown to a good enough html/css, I’ll put it into the same bucket
with github/gitlab.&lt;/p&gt;

&lt;p&gt;And to make things even more interesting - all major web browsers have a reader mode now
that takes any wonderful well designed page with ads, noise etc takes the main content
and renders it into a simple easy to read form. From what’s supported one might argue that
an opposite conversion takes place - from HTML/CSS back to markdown with it’s limited
support for headers, lists images and a bit more. Or you can &lt;a href=&quot;https://danluu.com/simple-architectures/&quot;&gt;take&lt;/a&gt; any of Dan Luu’s posts
which look bad only till you enable the reader modex in firefox which makes them near perfect.&lt;/p&gt;

&lt;p&gt;We didn’t talk about money so far. The situation improved quite a lot from early days in sense
that there are solutions like Stripe now that allow to implement payment solutions with much
less effort and there is Patreon that provides a distinct monetization strategy by allowing
to support a person on ongoing basis. There are even projects like &lt;a href=&quot;https://substack.com&quot;&gt;Substack.com&lt;/a&gt;,
that allow to do paid newsletters. And we thought newsletters were dead.&lt;/p&gt;

&lt;p&gt;The last part is community and there is a split there too. While back in the days everybody
was busy integrating something like Disqus comments (this blog included), I have yet to see
lively discussions happening in standalone blogs. Discussions happens in places where people
actually hang out, hence they’re all on Hacker News, Reddit or big social networks now. Of
course it’s possible to name some exceptions, but it’s safe to agree that it’s exactly that - an exception.&lt;/p&gt;

&lt;p&gt;What that means is that you don’t need HTML/CSS to be popular and facilitate discussions. Two
good exteme examples of this are &lt;a href=&quot;https://danluu.com/&quot;&gt;blog&lt;/a&gt; of Dan Luu and &lt;a href=&quot;http://paulgraham.com/articles.html&quot;&gt;essays&lt;/a&gt;
of Paul Graham.&lt;/p&gt;

&lt;p&gt;One can look at this trend from a different angle too. Semantic markup, precise styling, strict
protocols were all about effectiveness. How do we describe page in the best way, that it will
be technically possible to render it like we want. How do we integrate payments in our platform
in the best possible way? It’s still there, but what is also there now is that people find new
ways of working with restricted platforms that do not support a lot/any of it.&lt;/p&gt;

&lt;p&gt;I can definitely say this for the russian part of the Instagram, but I guess it should be the same
for other segments too. Users sell things, facilitate courses or workshops and handle a lot
of things manually or by combining existing tools. Want to make a payment? Just transfer money
on someone’s bank account. No need to make a separate courseware - yet another closed account
or Whatsapp group is enough. Everything that software engineers hoped to solve in a perfect
way was successfully hacked around by ordinary people with much more limited technical skills.&lt;/p&gt;

&lt;p&gt;The last thing that’s worth talking about in this long intro is a concept of ownership and
distribution. On classical websites author controled all of it. And you can see copyright
notes all over the place. Does it really work on big social networks? Distribution is lost
for sure. Once you make a post, it’s visibility is certainly out of your control. It’s up to
facebook to put it in front of millions of users or never show it to anyone and the only
tracking you can get is the one that’s provided by the platform and you only can trust it,
the same goes for any other platform and there is nothing users can do about it. And what’s
interesting people are fine with it too, even with reposts, since there is little left of
the concept of a “place” of a person on the platform.&lt;/p&gt;

&lt;p&gt;To sumarize, what people really want is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A decent (not perfect) way to render content with some simple formatting&lt;/li&gt;
  &lt;li&gt;Discovery&lt;/li&gt;
  &lt;li&gt;Community&lt;/li&gt;
  &lt;li&gt;Some way to do payments that does not necesserily have to be integrated into a platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And as you can see, it appeared that there is a very limited need in complex markup language,
styling and scripting in this usecase. Moreover it can now be considered as a burden,
since there is a lot to learn there, a lot of platforms to support, a hosting and related
costs to worry about with modest benefits compared to social networks solution.&lt;/p&gt;

&lt;h2 id=&quot;no-html&quot;&gt;No HTML&lt;/h2&gt;

&lt;p&gt;Now, having said all of this, it’s time to think about if we still need html now in the form
as it is now. For complext usecases we certainly do - it’s hard to implement amazon in markdown
without extending it the extend that it becomes similar to HTML, but for many other cases
it might be a really good fit. And the fact that it’s constrained is more of an advantage than
a drawback.&lt;/p&gt;

&lt;p&gt;For example, it’s much better for the bandwidth, can be easily stored offline. What’s more
we move from pixel perfect design which requires effort from authors to browsers that
are now responsible for all visual aspects.&lt;/p&gt;

&lt;p&gt;Can we imagine a situation where we skip all the HTML/JS/CSS and serve a page in a language like
markdown directly? It’s like going to pre-CSS era, but it doesn’t sound aweful anymore.&lt;/p&gt;

&lt;p&gt;Also, it’s kind of solved on big social platforms, however can we take this bit out of them
and put it in the wild?&lt;/p&gt;

&lt;p&gt;Features we would like to preserve are identity, distribution and payments. None of it
necessarily requires any complex markup to be solved, especially if we assume that browser
takes care of it. So, what if we return to a concept of browser fully owning the presentation
layer?&lt;/p&gt;

&lt;p&gt;We can also split different aspects apart. It’s not required for all these features to be
on one platform in case page itself has some metadata that can be used by the browser, and that
also opens up an opportunity to build the best possible experience on this particular platform.&lt;/p&gt;

&lt;p&gt;For example, if you want to sell the access to an article, you can encrypt it with your
favorite payment provider and mention required data in the header, so that browser can display
all necessary information to the user, facilitate a payment using open protocol with this provider
and get a key to decipher it.&lt;/p&gt;

&lt;p&gt;In the same way it’s possible to have a link to a site that facilitates discussions, however
it’s up to browser to check your favorite one to see if a post is being discussed there.&lt;/p&gt;

&lt;p&gt;If you think about, taking monetization strategy and discussion apart also means that you don’t
really care if a page has been really served from your website, since there is no dependency
on pageviews anymore.&lt;/p&gt;

&lt;p&gt;All this does not mean that it has to be self hosted, but it might be coupled with rss to make
it discoverable and different content aggregators can use it to show you personalized suggestions.&lt;/p&gt;

&lt;p&gt;Moving all the rendering functionality to the browser also means that it becomes a point of
extension. If you want to support a different type of content, you might need to have a different
render, which might even be the same html/css, but it will be only one of possible options.&lt;/p&gt;

&lt;p&gt;There is still place for them of course, especially for big application that will never fit into
a constrained solution, and making a separate extension for all of them is probably not an option.&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Complexity tower and a holy grail of web development</title>
    <link href="https://can3p.github.io//blog_draft/2020/01/03/complexity-tower/"/>
    <updated>2020-01-03T00:00:00+01:00</updated>
    <id>https://can3p.github.io/blog_draft/2020/01/03/complexity-tower</id>
    <content type="html">&lt;p&gt;I’ve been thinking about it for a while. Sometimes I was so bad that I wanted
to quit programming, sometimes it got better and I though it makes sense again
(sort of). But in reality, it’s bad, it’s really really bad.  After watching
Jonathan’s Blow video &lt;a href=&quot;https://www.youtube.com/watch?v=pW-SOdj4Kkk&quot;&gt;“Preventing the collapse of Civilization”&lt;/a&gt; I
decided to finally convert some words into bytes. What am I talking about?&lt;/p&gt;

&lt;p&gt;Let’s just observe how a perception of complexity kept changing with
time in regards to different concepts of web development.&lt;/p&gt;

&lt;p&gt;If you remember good old days, it was a pretty easy thing to make a website.
You edit an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; page till it looks fine, then you copy it to a
cheap hosting with any tool you like and you’re done. If you really wanted
to have some interactivity you could go wild and write some perl or PHP.&lt;/p&gt;

&lt;p&gt;Maybe you also remember how local development worked back in the days. My
weapon of choice back then was &lt;a href=&quot;http://www.wampserver.com/en/&quot;&gt;WAMP&lt;/a&gt; or something similar.&lt;/p&gt;

&lt;p&gt;And that was it, mostly. There were several not so easy things with this
approach. For example, you had to manually fiddle with the database,
and CSS + HTML was a special kind of art due to amazing number of bugs
and inconsistencies across different platforms.&lt;/p&gt;

&lt;p&gt;Then the pendulum started it’s swing.&lt;/p&gt;

&lt;p&gt;A couple of great things that happened back then was appearance of jQuery
and other similar libraries like mootools and standards movement together
with bulletproof css frameworks like Twitter Bootstrap that allowed
an average developer to abstract away from the browser internals and
just code a website.&lt;/p&gt;

&lt;p&gt;On the other side of the stack framworks like Zend, Django or Rails turned
backend development on it’s head and raised productivity many times, because
data management, templates and routig became dead simple.&lt;/p&gt;

&lt;p&gt;It was cool and nice, however with the rise of complexity on the frontend
jQuery was not enough anymore. It was dead simple to do stuf like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$('#block').slideUp()&lt;/code&gt; or even &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$('#popup').show()&lt;/code&gt;, but if you wanted
to wire up something really complex, all that rather quickly turned into
a spaghetti. Just remember all that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script.js&lt;/code&gt; files that contained
everything as one wall of text. The same goes for template engines -
it was quite simple to split them into a couple of files, but a big
number of templates was troublesome - most of them were text based (just
remember that time when you spent hours hunting a missing closing tag),
and scope was a real problem: with many layers it was harder to say
where you got this specific variable from and when it’ll disappear.&lt;/p&gt;

&lt;p&gt;Web development was booming and everyday someone popped up with a new idea on
how to do it in a simple way and the whole web-development ecosystem turned
into a “Hey, check out this cool hack” kind of community.&lt;/p&gt;

&lt;p&gt;What happened next was containers, rise of single page applications,
dynamic data formats like json or yaml (only old guys love xml, yes)
and proper package management.&lt;/p&gt;

&lt;p&gt;They made it so simple to share and compose, that you could barely
resist. Now you could get even more efficient with what you do, especially
with libraries and frameworks like React or Angular or, later Vue.&lt;/p&gt;

&lt;p&gt;The only catch was that the day when you could just save a file and upload
it to a cheap hosting was gone. Build step became a necessity and as
things progressed even more, React and friends became a sort of default
solution for any kind of application. You could easily do time machine
kind of tricks, because your state was not managed with Redux, but a
simple animation was not so simple anymore and having a popup was not
something you would really like to do.&lt;/p&gt;

&lt;p&gt;Moreover since your frontend turned into a full js app, serving an html
page became something that you really need to think about. In the
end a popular solution was to run a node.js app alongside with a
backend app and consume backend apis both from the node.js app and from
the frontend, we got isomorphic apps.&lt;/p&gt;

&lt;p&gt;It was really cool, the only thing that you now had two runtimes instead
of one, had to monitor and understand them and their interaction.
The pain was not immediate, especially if you run them locally, but
docker and docker compose made it so dead simple to run a lot of stuff
in an easy manner, that you could hardly understand how such setups
worked and you had to in the end because out of many compomnents the
probability was quite high to have at least on of them brokend at
least in one of the many environments.&lt;/p&gt;

&lt;p&gt;And then we got kubernetes that made it quite simple to run such
complex apps, but it was a beast by itself and now you had a magical
thing running and it was wired up by a pile of yaml files that
looked so easy to write but were fantastically hard to reason about
when you had a lot of them.&lt;/p&gt;

&lt;p&gt;Now you could do a lot of stuff. The only thing that you had trouble
with was to get a development environment and speed like it was
with WAMP stack.&lt;/p&gt;

&lt;p&gt;One more side effect that we got was that backend frameworks somehow
lost a lot of ground. Since everything is an spa, you can’t simply
use Django forms now, you’ll have to leave that alone and do your
form with react, which is not as simple. And don’t forget to do
validations! And all this had to be joined with an api that you now
had to write instead of just writing  post handler there.&lt;/p&gt;

&lt;p&gt;The other moment is a cloud push. It works amazingly well and
allows you to run the craziest setup if you manage to wire it up
but what it makes challenging is having an idea about your
bill next month.&lt;/p&gt;

&lt;p&gt;There was so much frustration at this point that it’s quite reasonable
that the whole frontend world embraced Typescript to get a bit
more sanity in everything happening.&lt;/p&gt;

&lt;p&gt;And btw, I’m not even talking about all the regulations that came
our way - think about all the EU regulations that made it kind of
safer for us, except that everybody agrees on anything anyway just
to get thought that pile of cookie related popups to read an
article. One can say that we’re in a better place as customers now,
but I think that all big guys will implement/find a workaound
to it anyway, so in reality we just drastically increased the load
on all small companies that have to deal with it. When did you
ask a random website to delete all your data last time?&lt;/p&gt;

&lt;p&gt;Now, I think it’s time to make a step back and think for a bit.&lt;/p&gt;

&lt;p&gt;Everything I mentioned so far was really necessary for some projects.  I mean
it - docker is really powerful, you can do wonders with new js framworks,
however what’s not there is the simplicity of doing things in a way that was
achievable.&lt;/p&gt;

&lt;p&gt;And while it’s really nice that all new stuff takes literally seconds
to start, it’s not what we’re doing every day. What we’re doing is
maintenance and iterations on a codebase. If your setup requires
200mb of javascript and some more python to run with some yaml on
top, it’s not simple to maintain and quite frustrating to deploy. No
wonder developers go for solutions like Heroku just to relieve some
pain.&lt;/p&gt;

&lt;p&gt;And while all big companies require these tools we should not. If
what you’re building is a not so big website, you should be able
to do it in a simple to maintain and deploy manner with as little
moving parts as possible, while still being able to take an advantage
of last years of web development.&lt;/p&gt;

&lt;p&gt;It’s time to think again and come up with and promote a solution that
will be dead simple in implementation and still allow to evolve to a
complex one if necessary but not before.&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Part 5: Gems and pitfalls</title>
    <link href="https://can3p.github.io//blog/2018/06/30/client-writeup-gems/"/>
    <updated>2018-06-30T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2018/06/30/client-writeup-gems</id>
    <content type="html">&lt;p&gt;All the points below summarize the things I enjoy using in Common Lisp
and are not especially the ones preferred or recommended by
community. Also proof reading showed that many of the examples were
already mentioned earlier, but I still think it’s good to have them
all in one place for the reference. Here you go.&lt;/p&gt;

&lt;h3 id=&quot;generic-functions&quot;&gt;Generic functions&lt;/h3&gt;

&lt;p&gt;If I were to choose the single most amazing feature of the language I
would choose generic functions. Why?&lt;/p&gt;

&lt;p&gt;The reason is that decoupling them from the object resulted in amazing
freedom and flexibility when using them.&lt;/p&gt;

&lt;p&gt;First of all, generic function dispatch against all it’s arguments and
second, all of the arguments can be of any type and not some object
hierarchy.  What the means that it’s very easy to build recursive
implementations that do some preliminary work with arguments and then
call the same function again which makes generic function dispatch
differently and execute another implementation. I used this trick a
lot.&lt;/p&gt;

&lt;p&gt;One example of such a definition could be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch-posts&lt;/code&gt; function.
What we know that we want to fetch posts to some kind of database,
which has a store. Such a definition means that we can have objects of
both types at our disposal. With described technique it’s simple to
make it work as desired:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defgeneric&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fetch-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fetch-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;set-credentials&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fetch-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fetch-store&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fetch-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;store&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;do-actual-fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch-posts&lt;/code&gt; against the database implementation just
extracts store from it and call the function again which execute
another method and now we can fetch posts both with store or database
instance without any effort.&lt;/p&gt;

&lt;p&gt;Another bonus from decoupling is that you are free to create any new
function and implement it against any object hierarchy. It may be not
even a hierarchy but just a set of types.&lt;/p&gt;

&lt;p&gt;Remember function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-hash-table&lt;/code&gt; that I used before? It is
implemented as a generic function.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defgeneric&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to-hash-table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With such a definition you can just implement it against any type that
you want without any restrictions. When I needed it for the database I
wrote this implementation:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to-hash-table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;key-sub&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make-hash-table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dolist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;funcall&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key-sub&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And when later it became clear that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;store&amp;gt;&lt;/code&gt; class can also benefit
from one, I just implemented it:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to-hash-table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;store&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make-hash-table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dolist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;-&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt;
                         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:itemid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:sync-ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Having said that I need to mention that this freedom applies to
external functions as well and many of the external libraries expose
precisely generic functions to control their behavior. The good
example is with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plump&lt;/code&gt; library when it was enough just to implement
function against the set of types without doing any work at all with
types.&lt;/p&gt;

&lt;p&gt;Last bit that I want to praise is auxiliary methods. This is just
incredible because of flexibility it gives. When I just started coding
I wanted to have database saved on any operation. Post created? Save!
Updated?  Save! Deleted? Save!&lt;/p&gt;

&lt;p&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:after&lt;/code&gt; modified allowed me to completely decouple the logic
and for example, the main implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;publish-post&lt;/code&gt; knew nothing
about saving to the database, but meanwhile, in the other package it
was as easy as&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publish-post&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And it’s done.&lt;/p&gt;

&lt;p&gt;Another use case can be found in markdown handling that I did. What I
wanted to do was to write a file with links pointing to the local files
and translate them to the real urls on the markdown compilation phase.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;render-span-to-html&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:before&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eql&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'inline-link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;encoding-method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cl-journal.db:get-by-fname&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cl-journal::*posts*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cadr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;record&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cadr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cl-journal.db:url&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:before&lt;/code&gt; method does not in general change the behavior of the main
code, however, it has access to all it’s arguments and in this case I
check if whatever is passed as an url can be resolved to a post url
from the database and modify argument accordingly. After that, I don’t
really need to touch library logic, it works as usual.&lt;/p&gt;

&lt;h3 id=&quot;streams&quot;&gt;Streams&lt;/h3&gt;

&lt;p&gt;Streams are really powerful. Languages that don’t have them (let’s say
javascript) often end up with ugly hacks or additional code or with
string concatenation all over the place and with streams all the api
gets streamlined immediately and the neat thing is that it’s still
totally under control.&lt;/p&gt;

&lt;p&gt;I benefited from this fact quite a lot. One example is saving data to
the file. Common Lisp provides a function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprint&lt;/code&gt; that prints data
structure in a nice way. Default is stdout, but stream can also be
passed as an argument. Given that saving state to file becomes as
simple as:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;save-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-open-file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts-file*&lt;/span&gt;
                       &lt;span class=&quot;ss&quot;&gt;:direction&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:output&lt;/span&gt;
                       &lt;span class=&quot;ss&quot;&gt;:if-exists&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:supersede&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-standard-io-syntax&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pprint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to-list&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And what’s cool is that whatever is printed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprint&lt;/code&gt; by the
definition can be consumed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt;. Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt; invokes Common Lisp
reader, it has some security implications for sure. In my use case
however, I didn’t really bother about this part, because I’m a sole
user of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl-journal&lt;/code&gt; and database reading and writing logic is
written in such a way, that I can change it to whatever format anytime
without any friction.&lt;/p&gt;

&lt;p&gt;Another usage was showcased in part about html to markdown conversion.
Serialization logic in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plump&lt;/code&gt; simply prints results in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*stream*&lt;/code&gt;
variable that by default happens to be equal to stdout (if I’m not
mistaken) and in the method I could capture any part of output simply
by temporary setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*stream*&lt;/code&gt; to the other stream and then doing
whatever I found fit with the output.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-output-to-string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;*stream*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;across&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;children&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;serialize-object&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format&lt;/code&gt; works with streams too, I’ll talk about it later.&lt;/p&gt;

&lt;h3 id=&quot;special-variables&quot;&gt;Special variables&lt;/h3&gt;

&lt;p&gt;Special variables is yet another super powerful concept. What’s
special about them is that they use dynamic binding instead of a
lexical one and that means the changing the value of such not only for
the current scope but for all the code that is called from there.&lt;/p&gt;

&lt;p&gt;This gives a powerful weapon to penetrate through layers of
abstractions without a cost of passing this variable through or making
it global in the true sense of this word.&lt;/p&gt;

&lt;p&gt;In case of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plump&lt;/code&gt; special variable provided an easy way to control
the output.&lt;/p&gt;

&lt;h3 id=&quot;loop&quot;&gt;Loop&lt;/h3&gt;

&lt;p&gt;If were to write this post a couple of months ago I wouldn’t mention
loop at all. I came to Common Lisp from clojure and my brain was
really fixed on immutable data structures, hence I tried to avoid any
imperative constructs. Another moment was that loop had a
controversial perception across community some of which preferred
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iterate&lt;/code&gt; and the rest tried to avoid both.&lt;/p&gt;

&lt;p&gt;The turning point was when I started writing fetch and sync logic and
I had to iterate there a lot in many different ways. I tried loop once
then twice and then I ended up using it all over the place. Why so?&lt;/p&gt;

&lt;p&gt;Simply because loop unify all different looping constructs that other
languages have like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while&lt;/code&gt; or even &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; in one unified call
and in addition to that gives local bindings all an easy way to
execute body only under certain conditions and return from any point,
and I’m sure there are lots of things I’m not aware of.&lt;/p&gt;

&lt;p&gt;Let me show a couple of especially impressive examples from the code.
Here is a function that prints a list of files that need to be merged:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;get-merge-candidates&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;restore-source-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fetch-store&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to-hash-table&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;visited&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make-hash-table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:itemid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;older-than-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:sync-ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;collect&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;progn&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a - ~a&quot;&lt;/span&gt;
                          &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt;
                          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a - ~a (~a)&quot;&lt;/span&gt;
                          &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt;
                          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Iteration goes over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(events store)&lt;/code&gt;, two other &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt;s work just like
local bindings. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;when&lt;/code&gt; part uses local bindings as well as bindings
from function scope to understand if this particular post needs to be
merged. If this check succeeds &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collect&lt;/code&gt; executes next form and
appends it to a resulting list to be returned. In this body we update
a list of visited posts so that any duplicate post is ignored.&lt;/p&gt;

&lt;p&gt;Next example is silly but show loop can serve as a loop construct.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;lj-get-server-ts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; scary hack to get server ts in a single timezone&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;labels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lj-getevents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:lastsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;;; something big enough to have empty lookup&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;do&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a~%&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;older-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;older-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt; serves as a local binding there and whenever we want to finish
we can simply call return and it’s argument will be the return value.&lt;/p&gt;

&lt;p&gt;Yet another loop combo allows an easy traversal of plist with
destructuring of key/value pair:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post-file&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:fields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cddr&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;do&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a: ~a~%&quot;&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;string-downcase&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;symbol-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                 &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From examples above you can already spot the greatest weakness of this
macro - it’s syntax is so diverse that using it looks natural only
when you read it, writing tends to be more in the field of trial and
error.&lt;/p&gt;

&lt;h3 id=&quot;macros&quot;&gt;Macros&lt;/h3&gt;

&lt;p&gt;There is a lot written about macros and their pros and cons. Main
drawback for me is that their usage or, better, usage of nonstandard
ones has a huge impact in readability of the code simply because you
need to go and understand them first and macros are not the easiest
thing to understand, especially for people without years of full time
lisp development like me.&lt;/p&gt;

&lt;p&gt;From the other side writing them is a total pleasure because you can
watch the language mold under your hands. I’ll show one example there
and it’s naming is probably totally incorrect, but it solved the issue
I had.&lt;/p&gt;

&lt;p&gt;To print the status I had to print information about different
states - print a list of new files, drafts, updated files etc. Each of
them had it’s own sub to produce a list and it’s own text of
course. To make it more human-friendly I wanted to have not one but
three text - for zero, one and many results.&lt;/p&gt;

&lt;p&gt;I wrote initial logic as a function and amount of duplication became
obvious very soon. I decided to give macros ago and imagined a perfect
way to generate a status of a certain kind. I could do a macro that
does code execution in place but that would sit in the body of a
single function inflate it’s size. Based on that I decided to make a
macro to generate a function definition.&lt;/p&gt;

&lt;p&gt;This was a perfect syntax in my opinion:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-files&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;get-new-files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;There are ~a new files to publish~%&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;There is a new file to publish~%&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;No new files to publish~%&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That would generate a function with the name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with-new-files&lt;/code&gt; that
takes a callback to print a list that’s a result of calling
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(get-new-files)&lt;/code&gt; form and prints header by itself and items list with
this callback.&lt;/p&gt;

&lt;p&gt;The callback was an extension point to remove any constraint on the
type of list items and let actual code deal with it.&lt;/p&gt;

&lt;p&gt;With the macro implemented actual status code again became really
trivial.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;print-status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;flet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~%~{    ~a~^~%~}~%~%&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mapcar&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-string-names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~%~{    ~a~^~%~}~%~%&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-draft-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-string-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-new-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-modified-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-deleted-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-fetched-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-string-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here is the actual macro code:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmacro&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;with-files&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;accessor&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;multiplemsg&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;singlemsg&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;nomsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fn-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;intern&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;concatenate&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'string&lt;/span&gt;
                               &lt;span class=&quot;s&quot;&gt;&quot;WITH-&quot;&lt;/span&gt;
                               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;symbol-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                               &lt;span class=&quot;s&quot;&gt;&quot;-FILES&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fn-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;accessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;progn&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;multiplemsg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;singlemsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;funcall&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cb&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nomsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What happens there is I’m assembling function name and then return
function definition with placeholders replaced with the data from
arguments.&lt;/p&gt;

&lt;p&gt;Macro is not a tool for any task but it can be really life changing if
you only ever programmed with macroless languages.&lt;/p&gt;

&lt;h3 id=&quot;format&quot;&gt;Format&lt;/h3&gt;

&lt;p&gt;I find &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format&lt;/code&gt; really fascinating. Most of the languages mimic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C&lt;/code&gt;,
they implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sprintf&lt;/code&gt; style functions and it’s a shame given how
much more powerful &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format&lt;/code&gt; is.&lt;/p&gt;

&lt;p&gt;First of all, it works on streams, that gives a lot of power on their
own, as I wrote earlier. Next it literary has all functionality
necessary to always print to output with one call.&lt;/p&gt;

&lt;p&gt;It’s possible to print a quoted value, a value without surrounding
quotes, add all sorts of paddings to the printed data and to even
print arrays!&lt;/p&gt;

&lt;p&gt;Here are few spells:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~{~a/~}~a-~2,'0d-~2,'0d-~a.md&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;rest&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:mon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What happens there? We print a pathname. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rest&lt;/code&gt; contains a list with
folders, date parts go after that and in the end we want to print the
name. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~{~a/~}&lt;/code&gt; prints folders separated by the forward slash, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~2,'0d&lt;/code&gt;
ensure that two digit number is printed and pads it with leading zeros
if necessary. If arguments are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;((list &quot;path&quot; &quot;to&quot; &quot;file&quot;) 2018 3 4
title&lt;/code&gt; result will be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;path/to/file/2018-03-04-tile.md&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or here is how I print a list of files in status, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~%&lt;/code&gt; means newline:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~%~{    ~a~^~%~}~%~%&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Format can do much more and it simply eliminates manual fiddling with
data to prepare it for printing.&lt;/p&gt;

&lt;h3 id=&quot;little-things&quot;&gt;Little things&lt;/h3&gt;

&lt;p&gt;There are lots of decisions in Common Lisp standard package that make
you only wonder why didn’t the find the way to any other language.&lt;/p&gt;

&lt;p&gt;For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read-line&lt;/code&gt; function accepts an argument that will be
returned if the end of stream was reached. Why would you need this?
Simply because returning custom value there can make some upper-level
logic more generic.  Btw, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dolist&lt;/code&gt; does that too.&lt;/p&gt;

&lt;p&gt;Another small nicety is that many functions on collections accept
parameters like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:test&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:key&lt;/code&gt; that immediately make them more
useful.&lt;/p&gt;

&lt;p&gt;Here is how last published post is found, for example:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;get-last-published-post&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;created-at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not effective, I know. Or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;member&lt;/code&gt; function in this example:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;known-editor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;member&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;vim&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;emacs&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;emacsclient&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'string=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s string there, but having this parameter immediately allows to
have list members of any type as long as there is an equality check
for them.&lt;/p&gt;

&lt;h2 id=&quot;common-lisp-pitfalls&quot;&gt;Common Lisp pitfalls&lt;/h2&gt;

&lt;h3 id=&quot;packages&quot;&gt;Packages&lt;/h3&gt;

&lt;p&gt;Before writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl-journal&lt;/code&gt; I didn’t have too much experience working
with Common Lisp, so I decided to pick up whatever I considered to be
latest best practices and try to live with it. I’ve split all the
functionality into separate packages and tried to export and import
only really necessary functionality.&lt;/p&gt;

&lt;p&gt;Soon I’ve found out that this way of development was much more verbose
there than in other languages and the reason was mostly CLOS. Packages
serve as containers for symbols in Common Lisp and any symbol you want
to share between packages needs to be exported.&lt;/p&gt;

&lt;p&gt;That means that class name should be exported as well as all generic
functions generated for it accessors. A good example is
&lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/db.lisp&quot;&gt;cl-journal.db&lt;/a&gt; package. Three classes with a handful of slots
each generated a long list of symbols to export and besides that, there
were still ordinary functions and special variables. And since generic
functions where magically generated it still left open questions about
how they will work if such generic functions were imported from two
different packages into third one. I’m sure this behavior is defined
somewhere, but I don’t know.&lt;/p&gt;

&lt;p&gt;In the end, I got so tired of all this maintenance that I started
importing whole packages with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:use&lt;/code&gt; even though I treated that earlier
as a non recommended way.&lt;/p&gt;

&lt;h3 id=&quot;standard-library&quot;&gt;Standard library&lt;/h3&gt;

&lt;p&gt;I think a very thick book can be compiled from all the complaints
regarding the standard package of the language. Sometimes there are
functions that are of no interest to most people and there are many
cases when obviously necessary functions are not there.&lt;/p&gt;

&lt;p&gt;Good examples are user input, string manipulations or external
processes.  Some things like prompt are more or less easy to do, but
password input proved to be a really painful
exercise. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uiop/run-program&lt;/code&gt; is also far from the easiest function to
use. Ok, maybe I missed a very good tutorial on this one, but I had to
go through it’s code several times to understand the details or
meaning of it’s parameters.&lt;/p&gt;

&lt;p&gt;Common Lisp comes from the time when it was if not mainstream but a
widespread language with lisp machine legacy and from what I
understand this had an influence on it’s relations with outside world
and that really hurts, especially comparing string and io operations
with languages like perl that do that this bit particularly well.&lt;/p&gt;

&lt;h3 id=&quot;third-party-libraries&quot;&gt;Third-party libraries&lt;/h3&gt;

&lt;p&gt;That’s another very common complaint. Common Lisp libraries are often
of fantastic quality feature-wise but it doesn’t really help if they
have no documentation or a brief one that explains 10% of the
functionality.  And you can almost forget about library specific
tutorials. Getting over this was a rewarding intellectual achievement
for me but the price was time, lots of time.&lt;/p&gt;

&lt;p&gt;Here I’d like to admit that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plump&lt;/code&gt; library had one of the best
documentations and even a couple of projects using it in neighbor
repos.  This bit really helped me in understanding the library and
coming up with a proper solution for html conversion.&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;

&lt;p&gt;You probably spotted already that I’ve been mentioning Livejournal
everywhere it can give an impression that there is no way in life for
the client to support any other service. That’s actually not true, but
will require a good amount of work of course. The first step would be
to abstract away remote api and the second one will be to make
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/code&gt; classes service agnostic. It’ll be even
easier if we do not set the aim to support the same feature set for
every single platform and provide a cl-journal as a platform that can
give the same experience as it does now for Livejournal with service
specific changes.&lt;/p&gt;

&lt;p&gt;In terms of featureset, a lot of things can be done better of course.
For example, I can think of template posts, or client can support
failures much nicer, but after using it for more than two years I can
say that none of it is something that turns it into something
unusable.&lt;/p&gt;

&lt;p&gt;Was it a correct choice to use Common Lisp for implementation? For me,
absolutely yes, because a lot of things I implemented would have taken
twice as code to get them working and interactive development mad me
so performant that I was able to add significant features to the code
even with a very limited time I had.&lt;/p&gt;

&lt;p&gt;One good learning for me was to add test into interactive development
workflow. It was really not that obvious for me, but comment driven
development as I did for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;syncitems&lt;/code&gt; with an addition of test allowed
me to write functional code even in the time of heavy sleep
deprivation that I tend to fall into.&lt;/p&gt;

&lt;p&gt;Another good learning that I had was to follow a bottom-up approach, you
could see an example in the merge description. Whenever I approached
big task I started asking myself very simple question starting from
“How do I get the changes?” or “How do I get the filename” and solving
them slowly one by one. After they were all complete the final task
that was thought to be complex and indeed was turned out to be a very
small function with simple logic.&lt;/p&gt;

&lt;p&gt;Thank you for reading.&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Part 4: Merge</title>
    <link href="https://can3p.github.io//blog/2018/06/28/client-writeup-merge/"/>
    <updated>2018-06-28T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2018/06/28/client-writeup-merge</id>
    <content type="html">&lt;p&gt;With a database of raw posts at my hands, I started thinking about ways
to implement the merge. First and foremost I had to identify which
posts actually had to be merged. This is trivial with missing posts,
but for existing ones I had to have something to compare them to, and
since I had raw posts from the server with the server timestamp I had
to get server timestamp for all existing posts and update it for any
posts that I wanted to change.&lt;/p&gt;

&lt;p&gt;If you remember I already had &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ignore-all&lt;/code&gt; command to bump local
modification timestamp of all posts and I ended up doing the same for
the remote timestamp. The easiest way to get it was to take it from the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;syncitems&lt;/code&gt; call and to crossect them with all existing posts.  As a
first step, I ensured that every fetched post received corresponding
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;syncitems&lt;/code&gt; update timestamp and after that I only had to add this
timestamp to all the posts in the database.&lt;/p&gt;

&lt;p&gt;And this is where my obsession with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop&lt;/code&gt; macro began.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;mark-as-pulled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;-&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fetch-posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;restore-source-posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to-hash-table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Item ~a was not fetched yet, run cl-journal fetch first~%&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;server-changed-at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You see there old friends - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&amp;lt;&amp;gt;&lt;/code&gt; macro and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-hash-table&lt;/code&gt; function
which became independent implementation there with an option to chose
what value to use as a key for a hash table. Once that was done update
became as trivial as setting slot values for any posts that were found
in the hash table.&lt;/p&gt;

&lt;p&gt;That is one direction, but as I said there was another one - I had to
get a server timestamp for any post update or creation I did. The post
data being returned contained some timestamps but none of the public
ones contained a timestamp of last post update. I started looking
elsewhere and found that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getevents&lt;/code&gt; call also returned server
timestamp.  Super handy! I did a quick function to get a timestamp
based on the call only to find out later that timestamp did not always
match the one from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;syncitems&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What the hell? Timezones. Livejournal api is quite naive in terms of
dates in a sense that all the timestamps it returns are in format
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY-MM-DD HH:MM:SS&lt;/code&gt; and one can only assume that they all relate to
the same timezone and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getevents&lt;/code&gt; call was one particular example
where this invariant didn’t hold, or to say it more precisely, not
always.  It seems like this api call is served by the two servers in
two different dcs with their local timezone set. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;syncitems&lt;/code&gt; seemed
to work with UTC timezone and I started looking around in search
of something that can work. First solution I came up with was
rather horrible: we just need to keep calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getevents&lt;/code&gt;
till we get two different timestamps and from that point we can take a
lower one and save it along the post.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;lj-get-server-ts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; scary hack to get server ts in a single timezone&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;labels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lj-getevents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:lastsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;;; something big enough to have empty lookup&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;do&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a~%&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;older-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;older-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You see? It’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop&lt;/code&gt; again. What happens is that we keep calling the
api and keeping the result of the previous call. Once we see the
difference we return the earliest. Loop works wonderfully there: it
allows to set a local binding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; and then execute loop body and
return from it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;older-p&lt;/code&gt; is also interesting. I didn’t find a simple way to compare
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY-MM-DD HH:MM:SS&lt;/code&gt; timestamps, however, &lt;a href=&quot;https://common-lisp.net/project/local-time/&quot;&gt;local-time&lt;/a&gt; library
provided a way to compare it’s time object instances. And this helper
function exploits this fact:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;older-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ts1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ts2&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;labels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;parse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;local-time::*default-timezone*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;local-time::+utc-zone+&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;parse-timestring&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:date-time-separator&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;#\Space&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;timestamp&amp;gt;&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;timestamp-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;parse&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ts2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;threshold&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:sec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;parse&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ts1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Setting timezone doesn’t have any special meaning there, it’s just
some stable value that allows us to compare timestamps. The threshold
is there to filter out small inconsistencies between time stamps.&lt;/p&gt;

&lt;p&gt;While it worked at the beginning, later I was punished for my
naiveness and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getevents&lt;/code&gt; started returning mostly non UTC datetime. I
decided to search more and finally understood that I could as well
just use unix timestamp that some of the calls return and then format
it according to timezone of my liking. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local-time&lt;/code&gt; library was really
helpful in this sense. Timestamp formatting can be implemented as easy
as:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defparameter&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*livejournal-timestamp-format*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;
                                 &lt;span class=&quot;ss&quot;&gt;:year&lt;/span&gt;
                                 &lt;span class=&quot;s&quot;&gt;&quot;-&quot;&lt;/span&gt;
                                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:month&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                 &lt;span class=&quot;s&quot;&gt;&quot;-&quot;&lt;/span&gt;
                                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:day&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                 &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;
                                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:hour&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                 &lt;span class=&quot;s&quot;&gt;&quot;:&quot;&lt;/span&gt;
                                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:min&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                 &lt;span class=&quot;s&quot;&gt;&quot;:&quot;&lt;/span&gt;
                                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:sec&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                 &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;format-unix-timestamp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;format-timestring&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;unix-to-timestamp&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                     &lt;span class=&quot;ss&quot;&gt;:format&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*livejournal-timestamp-format*&lt;/span&gt;
                      &lt;span class=&quot;c1&quot;&gt;;; looks like utc zone is what syncitems use&lt;/span&gt;
                     &lt;span class=&quot;ss&quot;&gt;:timezone&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;+utc-zone+&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Initially I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defconstant&lt;/code&gt; there, but every copilation resulted in
the restart and sbcl even &lt;a href=&quot;http://www.sbcl.org/manual/#Defining-Constants-1&quot;&gt;mentions&lt;/a&gt; this behaviour.  I
gave up and moved on to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lj-get-server-ts&lt;/code&gt; function that now looked
much simpler:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;lj-get-server-ts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;-&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rpc-call&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;LJ.XMLRPC.getchallenge&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:server_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;format-unix-timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After this was done I had a clear way to get posts to merge and it
appeared to be an interesting exercise! Fundamentally speaking there
are two parts: first, we need to map all the fields supported by the
client to the fields in a raw response, and second, we need to take
post body from the response which happens to be html most of the time
and convert it into markdown.&lt;/p&gt;

&lt;h3 id=&quot;fields&quot;&gt;Fields&lt;/h3&gt;

&lt;p&gt;Livejournal API is really thought through for it’s age and has a lot
of decisions make it handy for the clients. One example is a changelog
provided by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;syncitems&lt;/code&gt; call. The other one is that although it’s not
REST or anything alike original authors already thought about entities
and it appeared that fetch api returns more or less the same structure
that post api accepts. More or less means that main fields are on the
same places, however, reading entity returns more data and most of it
was added much later and is not documented anyhow. Another important
bit that whenever field contains non-latin characters it’s accepted as
is for posting but is returned encoded in base64.&lt;/p&gt;

&lt;p&gt;I didn’t know or care about this similarity when I wrote posting part
and didn’t spot it right away when I did reading part and that led to
the existence of two different pieces of code to handle fields. About
the first part I already wrote, see the description of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-event-list&lt;/code&gt;
generic. Reading part is handled by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parse-xml-response&lt;/code&gt; in
&lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/file-api.lisp#L71&quot;&gt;file-api&lt;/a&gt;. The idea of this function is to return parsed
data in such a way that I can use it later to create instances of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/code&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post&amp;gt;&lt;/code&gt;. Potentially non-latin fields are handled by
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b64getf&lt;/code&gt; function that works almost like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getf&lt;/code&gt; however, if received
value is a list with car equal to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:base64&lt;/code&gt; it takes cdr and decodes
it.&lt;/p&gt;

&lt;h3 id=&quot;markdown&quot;&gt;Markdown&lt;/h3&gt;

&lt;p&gt;Now, one more interesting part, at least it was really interesting to
me to implement. The most important part of the raw post was an actual
post content. And it was returned mostly in the form of html both for
posts written via Livejournal web interface and with the client (which
was expected since client converted markdown to html before sending).&lt;/p&gt;

&lt;p&gt;Given that what I wanted to get was to convert noncomplex html like
this:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;One more heading&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;First &lt;span class=&quot;nt&quot;&gt;&amp;lt;em&amp;gt;&lt;/span&gt;paragraph&lt;span class=&quot;nt&quot;&gt;&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;blockquote&amp;gt;&lt;/span&gt;As someone said&lt;span class=&quot;nt&quot;&gt;&amp;lt;/blockquote&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to a pretty markdown form such as this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# One more heading

First *paragraph*

&amp;gt; As someone said
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To complicate things a bit more Livejournal had a couple of custom
tags in use. The trickiest one was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lj-embed&lt;/code&gt; that was created for any
embed from youtube or a number of other services. At first problem
looked unsolvable there, because by default response contained only
tags like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;lj-embed id=&quot;1&quot;&amp;gt;&lt;/code&gt; which gave no chance to guess the
contents.  Luckily Livejournal once was an open souce platform and a
number of people made snapshots of the source code before it got
closed. After a thorough inspection of xmlrpc api
&lt;a href=&quot;https://github.com/apparentlymart/livejournal/blob/master/cgi-bin/ljprotocol.pl&quot;&gt;implementation&lt;/a&gt; I found that there was a magic
undocumented flag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_video_ids&lt;/code&gt; which allowed to add a bit more
information to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lj-embed&lt;/code&gt; and turn it into something like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;lj-embed source=&quot;youtube&quot; vid=&quot;1bU7COLWJE0&quot;&amp;gt;&lt;/code&gt; which was totally
enough to restore original embed code.&lt;/p&gt;

&lt;p&gt;In order to put that together, I looked for an appropriate html
parsing library and after a couple of experiments it turned out that
&lt;a href=&quot;https://shinmera.github.io/plump/&quot;&gt;plump&lt;/a&gt; library was completely sufficient for the purpose. The
trick is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plump&lt;/code&gt; can not only parse html but also serialize it
back and this is where it appeared to be super easy to plug in and
turn html generation into markdown generation. Serialization is
actually done by one generic function, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plump-dom:serialize-object&lt;/code&gt;
and Common Lisp generics and auxiliary methods made it trivial to
achieve desired behavior.&lt;/p&gt;

&lt;p&gt;First, I was able to target only tags I was interested in by
specifying the type of the first argument, which represented the
element. After that, by specifying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:around&lt;/code&gt; method I could completely
control printing behavior. Let’s say that we want to implement only
paragraph, links conversion and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lj-embed&lt;/code&gt; support. Here is how the
code would look like (you can check [full source][markdowwnify].&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;plump-dom:serialize-object&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:around&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cond&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tag-name-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;P&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;across&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;children&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
               &lt;span class=&quot;nb&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;serialize-object&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*stream*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~%~%&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

      &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tag-name-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;string-trim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;#\Space&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;#\Newline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-output-to-string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;*stream*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                                   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;across&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;children&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                         &lt;span class=&quot;nb&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;serialize-object&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*stream*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;[~a](~a)&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                         &lt;span class=&quot;nv&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

      &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tag-name-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;lj-embed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cond&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;string=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;source&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;youtube&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*stream*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;iframe width=\&quot;560\&quot; height=\&quot;315\&quot; src=\&quot;https://www.youtube.com/embed/~a\&quot; frameborder=\&quot;0\&quot; allow=\&quot;autoplay; encrypted-media\&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;vid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;call-next-method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;call-next-method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What happens is that we think of every paragraph as of a block of text with
two newlines after it and this is what happens. By calling
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serialize-object&lt;/code&gt; we pass control back to generic and recursively it
can return back to this method to do any conversions we’re interested
in for the child nodes of the paragraph.&lt;/p&gt;

&lt;p&gt;Also, another awesome part of this api is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serialize-object&lt;/code&gt;
doesn’t return a string, it prints to a stream and that means that
we don’t need to waste our time with doing string concatenation and
related housekeeping but we are still in full control of the fate of
the content to be printed.&lt;/p&gt;

&lt;p&gt;Link conversion shows it very good. I override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*stream*&lt;/code&gt; stream that
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plump&lt;/code&gt; uses for printing and catch all the children output into a
string, which gives me a way to produce a link like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[contents](url)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this sense, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lj-embed&lt;/code&gt; conversion doesn’t show anything new, but I
decided to add it to show how easy it was.&lt;/p&gt;

&lt;p&gt;Final conversion code turned out to be more or less lengthy but that’s
only because I was already too tired and lazy to come up with smart
solutions and the only thing I cared about was to make enough test
cases and make them pass.&lt;/p&gt;

&lt;p&gt;One library that I found super useful at this step was
&lt;a href=&quot;https://github.com/diogoalexandrefranco/cl-strings&quot;&gt;cl-strings&lt;/a&gt; that provides basic string manipulation
utilities that all languages except Common Lisp provide out of the
box. Of course, I could have used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl-ppcre&lt;/code&gt; but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl-strings&lt;/code&gt; just did
the job and again made the logic trivial.&lt;/p&gt;

&lt;p&gt;I can say that it was magically simple for me due to how beautifully
different Common Lisp played together in api. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plump&lt;/code&gt; is awesome
by itself!&lt;/p&gt;

&lt;h3 id=&quot;filename&quot;&gt;Filename&lt;/h3&gt;

&lt;p&gt;Once I had all proper fields and post source in place the last
remaining part was to actually save the file. For existing posts it’s
easy, but for new ones I had to come up with a name. When I write a
post with a client, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl-journal new post-name&lt;/code&gt; command generates
filename &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY-MM-DD-post-name.md&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post-name&lt;/code&gt; was completely
arbitrary and for reverse action I decided to generate it from the
title. I almost decided to write such a library myself but then
stumbled upon [reddit post][reddit slugify] where author presented
&lt;a href=&quot;https://github.com/EuAndreh/cl-slug&quot;&gt;cl-slug&lt;/a&gt; library which solved exactly this problem and not
only for English but also for a number of other languages including
Russian. Thank you André!&lt;/p&gt;

&lt;p&gt;After slug was there I had to ensure the uniqueness of the filename.
final logic turned out to be pretty straightforward: generate base in
the form of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY-MM-DD-slug&lt;/code&gt; where timestamp was creation timestamp
of the post and if there was already a file with the name
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY-MM-DD-slug&lt;/code&gt;, keep adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-2&lt;/code&gt; etc at the end till we found
a vacant spot.&lt;/p&gt;

&lt;p&gt;Here is a full code of the sub:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;generate-unique-filename&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Generate a filename that does not yet exist in the database
   based on datetime (yyyy-mm-dd hh:mm:ss) and a base (any string).

   Function will generate a base name of the form &amp;lt;date&amp;gt;-&amp;lt;slug&amp;gt;.md
   and in case such a filename already exist, will keep adding increasing
   postfix numbers till it finds a vacant spot&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;labels&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;gen-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;equal&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a-~a.md&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a-~a-~a.md&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;split&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;slugify&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to-hash-table&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:key-sub&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-output-to-string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cnt&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
              &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;gen-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;incf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
              &lt;span class=&quot;nv&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;nv&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Did I just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop&lt;/code&gt; again? Oh, my. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-hash-table&lt;/code&gt; was useful
again with a little modification of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:key-sub&lt;/code&gt; parameter that allowed
to have anything from the post as a key. In this case, using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filename&lt;/code&gt; as a key made generation logic really simple.&lt;/p&gt;

&lt;p&gt;Now I have all parts in place - all posts could be fetched from the
server, I could determine which posts were updated remotely or just
didn’t exist locally and I could convert remote representation back to
the markdown text (I omitted the code that turned fields into a header
with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title:&lt;/code&gt; and other fields but it’s there of course.&lt;/p&gt;

&lt;p&gt;By the way, in case usage of the format in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finally&lt;/code&gt; part surprises
you, I had to add it, because otherwise, sbcl kept serializing filename
as an array of characters for some reason.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge-fetched-posts&lt;/code&gt; function contains one of the most epic usages of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop&lt;/code&gt; macro I ever did. Let me just paste it as an excerpt from the
sub, full code is &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/cl-journal.lisp#L243&quot;&gt;there&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;reverse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:itemid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;target-itemid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;equal&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;target-itemid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;older-than-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:sync-ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All the logic fit nicely in default features of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reverse&lt;/code&gt; it’s
there because I wanted to convert only the most fresh version of the
post that I downloaded, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; part of the macro played the role of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; and assigned values on every loop iteration and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;when&lt;/code&gt; included
all the logic necessary so that loop body executed only in case it had
to: in case post was not already generated during this run, it didn’t
exist in database or database version was older than remote.&lt;/p&gt;

&lt;p&gt;One important thing in this logic was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;synced-from-fetch&lt;/code&gt; flag for
every post. Whenever I merge it I raised this flag to 1 and whenever I
updated or created a post with the client I reset it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;. Why
important? Simply because the conversion process was and still isn’t
perfect, I wanted to have a nondestructive way to run merge
functionality again and again, end this is what
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remerge-fetched-posts&lt;/code&gt; function and related cli command does. With
this flag I could take all the posts that I already converted but that
were not touched after and merge them again. This gave me an
opportunity to already have markdown files of some sorts and still
work on better conversion afterward.&lt;/p&gt;

&lt;p&gt;From potential improvements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plump&lt;/code&gt; doesn’t handle accidental closing image tags as I wanted
them to (I want them to be ignored)&lt;/li&gt;
  &lt;li&gt;All paragraph contents tend to be turned into one line and in a
perfect world I wanted my converter to also have line length
limitation and nicely wrap the code if necessary.&lt;/li&gt;
  &lt;li&gt;And probably many more bugs and mistakes that I did myself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After all this functionality got implemented, the client reached near
perfect state to me, because if you remember it’s unidirectional
nature was one of the client limitations and now it actually didn’t
matter where a post was written or updated, fetch end merge steps could
incorporate all the changes back. And what’s even cooler, now the client
can be used for any blog and from the day one it’s possible to get all
posts offline and work as if you were using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl-journal&lt;/code&gt; forever.&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Part 3: Fetch it!</title>
    <link href="https://can3p.github.io//blog/2018/06/25/client-write-up-fetch-it/"/>
    <updated>2018-06-25T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2018/06/25/client-write-up-fetch-it</id>
    <content type="html">&lt;p&gt;All the functionality mentioned before was quite complete and I used
it constantly for a year or even more. There were two drawbacks,
however:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I had source code only for the posts I wrote using the
client. Nothing from earlier days was preserved and in case
livejournal.com goes down forever, I would lose them all.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;All the posts had to be written and update later with the client,
because it had no way to know about changes done elsewhere.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both parts were not important for day to day writing activities, but
the former kept returning after every next negative news about the
service and the latter was more of a challenging task that I wanted to
tackle. I resisted for a long time but finally gave up and decided to
write the logic. Besides that, how could it be a real git like client
if it has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt; command but didn’t have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;For the first question &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt; was the most important bit. I assumed
that if I downloaded posts in any form or shape I could work later to
turn them into a markdown later without any hassle.&lt;/p&gt;

&lt;p&gt;Luckily original Livejournal authors assumed such needs and designed
API protocol to enable it (Kudos to Brad Fitzpatrick!).&lt;/p&gt;

&lt;p&gt;First important bit is &lt;a href=&quot;https://www.livejournal.com/doc/server/ljp.csp.xml-rpc.syncitems.html&quot;&gt;syncitems&lt;/a&gt;. What it allows to do is
to get updates since some timestamp or from the beginning. A change
can be a new post, an update to post or a comment, and since any post
could be updated several times, duplicates were expected. The only
thing protocol did not support were post deletions, which meant that I
wouldn’t notice such a change.&lt;/p&gt;

&lt;p&gt;I didn’t care about comments or deleted posts, however, what I wanted
to get was a list of posts that changed somehow since timestamp. To
make things even more complicated, api call didn’t return &lt;strong&gt;all&lt;/strong&gt;
changes but just some subset of it and I had to do several calls in a
row to get the desired result.&lt;/p&gt;

&lt;p&gt;Logic looked quite complicated and I ended up literally
&lt;a href=&quot;https://github.com/can3p/cl-journal/commit/93695d3b0de4a9cdb37ee7b79a30de5bd2ed0370&quot;&gt;writing&lt;/a&gt; the steps algorithm should take and them slowly
implementing them step after step and surrounding them with tests to
keep it under control, you can check the final logic in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get-fetched-item-ids&lt;/code&gt; function in &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/lj-api.lisp&quot;&gt;lj-api&lt;/a&gt;.  As you can see I
used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&amp;lt;&amp;gt;&lt;/code&gt; threading macro all over the place to keep the code
readable. Apart from the logic itself, I ended up implementing some
function helpers that I didn’t find in the language but that we’re
very useful.&lt;/p&gt;

&lt;p&gt;The first example of this is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acc&lt;/code&gt;. I do a lot of Perl and data traversal
is really good there. What the language allows you to do is to easily
access nested arrays without checking the existence of anything -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$obj-&amp;gt;{a}{b}{c}&lt;/code&gt;. Common Lisp &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getf&lt;/code&gt; is much less flexible and allows
only one level lookup and this is where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acc&lt;/code&gt; is useful since it
allows any series of keys:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;acc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;rest&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Access member in a nested plist.

   Usage (acc l :one :two :three)&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cond&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;listp&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;acc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cdr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another two functions are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;partial&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print-and-return&lt;/code&gt;. Name of
the one first is self-explanatory and the next one accepts a value,
prints it and immediately returns it. This is useful in the middle of
threading macro call, because it allows printing intermediary steps in
the computation.&lt;/p&gt;

&lt;p&gt;One last perlism is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-hash-table&lt;/code&gt;. In Perl transformation between a
list and a hashmap is extremely simple and happens all the time. This
is how I would do it:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%ht&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simplicity means that most of Perl code consists of jumps between
lists and hashes to use whatever works best in a particular situation.
Posts are stored in the database as a list hence I had to iterate over
it in a more or less smart way all the time. A list is a reasonable
structure there and one of the ways to fix the problem with lookups was
to maintain a hashtable in the class, but I I decided to go with a
simpler one and convert the list to the table on the go.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;degun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to-hash-table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make-hash-table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dolist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;gethash&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cadr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This snippet highlights one of the neat features I like so much in
Common Lisp. Maybe it has not the best possible standard library in
the world, however, there are true gems of usability there. In this
particular case, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dolist&lt;/code&gt; accepts the third parameter which will be
returned as a result of dolist. That means that you can write things
like filling in hash table very naturally.&lt;/p&gt;

&lt;p&gt;After &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get-unfetched-item-ids&lt;/code&gt; function was done, the next one on the
list was one to download a list of posts. And here it got a bit tricky
because of Unicode.&lt;/p&gt;

&lt;h3 id=&quot;unicode-and-livejournal&quot;&gt;Unicode and Livejournal&lt;/h3&gt;

&lt;p&gt;Livejournal is there for quite a long time and that means that it’s
codebase and data predates the times when everything became all
Unicode.  Many veteran coders can still show scars from that time,
yeah. In essence, there was an ASCII table that was used to define a
meaning of the byte of data (hence single byte encoding) and it
defined the meaning of the first 127 values and left the rest
undefined ant that was used as a space for extra characters for every
specific alphabet. In Russia, there were two most popular encodings -
cp1251 and koi8-r. How did it affect Livejournal? As is written in
their FAQ, they had no way of understanding the encoding and hence it
was left up to users to choose a proper one to render a page.&lt;/p&gt;

&lt;p&gt;In case such encoding was chosen, Livejournal API allowed to download
both Unicode and non-Unicode posts in the same way. Unfortunately,
what I found empirically, serverside encoding did some weird stuff
presumably because the text was already converted to Unicode somewhere
along the way, and I had to disable it in order to get a properly
readable way.&lt;/p&gt;

&lt;p&gt;This decision had consequences in a sense that now I couldn’t download
different types of posts in one batch - Livejournal api returned an
error in this case. The beast I ended up writing is called
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lj-getevents-multimode&lt;/code&gt; you can check it out in &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/lj-api.lisp#L85&quot;&gt;lj-api&lt;/a&gt;. I baked in a couple of assumptions in the code:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;One of the api versions (Unicode one) was much more probable&lt;/li&gt;
  &lt;li&gt;If the post had one version, the next one had a high chance of having
the same one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final logic looked like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Try to download posts in one version&lt;/li&gt;
  &lt;li&gt;If fails reduce batch to one post and repeat&lt;/li&gt;
  &lt;li&gt;If that fails, flip download mode and repeat&lt;/li&gt;
  &lt;li&gt;If that fails, error out&lt;/li&gt;
  &lt;li&gt;If one of the previous steps succeeded, increase batch size 2x and
repeat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe a bit naive approach, but it worked really well in the
end. After these two bits were done, fetch logic started looking very
simple:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fetch-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;store&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Fetch all new items from remote service since last-fetched-ts
   of the store&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;multiple-value-bind&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;new-itemids&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;last-item-ts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;get-unfetched-item-ids&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cond&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;new-itemids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;new-events&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;-&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;new-itemids&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lj-getevents-multimode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mapcar&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;enrich-with-ts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ht&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;merge-events&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;new-events&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;last-item-ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fetch-posts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;store&amp;gt;&lt;/code&gt; there is another class I created to store a list of
downloaded posts in their original form. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enrich-with-ts&lt;/code&gt; function
exploited the fact that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get-unfetched-item-ids&lt;/code&gt; could now server
change dates for any updated post and download post api call did not
return that and it simply added such a timestamp to every
post. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge-events&lt;/code&gt; did no more than placing downloaded posts at the
end of the list.&lt;/p&gt;

&lt;p&gt;Now, where should I take this store? I mimicked the way I stored and
saved the database with posts and added a hacky solution to do lazy
loading of the database. I don’t think it’s necessary since posts are
downloaded differently, but in case you wonder here is the
implementation:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;slot-unbound&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;slot-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eql&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'fetch-store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;slot-value&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'fetch-store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make-instance&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'&amp;lt;store&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This gets triggered whenever slot doesn’t have value set and function
sets slot value after creating the class instance. Next time slot
already has value and this method is not called anymore.&lt;/p&gt;

&lt;p&gt;Top level fetch function now looked very simple:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fetch-updated-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;restore-source-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fetch-store&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fetch-posts&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-source-posts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After I got all this working I got a raw dump of all the posts I ever
wrote, which meant that I could safely work on actually converting
them back to markdown without fear to lose the contents.&lt;/p&gt;

&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;

&lt;p&gt;To be frank sync protocol didn’t come for free to me. Too many moving
parts and conditions. And while most of the code base has been written
in a pure leisure fashion without a single test, I decided to build
new features starting from fetch with at least some coverage. And I
chose &lt;a href=&quot;https://github.com/fukamachi/prove&quot;&gt;prove&lt;/a&gt; as a framework of choice.&lt;/p&gt;

&lt;p&gt;The most annoying bit of this framework is it’s default reporter which
uses escape control sequences for colors and emacs requires some
additional configuration to make that work and that’s not something I
wanted to invest my time in. Instead, I invested my time in finding a
way to disable them. It appears that I’ve always been one dynamic
variable away from the result:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;prove:*enable-colors*&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The overall tests integration could have been easier, however, still
doable.  I’ve made a test system, added a magical spell to the main
asd file and test framework was set up. One of the important things to
note is that prove itself is a dependency of the test system, hence in
order to have it available in the repl this test subsystem should be
loaded instead of the main one which will be loaded as a dependency.&lt;/p&gt;

&lt;p&gt;A really awesome feature of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prove&lt;/code&gt; framework is that it allows to
rerun specific tests just by recompiling them. This feature enables
near magical workflows when I could prototype a feature and then cover
it with tests in real-time without the need to run a full test suite
again and again or to do a build every time and run tests there.&lt;/p&gt;

&lt;p&gt;Since I wanted to write tests for the logic built around api calls I
wanted to mock them to test outcomes of specific sequences of calls.
I took &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mockingbird&lt;/code&gt; and it did provide me with a basic feature of
mocking any function in any package, however, I ended up implementing a
small macro to enable testing a sequence of calls.&lt;/p&gt;

&lt;p&gt;Funnily enough, all the mocked calls ended up being trivial, but the
possibility is still there! The idea was that if you want to mock a
function, say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; and have it return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(1 2 3)&lt;/code&gt; on the first call and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt; on any subsequence you could just write:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-mocked-calls&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;foo&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Macro allows generating a lambda function for mockingbird that has a
baked in logic that tests against the number of calls and returns a
respective result. Here is it:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmacro&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;with-mocked-calls&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;rest&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;This function is necessary to emulate the behavior of
   a function that has side effects. Every subsequent
   call to the function will return next item from the
   data list, except the last one which will be returned
   endlessly&quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-dynamic-stubs&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;func&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;amp;rest&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ignore&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cond&lt;/span&gt;
             &lt;span class=&quot;o&quot;&gt;,@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;
                    &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;nv&quot;&gt;collect&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;equal&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                        &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;quote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                        &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;equal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;call-times-for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;quote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;quote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))))&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;,@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m no macro guru and I get super excited about every single case when
I managed to write something that actually looks like useful
thing. You can check the &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/t/cl-journal.lisp#L96&quot;&gt;tests&lt;/a&gt; for this definition and
real-world usage.&lt;/p&gt;

&lt;p&gt;Now, let’s merge.&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Part 2: Client logic</title>
    <link href="https://can3p.github.io//blog/2018/06/22/client-writeup-logic/"/>
    <updated>2018-06-22T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2018/06/22/client-writeup-logic</id>
    <content type="html">&lt;p&gt;There are a lot of different bits related to the client that were
pretty fun to solve, especially in order to get git-like behavior,
but nothing of that matters if we cannot communicate with the server,
so let’s start with that.&lt;/p&gt;

&lt;h3 id=&quot;xmlrpc&quot;&gt;XMLRPC&lt;/h3&gt;

&lt;p&gt;Livejournal uses XMLRPC as a protocol for communication. Documentation
is &lt;a href=&quot;https://www.livejournal.com/doc/server/ljp.csp.xml-rpc.protocol.html&quot;&gt;old&lt;/a&gt; but serves it’s purpose. However, I had to check the
last opensource version of the protocol implementation to make sense
out of it for a back sync functionality. I’ll return to it later, but
now let’s see how that can be done at all.&lt;/p&gt;

&lt;p&gt;I looked for a library to use and initially my choice was
&lt;a href=&quot;https://common-lisp.net/project/s-xml-rpc/&quot;&gt;s-xml-rpc&lt;/a&gt;. I don’t remember exactly why I decided to get
rid of it. I think it was due to the fact that s-xml-rpc returns
result in term of a hierarchy of objects and it proved to be difficult
to maintain.&lt;/p&gt;

&lt;p&gt;Instead of that, I’ve settled with &lt;a href=&quot;http://quickdocs.org/rpc4cl/&quot;&gt;rpc4cl&lt;/a&gt; which simply
returns a nested list and that’s precisely what I need. Now that I’m
writing this, it appears that author removed rpc4cl from Github which
is very unforunate. To simplify the code I made a simple wrapper that
takes adds host information to every call:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rpc-call&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;rest&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;method-parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rpc4cl:rpc-call&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*service-endpoint*&lt;/span&gt;
         &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;method-parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that, all remote calls naturally fit into the code. Here is as
example a sub to get a so-called challenge from Livejournal service:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;getchallenge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;-&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rpc-call&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;LJ.XMLRPC.getchallenge&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:challenge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please note &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&amp;gt;&lt;/code&gt; from &lt;a href=&quot;https://github.com/nightfly19/cl-arrows&quot;&gt;cl-arrows&lt;/a&gt; here. I seriously cannot
imagine my lisp coding with it, because using the arrow prevents all
sorts of deep nesting and keeps the code clear. Since it’s not always
easy to remember in which place the previous result goes by default in
the latest code I always use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&amp;lt;&amp;gt;&lt;/code&gt; instead which allows putting a
placeholder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;gt;&lt;/code&gt; that will be replaced with the result. This way makes
argument placing more explicit. As an example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getchallenge&lt;/code&gt; can be
transformed to this:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;getchallenge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;-&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;LJ.XMLRPC.getchallenge&quot;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rpc-call&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;getf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:challenge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One of the distinctive features of Common Lisp is its live editing
process where one can start interpreter, load necessary code there and
do all the changes right in it compiling changed functions if
necessary without a need to compile and start the program all over again.&lt;/p&gt;

&lt;p&gt;In case of the API, it’s particularly useful. I defined three dynamic
variables - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*service-login*&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*service-password*&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*service-endpoint*&lt;/code&gt;.  I set them once the repl starts and after that
I can do any experimentation with the api with help of all the power
the language provides. And after I &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/lj-api.lisp&quot;&gt;wrapped&lt;/a&gt; all service
endpoints as functions I literally could do all blog manipulations
without leaving the editor. It proved to be so stable that I kept the
running lisp process for months without any need for a restart.&lt;/p&gt;

&lt;p&gt;The only improvement I made recently was to add unit tests into the
project development workflow and it works wonderfully with repl-driven
development. Why is it cool?  With a usual way of developing one of
the hardest bits is to recreate an environment where tests happen, and
in cases where there is no obvious way to implement something,
changing implementation can have a lot of impact on how tests are
written. With Common Lisp, I can experiment with code freely until I
get something working without overhead related to environment and
create supporting architecture only after that and I can still run all
the tests right there! Change a function and recompile only one
specific test till everything works and then compile the whole suite
to see if everything’s in place.&lt;/p&gt;

&lt;p&gt;It’s so fascinating that I can safely say that it’s one of the
features that really make me stick to the language, I enjoy every
second of it.&lt;/p&gt;

&lt;h3 id=&quot;storage&quot;&gt;Storage&lt;/h3&gt;

&lt;p&gt;I wrote the client primarily for myself and I didn’t want to invest
in security more than a secure password storage that was achieved
but external tools. I wanted some storage, however.&lt;/p&gt;

&lt;p&gt;First things first I decided to have a class that represents a posts
database, that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defclass&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:posts&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:accessor&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:version&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reader&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:login&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reader&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;raw-text&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:raw-text&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reader&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;raw-text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:service&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reader&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;service-endpoint&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:service-endpoint&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reader&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;service-endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fetch-store&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reader&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fetch-store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s the most recent version, of course, the most minimal version
contained only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posts&lt;/code&gt; slot and thanks to the lisp interactivity more
slots could be added by class recompilation and all the live instances
got new slots automatically.&lt;/p&gt;

&lt;p&gt;Anyway, as a next step, I wanted to serialize the database and it’s
contents and store it into a file. This is very Common Lisp generics
step in. They are completely orthogonal to classes and make it super
simple to do all sorts of recursive definitions for particular
functionality. In this case, I defined a generic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-list&lt;/code&gt; and wrote
it’s implementation for the database:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:login&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:version&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:service&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:raw-text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;raw-text&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:service-endpoint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;service-endpoint&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mapcar&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To make this method work completely I had to only implement this
method for post &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post&amp;gt;&lt;/code&gt; class, which I did.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:itemid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;itemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:anum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;anum&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:ditemid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ditemid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:created-at&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;created-at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:updated-at&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;updated-at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:ignored-at&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ignored-at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:server-changed-at&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;server-changed-at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:synced-from-fetch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;synced-from-fetch&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:log-ts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;log-ts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:filename&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Super simple, after that saving database to a file became trivial:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;save-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-open-file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts-file*&lt;/span&gt;
                       &lt;span class=&quot;ss&quot;&gt;:direction&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:output&lt;/span&gt;
                       &lt;span class=&quot;ss&quot;&gt;:if-exists&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:supersede&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-standard-io-syntax&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pprint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to-list&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*posts*&lt;/code&gt; here is another global var that holds a reference to the
open database and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*posts-file*&lt;/code&gt; is a relative path pointing where the
posts file should live.&lt;/p&gt;

&lt;p&gt;What happens in the sub is that we pretty print s-expression that
happens to be a result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-list&lt;/code&gt; method call into a stream that
happens to be an open file. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with-standard-io-syntax&lt;/code&gt; macro ensures
that all the output related dynamic variables are reset to their
default state for the time of printing.&lt;/p&gt;

&lt;p&gt;After this was done using auxiliary methods made it trivial to save
changes on all meaningful actions. I had generics for publishing,
updating and deleting the post and all save logic is as simple as:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publish-post&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;delete-post&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;update-post&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;An obvious downside of this approach is that such an implementation is
tied to the class and not to object instance and that can lead into
all sorts of troubles characteristic to global state.&lt;/p&gt;

&lt;p&gt;Now that the file is saved we need to be able to restore it. That’s
also easy:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;restore-posts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;read-parse-file&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts-file*&lt;/span&gt;
                   &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;create-db-from-list&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                       &lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you see there are no safety checks there, which might be needed
were I to worry about it. I chose for simplicity in this case since
for the time being I’m the only user of the program.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create-db-from-list&lt;/code&gt; is an ordinary function in this case, not
generic, and there is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create-post-from-list&lt;/code&gt; for posts. Now, during
the development, the structure of the database and posts inevitably
changes and we need to handle that somehow and that’s why database has
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;version&lt;/code&gt; slot.&lt;/p&gt;

&lt;p&gt;What I decided to do was to do database migrations right during the
read phase. In order to do that I find a unique feature for the next
version of config, do migration and run the same function recursively
and by this way eventually get a most up-to-date structure that will
be nicely serialized on next save.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;create-db-from-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cond&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:version&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;create-db-from-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;migrate-db-v0-v1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:service&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;create-db-from-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;migrate-db-v1-v2&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;create-db-from-list-finally&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;publishing&quot;&gt;Publishing&lt;/h3&gt;

&lt;p&gt;Now that I had a database I wanted to fill it somehow with posts! Each
post is represented by a markdown file with a header that contains
custom fields.  For example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;title: This is a cool post
tags: this, is
privacy: friends

# Intro

This post will contain

* A header
* A list
* A couple of paragraphs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please note that till this moment we didn’t reach any cli interface and
git-like state management, hence I wanted to implement a simple
function that would take a file, parse it, convert markdown into html
and publish it as a post.&lt;/p&gt;

&lt;p&gt;Task itself is also recursive meaning that after we parse a file and
get a list of fields plus a text we need to parse the result again to
map all the fields to a representation Livejournal understands.&lt;/p&gt;

&lt;p&gt;The first part you can check out an &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/file-api.lisp#L35&quot;&gt;implementation&lt;/a&gt; of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parse-post-file&lt;/code&gt; function. What it does is it reads header line by
line and treats all the first field of the form &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;field: value&lt;/code&gt; as
individual fields and then all the rest as a markdown body of that
post which it converts to the html.&lt;/p&gt;

&lt;p&gt;After this step is done an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/code&gt; is initialized
and we can already do all sorts of things with it, not just
publishing. For example, we can check if it’s a draft. But let’s
publish.&lt;/p&gt;

&lt;p&gt;For that we need to convert this object to the format &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lj.postevent&lt;/code&gt;
understands. For this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-xmlrpc-struct&lt;/code&gt; generic is defined, which is
super handy since I can do an implementation for both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post&amp;gt;&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/code&gt; and use whatever is at hand. For example for a new post
it’s always &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/code&gt; and for updates it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/code&gt; I decided to go with a layered approach where I have
a basic object and then enrich it with all sorts of additional helpers
that contain logic for particular fields. Here is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-event-list&lt;/code&gt;
which is used by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to-xmlrpc-struct&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to-event-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;optional&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;transform&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;
            &lt;span class=&quot;ss&quot;&gt;:event&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*raw-text*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;body-raw&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;ss&quot;&gt;:subject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;-&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-props&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fields&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-privacy-fields&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;privacy&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-usejournal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;funcall&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transform&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add-*&lt;/code&gt; function should return a new object that is potentially
the same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;l&lt;/code&gt; but can be modified version of it. Here is
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add-usejournal&lt;/code&gt; which is used whenever I want to post to a different
journal than my own:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;add-usejournal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;plist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;journal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;concatenate&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'list&lt;/span&gt;
                   &lt;span class=&quot;nv&quot;&gt;plist&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:usejournal&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;journal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;plist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This conditional is not that elegant by itself, but the general pattern
proved to be very useful and I used it in many different places.&lt;/p&gt;

&lt;p&gt;Once I got an event in Livejournal view of it the publish function
itself becomes really simple:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;publish-post&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;set-credentials&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;*raw-text*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;raw-text&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;create-new-post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post-file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And if you remember for the previous parts database will be saved to
file whenever this method is called. And now, if we call this method
from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pre-commit&lt;/code&gt; hook we will get all changes saved to disk and will
be able to add them to the index and be included in the
commit. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set-credentials&lt;/code&gt; here is to ensure that we have all api
variables set before we make a call. The most interesting part of it
is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get-password&lt;/code&gt; that not just retrieves the password, but it also
requests and saves it in case of absence.&lt;/p&gt;

&lt;p&gt;And this is it about general client framework. With a list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post&amp;gt;&lt;/code&gt;
objects in the database we can do all sorts of calculations and answer
to all sorts of interesting questions especially because there is a
direct connection with the corresponding file on disk that can be
converted to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post-file&lt;/code&gt;&amp;gt; at will.&lt;/p&gt;

&lt;p&gt;How can I get the last published post? Easy:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;get-last-published-post&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;created-at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;How can I know the title of particular &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post&amp;gt;&lt;/code&gt;? Super easy:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;read-from-file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that’s just two examples, it’s always possible to start the repl,
load a posts database from file manually and start playing with
contents.&lt;/p&gt;

&lt;p&gt;To make repl experience even more pleasant Common Lisp provides
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print-object&lt;/code&gt; generic that is responsible for the text representation
of the object. Hence we can print any information we like from the
object instance.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;print-object&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;post filename:~a url:~a&amp;gt;~%&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last bit before implementing cli interface is the state management
and it can also be easily implemented using the same operations on
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/code&gt; objects.&lt;/p&gt;

&lt;p&gt;What do I mean by state management? Looking at the blog folder we
should be able to understand which posts are new, updated deleted or
are drafts and they all are defined in different terms:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;New post is the one that exists in the folder but is not mentioned
in the database&lt;/li&gt;
  &lt;li&gt;Deleted post is the one that does not exist in the folder, but does
exist in the database&lt;/li&gt;
  &lt;li&gt;Modified post is that one that exists in both places, however, it’s
recorded timestamp is lower than last modification timestamp of the
file on disk&lt;/li&gt;
  &lt;li&gt;The draft is a file in the folder that has a field &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draft: 1&lt;/code&gt; in the
header&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What they all share is that they all have a function that is able to
generate a list of items of a specific kind and a set of messages to be
printed depending on a number of items got in the previous step. Of
course, the line can be printed differently from case to case.&lt;/p&gt;

&lt;p&gt;For example, we can have the following output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;There a drafts file

    2018-06-04-books29.md

There is a new file to publish

    2018-06-06-test.md

There are 2 modified files to update

    2009-10-12-no-title.md
    2013-12-31-eshe-odin.md

There is a deleted file to unpublish

    2011-01-23-no-title.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s take drafts as an example. Here is a function to retrieve a
list:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;get-draft-files&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;get-markdown-files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;remove-if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;get-by-fname&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mapcar&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;read-from-file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;remove-if-not&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;draft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mapcar&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl-arrows&lt;/code&gt; rocks again there. What happens is that we get a list of
all markdown files in the folder, remove any files that exist in the
database, parse the reminder, kick out all files that do not contain
the mentioned field and retrieve filenames back from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filename&lt;/code&gt; slot
in resulting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post-file&amp;gt;&lt;/code&gt; objects. Not that performant, I know, but
it doesn’t really matter on this scale and my main aim was
correctness and readability, not speed.&lt;/p&gt;

&lt;p&gt;Once we have a list, we need to have a pretty printer for it and it
should be similar for all the cases. At this point in time, I decided
to test my defmacro-fu and came up with a macro, that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-files&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;draft&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;get-draft-files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;There are ~a drafts files~%&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;There a drafts file~%&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;No drafts found~%&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What this macro does is it generates another function named using the
second argument (it’ll be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with-draft-files&lt;/code&gt; in this case) and that
function will print one of the strings depending on the number of
items from the second argument (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(get-draft-files)&lt;/code&gt; in this case) and
print a list of items in a specified manner. The resulting
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print-status&lt;/code&gt; function looks very clean after all these
manipulations:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;print-status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;flet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~%~{    ~a~^~%~}~%~%&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mapcar&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-string-names&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~%~{    ~a~^~%~}~%~%&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-draft-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-string-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-new-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-modified-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-deleted-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with-fetched-files&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;print-string-names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I use one or another printing function depending on what is produced
but the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with-*&lt;/code&gt; function, it can be a plain list of filenames or a
list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;post&amp;gt;&lt;/code&gt; instances or anything else and I don’t need to worry
about it at the generation step and have all the flexibility to do it
at printing step, view layer is separate!&lt;/p&gt;

&lt;p&gt;Speaking of modified posts. Whenever I publish a post I run
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get-universal-time&lt;/code&gt; and record it along the filename. Then it takes a
super simple predicate to check whether a particular post is modified:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;modified-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;probe-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ignored-at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;updated-at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;created-at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file-write-date&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file-write-date&lt;/code&gt; function there. This logic works really well
if no one touches files, however, whenever files get moved or repo gets
cloned to another location, the timestamp from it gets distorted and all
files are marked as modified. This is precisely the reason for
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ignored-at&lt;/code&gt; field there. Whenever I want to mark all the files as
uptodate I run this function:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ignore-all&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get-universal-time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*posts*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ignored-at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This was one of the first times I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop&lt;/code&gt; macro and I really fell
in love with it some time after that.&lt;/p&gt;

&lt;h3 id=&quot;markdown&quot;&gt;Markdown&lt;/h3&gt;

&lt;p&gt;Last fun bit from the basic implementation is about markdown
formatting.  I decided to use it from the very beginning simply because
it’s an order of magnitude better to write than html and still allows
to have markup in place comparing to real plain text.&lt;/p&gt;

&lt;p&gt;And of course, both vim and emacs have excellent markdown modes and
make it a real pleasure to write.&lt;/p&gt;

&lt;p&gt;What could be interesting about writing markdown? Well, to extend it!&lt;/p&gt;

&lt;p&gt;I had two extensions planned. First of all, I didn’t want to use real
urls for links between the post. If I did, I would be tied to one
particular web service and migration to another one would be
error-prone. If all links in the source code point to other markdown
files, that means that whenever I decided to republish all posts
somewhere else, I could have all the links resolved.&lt;/p&gt;

&lt;p&gt;For markdown rendering I used &lt;a href=&quot;http://quickdocs.org/cl-markdown/&quot;&gt;cl-markdown&lt;/a&gt; library and
I’m not sure if it was the best choice possible. Why? Because it’s
source code is close to impenetrable to me and documentation is almost
non-existent.  Maybe I’m not a good reader, but I took me several
evenings to get my head around library internals and to understand
ways to extend it.&lt;/p&gt;

&lt;p&gt;For links generation I kept the assumption that I only have one
database open at a time and it live in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*posts*&lt;/code&gt; variable.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl-markdown&lt;/code&gt; unfortunately doesn’t export any methods it uses for
html generation and I had to &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/markdown.lisp&quot;&gt;get&lt;/a&gt; into library package to
extend it the way I want.&lt;/p&gt;

&lt;p&gt;Inline elements are rendered using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;render-span-to-html&lt;/code&gt; generic
function and Common Lisp rocks again there because it allows adding
an auxiliary method for it and target a specific set of arguments, that
allows to keep method body clean from unnecessary modifications:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;render-span-to-html&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:before&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eql&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'inline-link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;encoding-method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cl-journal.db:get-by-fname&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cl-journal::*posts*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cadr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;record&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cadr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cl-journal.db:url&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What happens is that for links the first element in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt; element
holds a link that will be put in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;href&lt;/code&gt;, and in before element we can
do a lookup in the database and replace a link if necessary. I think
it’s an amazing level of flexibility with this amount of effort.&lt;/p&gt;

&lt;p&gt;Another extension that I wanted to have was about links to the other
blogs. Livejournal has some custom tags for different purposes and in
this case it uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;lj user=&quot;can3p&quot;&amp;gt;&lt;/code&gt;, where user attribute holds the
name of a blog. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl-markdown&lt;/code&gt; allows extensions and in order to make
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{lj-user can3p}&lt;/code&gt; render to the desired tag it’s possible to use an
official extension logic:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;defextension&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lj-user&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:arguments&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:insertp&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ensure-string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;safe-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;html-safe-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ecase&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;phase&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:render&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;lj user='~a'&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;safe-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This looks and is indeed easy but it took me quite some time to figure
all the details out.&lt;/p&gt;

&lt;p&gt;This part concludes main highlights of the client. Of course, there is
more, you can check out the amount of &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/main.lisp&quot;&gt;commands&lt;/a&gt; supported at the
moment.  Some of them like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;last&lt;/code&gt; are there just for
convenience to make it easier to start writing a new post or quickly
open the last one in case of typos, some like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;status&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;url&lt;/code&gt; are
informative in a sense that they help to query the database and give out
information, last group like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ignore-all&lt;/code&gt; is used to fix the
imperfections in file state handling logic.&lt;/p&gt;

&lt;p&gt;There is one more group I haven’t talked about, which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remerge&lt;/code&gt; and this is what I’m going to talk about next.&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Part 1: Intro</title>
    <link href="https://can3p.github.io//blog/2018/06/19/client-writeup-intro/"/>
    <updated>2018-06-19T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2018/06/19/client-writeup-intro</id>
    <content type="html">&lt;p&gt;Once I decided to sit down and write a perfect blog client for
me. Since it was not just about the result but also about the process I
chose a language I enjoyed the most and this is how it ended up being
written in Common Lisp. I didn’t think I’d get that far and I’d like
to share my experience in the process along with all little details
that I had to go through during the implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt; Please treat this post as an experience report more
than anything else because this is what it is. As a consequence, there
can be a lot of things real lisp wizards will raise an eyebrow on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note 1&lt;/strong&gt; I’m glad to hear any feedback. If you have any suggestions
on the text, want to fix a typo or correct my explanation, please
consider opening an issue in the &lt;a href=&quot;https://github.com/can3p/can3p.github.io/issues&quot;&gt;blog repo&lt;/a&gt;. If you think
that client code doesn’t do what it has to do, please make a pull
request or open an issue in &lt;a href=&quot;https://github.com/can3p/cl-journal/issues&quot;&gt;client repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note 2&lt;/strong&gt; I decided to make all links to the code against fixed sha1
so that future changes do not invalidate them. I’m not sure if I
will frequently revisit this document and I hope that the post
will remain relevant with this approach.&lt;/p&gt;

&lt;p&gt;Below you will read a story about writing a blog client for
livejournal.com web service in Common Lisp. As one may note, it sounds
like using a very niche language for a near dead web service. To make
you a bit more surprised I can add that I’ve been working on it on and
off for a couple of years now.&lt;/p&gt;

&lt;p&gt;It used to be very simple but now &lt;a href=&quot;https://can3p.github.io/cl-journal/&quot;&gt;cl-journal&lt;/a&gt; is quite
powerful. For example, it handles:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Any Livejournal compatible service (like Dreamwidth)&lt;/li&gt;
  &lt;li&gt;Publishing new posts and updating changed ones&lt;/li&gt;
  &lt;li&gt;Post text is written in markdown, many Livejournal fields are supported,
such as visibility, location or music.&lt;/li&gt;
  &lt;li&gt;Fetching posts from remove server and transforming them into markdown
format.&lt;/li&gt;
  &lt;li&gt;Lot’s of smaller stuff like links to local files being transformed
to actual post urls, drafts, pre-commit hooks, password management etc.&lt;/li&gt;
  &lt;li&gt;And a lot more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve been using Livejournal since 2005 and never had a reason to
switch to anything else. I almost never write on political topics and
don’t really care where it’s hosted. I don’t even need many
connections but do value some and the fact that Livejournal provides
near zero ways to discover other blogs sometimes works more as a
benefit than a drawback, because with the current state of things when
majority of users migrated to Facebook or Instagram, there is not that
much to read there. It’s not as engaging as Facebook, and that’s
&lt;em&gt;good&lt;/em&gt;. The other benefit of the service is that it’s a very good site
to write long posts like this one and people actually do that and
there is a ton of beautiful content there, if you’re lucky to find it
of course.&lt;/p&gt;

&lt;p&gt;My main problem with Livejournal always was that I didn’t really own
the content in a sense of storage. The blog is mine, ok, but if web
service goes down or account gets blocked I’ll lose all my ~1400 posts
written to date without any good way to get them.&lt;/p&gt;

&lt;p&gt;From time to time I tried to use other blog platforms or static site
generators (like this site) and my real dream was to combine them and
have sources in markdown locally, so that I can edit them in my
favorite editor and still publish them on a platform that has at least
some social flavor. Blog on Github + Disqus is not social for sure.&lt;/p&gt;

&lt;p&gt;I had this idea for some time and somewhere around 2016 got really
burned out by trying to ship yet another web service in my spare time
in addition to those that I ship for my company. To recover I decided
to go in the direction that absolutely excluded any interest except my
own and didn’t have any money or fame in sight.  You see now, Common
Lisp and idea about better blogging at the time where blogging is
already out of fashion is almost a perfect match! And this is where it
all started.&lt;/p&gt;

&lt;h2 id=&quot;idea&quot;&gt;Idea&lt;/h2&gt;

&lt;p&gt;What does perfect blogging experience look like? It’s when you write
the text in your favorite editor, save it, push it to git and it’s
published. And it’s when you can just grep your posts to remember
something from the past or when you can do bulk updates to all the
posts with system tools that you’re used to and get all changes
published at once.&lt;/p&gt;

&lt;p&gt;This was my thought process and this is what became a base of
requirements. A client should be able to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;See new markdown files and publish them&lt;/li&gt;
  &lt;li&gt;See modified markdown files and submit changes&lt;/li&gt;
  &lt;li&gt;See deleted markdown files and  delete them in the service&lt;/li&gt;
  &lt;li&gt;Bonus points if it all happens automagically on git commit/push&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s a simple version of the client that implies that a folder with
markdown files acts as a master database that gets replicated to a
slave which in this case is web service. An obvious drawback here is
that I lose any other ways to update content, be it via service web
interface or it’s mobile app. So an even more perfect client should be
able to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fetch any updates from the service&lt;/li&gt;
  &lt;li&gt;Transform them into markdown format so that all synced posts can be
later edited in a usual manner.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One final thought was that since all editing happens in an ordinary
editor, an obvious way to implement client is to make a cli tool for
that. And if you check requirement you’ll spot that they resemble
typical git operations a lot and it makes sense to mimic git whenever
it makes sense.&lt;/p&gt;

&lt;h2 id=&quot;common-lisp-nontrivialities&quot;&gt;Common Lisp nontrivialities&lt;/h2&gt;

&lt;p&gt;When you start working on a really big task, the only thing that can
be said for sure is that there are a lot of unknowns. And if in a
familiar language most of the unknowns lie in the business domain, in
my case a pile of unknowns also waited to be found on the language and
ecosystem side. Some of the things that I thought to be trivial and
that were doable in many languages appeared to be not trivial at all
in Common Lisp and that required a lot of time to grasp to get a full
picture.&lt;/p&gt;

&lt;p&gt;Let me go through some of the topics that appeared to be nontrivial.&lt;/p&gt;

&lt;h3 id=&quot;current-working-directory&quot;&gt;Current working directory&lt;/h3&gt;

&lt;p&gt;This is one such case. It’s just one parameter, what can go wrong?
Well, it’s two, at least in sbcl. It appears that there is a
distinction between cwd of external commands run by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uiop/run-program&lt;/code&gt;
and internal lisp facilities. Let’s make a following experiment:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# preparation&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; ~/test_cwd/second_folder &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~/test_cwd
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;touch &lt;/span&gt;test.file
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;touch &lt;/span&gt;second_folder/nested.file
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sbcl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and lisp session:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;* (ql:quickload :uiop)
To load &quot;uiop&quot;:
Load 1 ASDF system:
    uiop
; Loading &quot;uiop&quot;

(:UIOP)

* (probe-file &quot;test.file&quot;)

#P&quot;/home/can3p/test_cwd/test.file&quot;

* (uiop/run-program:run-program  &quot;ls&quot;
    :input :interactive
    :output :string))

&quot;second_folder
test.file
&quot;
NIL
0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So far so good!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;* (uiop/os::chdir &quot;second_folder&quot;)

0
* (uiop/run-program:run-program  &quot;ls&quot; :input :interactive :output :string)

&quot;nested.file
&quot;
NIL
0
* (probe-file &quot;nested.file&quot;)

NIL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Oops. Yes, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;probe-file&lt;/code&gt; expects something else.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;* (setf *default-pathname-defaults* #P&quot;second_folder&quot;)

#P&quot;second_folder&quot;
*  (probe-file &quot;nested.file&quot;)

#P&quot;/home/can3p/test_cwd/second_folder/nested.file&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And to double check.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;* (setf *default-pathname-defaults* #P&quot;/home/can3p/test_cwd/&quot;)

#P&quot;/home/can3p/test_cwd/&quot;
*  (probe-file &quot;test.file&quot;)

#P&quot;/home/can3p/test_cwd/test.file&quot;
*  (uiop/run-program:run-program  &quot;ls&quot; :input :interactive :output :string)

&quot;nested.file
&quot;
NIL
0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that trailing forward slash in pathname is important, don’t lose
it.  As you see, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*default-pathname-defaults*&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uiop/os::chdir&lt;/code&gt;
are independent and influence different things and to keep it
consistent for both standard library and external calls it’s necessary
to call them both.&lt;/p&gt;

&lt;p&gt;It’s worth saying that this distinction is of particular importance
for repl based development, because when the image starts these two
values are most probably aligned.&lt;/p&gt;

&lt;p&gt;Another note is that the behaviour described belongs totally so sbcl,
other implementations may behave differently.&lt;/p&gt;

&lt;p&gt;Since I mentioned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uiop/run-program&lt;/code&gt;, let’s see how I used it.&lt;/p&gt;

&lt;h3 id=&quot;user-input&quot;&gt;User input&lt;/h3&gt;

&lt;p&gt;User input is hard in a sense that it’s not only about Common Lisp
versus the world it’s also about different operating systems, and
it’ll be nice if all user input works in emacs repl too.&lt;/p&gt;

&lt;p&gt;The simplest ever way exists for yes/no questions and it’s as simple
as&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;y-or-n-p&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Do the thing?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;do-the-thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But let’s say that you want to read an actual input. There is a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read-line&lt;/code&gt; function that can help. One complication is a prompt
message that you may want to display. Simply formatting it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STDOUT&lt;/code&gt;
will result in bad results in emacs in a sense that prompt text will
be shown after actual input. To solve this I &lt;a href=&quot;https://stackoverflow.com/questions/19204332/slime-prints-my-format-calls-only-when-called-function-ends&quot;&gt;found&lt;/a&gt;
that it’s necessary to force output on a used stream and now prompt
function looks like this for me:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;prompt-read&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*query-io*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a: &quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;force-output&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*query-io*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read-line&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*query-io*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that’s not all, because there is another possible user input which
is passwords. Why is it unique? You probably will not want to enter it
clear text. Unfortunately, I didn’t find a proper way of doing this
magic inside of lisp and in the end the solution was to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stty&lt;/code&gt; to
manipulate input visibility and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run-program&lt;/code&gt; and internal bash &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt;
function to get user input:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;prompt-read-password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*query-io*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~a: &quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;force-output&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*query-io*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;run-program&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;stty -echo; read val; stty echo; echo $val&quot;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:output&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:input&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;string-trim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;#\Newline&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;#\Space&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately this workaround doesn’t behave well in Emacs slime
session.&lt;/p&gt;

&lt;h3 id=&quot;storing-passwords&quot;&gt;Storing passwords&lt;/h3&gt;

&lt;p&gt;Next two are not Common Lisp specific but might be of interest for the
curious ones.  Since I read the password I wanted to store it to avoid
asking for it again and again. However, I wasn’t fond of storing it in
clear text and decided to explore system-specific solutions.&lt;/p&gt;

&lt;p&gt;Mac os is perfect in this case because of built-in program
&lt;a href=&quot;https://www.netmeister.org/blog/keychain-passwords.html&quot;&gt;security&lt;/a&gt; that allows to store and retrieve
passwords. For Linux (Ubuntu in particular) there are no built-ins,
however, after some search I found &lt;a href=&quot;https://wiki.gnome.org/Projects/Libsecret&quot;&gt;secret tools&lt;/a&gt; that
provided similar functionality:&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;get-password-cmd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;#-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LINUX&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;security find-internet-password -a ~a -s ~a -w&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;#+&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LINUX&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;secret-tool lookup '~a:login' ~a&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;#-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LINUX&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;set-password-cmd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;security add-internet-password -a ~a -s ~a -w '~A'&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;#+&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LINUX&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;set-password-cmd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;secret-tool store --label='~a' '~a:login' '~a'&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Why is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set-password-cwd&lt;/code&gt; signature different? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secret-tool&lt;/code&gt; will read
the password for you and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;security&lt;/code&gt; expects it as an input.&lt;/p&gt;

&lt;h3 id=&quot;packaging-and-running&quot;&gt;Packaging and running&lt;/h3&gt;

&lt;p&gt;Since the client is a cli tool, it’s really desired to compile the
client system somehow. It’s not difficult by itself but I decided to
place an additional restriction there and make it easy to run and
install it both in a dev environment and as an easily installable package.&lt;/p&gt;

&lt;p&gt;A task of running the system as a script is perfectly solved by
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roswell&lt;/code&gt; &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/roswell/cl-journal.ros&quot;&gt;script&lt;/a&gt; and compilation is &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/Makefile&quot;&gt;done&lt;/a&gt; with a &lt;a href=&quot;https://www.xach.com/lisp/buildapp/&quot;&gt;buildapp&lt;/a&gt;. Since script cannot be used by
buildapp to build a system and roswell and buildapp pass slightly
different parameters to the entry point I ended up moving entry point
logic into a &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/5659a99e89cc392fbd56ee3659e70ee8743e2b3e/src/main.lisp&quot;&gt;separate package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Buildapp itself works amazingly well in case lisp and quicklisp are
set up on the computer, however, I wanted to make it really easy
installable and attaching a binary file to Github release doesn’t
sound user friendly for cli tool. The usual way to install a program
on Mac OS nowadays is via Homebrew. A specific requirement for
homebrew formulas is not to use any kind of additional package manager
to build a program, hence quicklisp could not be used as is and I
looked for a workaround.&lt;/p&gt;

&lt;p&gt;The result was &lt;a href=&quot;https://github.com/can3p/cl-brewer&quot;&gt;cl-brewer&lt;/a&gt; system that generates a Homebrew
formula with urls pointing to all the dependencies so that formula can
download them all and then use quicklisp and buildapp to make a binary
without any additional downloads.&lt;/p&gt;

&lt;p&gt;For Linux distributions I didn’t bother to create a package, however
making a deb-package or a snap or whatever looks easy enough. It
just waits for a hero that will do it.&lt;/p&gt;

&lt;p&gt;A little note about arguments parsing. There are libraries for this
task in the ecosystem but I ended up using none of them. I wanted
to have a command based cli with some interactivity and the only
things important in this case were first and maybe the second argument.&lt;/p&gt;

&lt;h3 id=&quot;editing&quot;&gt;Editing&lt;/h3&gt;

&lt;p&gt;One last bit of user input! What I wanted to do is to allow to invoke
a user editor (set by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$EDITOR&lt;/code&gt; environment variable in case it’s
necessary from the cli tool. That also appeared to be not so trivial,
because actual implementation differs by Common Lisp implementation
used.&lt;/p&gt;

&lt;p&gt;Fortunately, I found &lt;a href=&quot;https://github.com/sanel/magic-ed&quot;&gt;magic-ed&lt;/a&gt; on Github and it magically
did the right thing (again, not from inside of emacs, which is
unfortunate). Quicklisp didn’t have it and I ended up including it in
my code to simplify the build process.&lt;/p&gt;

&lt;h3 id=&quot;pre-commit-hook&quot;&gt;Pre-commit hook&lt;/h3&gt;

&lt;p&gt;This one is not really Common Lisp specific, but it turned out to be
pretty useful. What I really wanted to do is to use the client with
git to have all my posts under version control. In this case the best
ever flow looks like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;cl-journal new post-slug
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# thanks to magic-ed, an editor opens and you can write your beautiful post&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;another post&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that new post file along with all the generated metadata should
be included in the commit. A trick here is to include all changes in
the pre-commit hook. Apparently, git allows that. Here is how current
cl-journal pre-commit hook looks like:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
cl-journal push
git add posts.lisp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, I didn’t figure everything out beforehand, but it took long
enough to be worth sharing. Now let’s get to the client internals.&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Perlotto - youtube support</title>
    <link href="https://can3p.github.io//blog/2018/05/30/perlotto-youtube/"/>
    <updated>2018-05-30T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2018/05/30/perlotto-youtube</id>
    <content type="html">&lt;p&gt;&lt;em&gt;Ouch&lt;/em&gt;: This post was supposed to be published back in September ‘17, but I
saved it in a future date. Here you go, anyway.&lt;/p&gt;

&lt;p&gt;One more update about &lt;a href=&quot;https://github.com/can3p/perlotto&quot;&gt;Perlotto&lt;/a&gt; app (last update is &lt;a href=&quot;/blog/2017/06/25/electron-2/&quot;&gt;here&lt;/a&gt;). I decided
to work on it again, because of annoyance I felt from using youtube in web
browser. It’s just dumb to go from a decent players we all had to a web
page that doesn’t work with multimedia keys to say at least.&lt;/p&gt;

&lt;p&gt;Integration appeared to be quite easy and now it’s more or less trivial to
extend perlotto to work with your favorite service (pull requests are welcome, btw).&lt;/p&gt;

&lt;p&gt;The whole integration part is split into several things that need to be done
to make service work:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;How to understand that player page is loaded&lt;/li&gt;
  &lt;li&gt;How to understand that player is playing&lt;/li&gt;
  &lt;li&gt;How to do play/pause ot switch to the next or previous track&lt;/li&gt;
  &lt;li&gt;How to get information about current song.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one appeared to be the trickiest, and the reason is that the only
way to get this data is to parse title and description, which are free form.
For now I decided to go with simple spitting of the title string, but I’m
totally sure, that it can be done hundred time better.&lt;/p&gt;

&lt;p&gt;One part that I haven’t mention in the list is ads. I’m more or less fine
with them, but it appears the without mblock origin youtube plays ad video
after every second song and it was annoying to extend that I decided to cut
it.&lt;/p&gt;

&lt;p&gt;Electron provides a quite convenient api for this usecase, it’s possible
to control and intercept requests on every stage of the lifecycle. The only
thing that’s tricky is how to setup filters, because filter argument looks
very misleading. After some trial and error I ended up with this code:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;initFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;defaultSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;webRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onBeforeSendHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;checkFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)});&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Calling this function will force electron to pass every single request via the
callback, where the actual filter lives:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;checkFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ad_filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;acc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ad_filters&lt;/code&gt; is an array with regexes that are applied to the url, any match
leads to the blocking of the request (full code &lt;a href=&quot;https://github.com/can3p/perlotto/blob/master/main.js&quot;&gt;there&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Current support is rudimentary and can certainly be improved. Some of the
possible features are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Smart track info discovery. With some more logic it’s possible to achieve
much better results with decoding track, artist and album title.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Full albums handling. Putting full albums on youtube is really popular now,
and most of the times you can see a full tracklist with timestamps in description
or one of the comments. It would be a reall dealbreaker to enable perlotto
to read this informaton and to integrate it with all the media keys and track
information.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;outro---spotify&quot;&gt;Outro - spotify&lt;/h2&gt;

&lt;p&gt;Another service that I decided to integrate was spotify. All the steps seemed
to be pretty easy, except that I’ve found out that spotify web player doesn’t
work in the perlotto due to abscence of widevine plugin.&lt;/p&gt;

&lt;p&gt;Adding support required much more work  - it should be downloaded separately
and version of the plugin should match version of the chrome browser, which
sounds like a lot of troubles without real benefit since I’m not a frequent
spotify user.&lt;/p&gt;

&lt;p&gt;Maybe I’ll do it some day, but if you’re interested, please don’t stop yourself
from doing a pull request!&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Building electron app - last.fm support</title>
    <link href="https://can3p.github.io//blog/2017/06/25/electron-2/"/>
    <updated>2017-06-25T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2017/06/25/electron-2</id>
    <content type="html">&lt;p&gt;Half a year ago I’ve &lt;a href=&quot;/blog/2016/09/13/electron/&quot;&gt;built&lt;/a&gt;
and Electron &lt;a href=&quot;https://github.com/can3p/perlotto&quot;&gt;app&lt;/a&gt; to turn Google Music
service into something behaving like a desktop app. The overal setup turned
out to be exceptionally stable - there was no need for any change since then.&lt;/p&gt;

&lt;p&gt;Last week I checked out my Last.FM profile and I though it’s quite a miss that
I stopped collecting all the stats about the playback, mostly because there is
a real discovery problem with a large music collection - you listen to small
fraction of it and forget about older additions as soon as you switch to the
new ones. And since you forget you don’t have an easy way to ask Google Music
to play something that you liked in 2015. On the other hand Last.FM gives users
decent analytics and some discovery tools which are quite handy for me as well
because I don’t use Spotify.&lt;/p&gt;

&lt;p&gt;So, last week I finally decided to spend time implementing it and there was
a couple of issues I’ve spent a plenty of time on, that are worth talking about.
So, here we go!&lt;/p&gt;

&lt;p&gt;This is a general flow I wanted to implement:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;User launches the app first time&lt;/li&gt;
  &lt;li&gt;There is a menu item “Connect to Last.FM”, user clicks on it&lt;/li&gt;
  &lt;li&gt;New window is opened with Last.FM authorisation page&lt;/li&gt;
  &lt;li&gt;After user confirms, window is closed, app starts scrobbling from the next track.&lt;/li&gt;
  &lt;li&gt;Menu item is changed to “Disconnect from Last.FM”. If user clicks it
app deletes session information, menu gets back to the restored state.
If not, user should get into authorized state after launching app next time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;track-information&quot;&gt;Track information&lt;/h2&gt;

&lt;p&gt;First of all I decided to check whether it’s even possible to discover information
about new song without dumb polling or hacking into compiled js. There were no
obvious events flying around, but the dom looked well structured, so just two
functions were needed to understand the current state of the player:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isPlaying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#player-bar-play-pause&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;playing&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;trackInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#currently-playing-title&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.player-album&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.player-artist&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerText&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I don’t do any error checking there at the moment, but it’s that simple. Now, that
I can get the desired information, the solution is to store the information about
current track and send an event outside whenever track is changed:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentTrack&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tracksEqualp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;album&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;album&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;artist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;analyzeTrackChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;playing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isPlaying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;playing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;trackInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tracksEqualp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;currentTrack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;currentTrack&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;scheduleScobbleUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// I'll come to that later&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;ipcRenderer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;player-song-change&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Really simple. Now we need to figure out when should I call this function
to understand wether new track started playing. Luckily we have &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events&quot;&gt;Mutation
events&lt;/a&gt;
now and we can simply subscribe to changes of the event we’re interested
in.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;runWhenTrackInfoChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#playerSongInfo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;observer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MutationObserver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;childList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;subtree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// see crbug.com/134322&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;observer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Events work amazingly good - I’ve tested it and event is fired only once
in my case. So now the only thing remaining is to understand when we can
actually start listening for changes. It’s obviously not DOMReady, and
I reverted to polling there.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;playerLoadedp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#player-bar-play-pause&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;runWhenLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;playerLoadedp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;playerLoadedp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;clearInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, all necessary pieces are there, and what’s left is to plug them together:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;runWhenLoaded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;runWhenTrackInfoChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;analyzeTrackChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, that would be enough if we only could notify Last.FM right after track
change, but scrobbling has some limitations - it api should be called later
then 30 seconds after start of the playback + there are limitations for
the track length. Now that I think of it, the best solution would be to
fire the scrobble event on track change as well, but back then I decided
to have a scheduling function for that:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updateTimer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;scheduleScobbleUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;clearTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;updateTimer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;updateTimer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;ipcRenderer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;player-scrobble-time&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;61000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// a bit bigger then minimal limit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Timestamp is necessary for the Last.FM api. Whenever user or app switches a
song, timer is reset and scheduled again, so that I don’t fire events when
it’s not needed.&lt;/p&gt;

&lt;p&gt;Why did I decided to go with events? The reason is that they provide a good
abstraction and decouple track notification from actual scrobbling calls.&lt;/p&gt;

&lt;p&gt;Here is how things look like in main thread, after everything is implemented:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;ipcMain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;player-song-change&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;lastfm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;nowPlaying&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;ipcMain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;player-scrobble-time&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;lastfm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scrobble&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;lastfm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In future it would be trivial to add notifications and other bloat there,
but it’s not my future, consider forking if you really need it!&lt;/p&gt;

&lt;h2 id=&quot;menu&quot;&gt;Menu&lt;/h2&gt;

&lt;p&gt;I follow a bottom-up approach when developing new features. That really
helps - instead of trying to do everything at once I can slowly create
building blocks that will make final solution trivial. Providing
events was a first step (itself made from small building blocks). After that
I decided to check if I can manipulate menu at all. It appears that it’s
quite simple, just a bit unusual.&lt;/p&gt;

&lt;p&gt;It’s not really possible to manipulate individual items of the menu, the
only working way was to create a full menu and replace everything. If you
think about it, it’s even better. First, template:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getMenuTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lastFMEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;submenu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;toggledevtools&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;separator&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lastFMEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Disconnect from Last.FM&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastfm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disconnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;updateMenu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Connect to Last.FM&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastfm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authorize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;updateMenu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;separator&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;quit&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Whenever menu has to be modified, we call updateMenu function that gathers
everything necessary for proper render:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function updateMenu() {
    var lastFMEnabled = lastfm.isAuthorized();
    var menu = Menu.buildFromTemplate(getMenuTemplate(lastFMEnabled));
    Menu.setApplicationMenu(menu);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, the only thing remaining is actual authorization with Last.FM&lt;/p&gt;

&lt;h2 id=&quot;lastfm-authorization&quot;&gt;Last.FM Authorization&lt;/h2&gt;

&lt;p&gt;Authorization logic has following requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;When user clicks on authorize link we should open authorization window&lt;/li&gt;
  &lt;li&gt;If user aborts the process / closes the window nothing should happen, user
should be able to start again&lt;/li&gt;
  &lt;li&gt;If user grants permission, we need to close the window and update the interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After some research I came to a conclusion that the only viable flow is
to use Last.FM authorization method that redirects user to the specified
url on success. Local urls (file:///) did not work for me with any options,
so I decided to go with local webserver.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4567&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://127.0.0.1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;connect_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;  &lt;span class=&quot;nx&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/auth&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startServer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// we don't want to fail if we did not stop previous server&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pathname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pathname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/auth&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stopServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;writeHead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;text/html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stopServer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since we only need to understand if interaction happened, simple callback
is enough, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connect_url&lt;/code&gt; will be used in other parts of the program. Please
note the trick with the port - I decided to go for it since stopping the
server doesn’t necessarily mean that connection is closed, so I decided
to go with changing the port to be on the safe side. Frankly that’s not
enough since chosen ports might be used, but I decided not to fight with
that corner case for now. Also, functions are exported for testing purposes.
And now, main part:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authorize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lfmWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;lfmWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;lfmWindow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;BrowserWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stopServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;lfmWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;lfmWindow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;nx&quot;&gt;lfm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lastfm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;lfmWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;lfmWindow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stopServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lfm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getAuthenticationUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;connect_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;lfmWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;loadURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disconnect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lastfm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, there is nothing we want to understand except whether authorization
succeeded, so I go with a simple callback and had all handling logic inside.
Settings are saved with synchronous library, since I did not want to deal
with it for the sake of simplicity.&lt;/p&gt;

&lt;p&gt;After authorization part is done, the only thing needed is a proper initialization:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lastfm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;lfm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setSessionCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isAuthorized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To complete the module I decided to add functions for scrobblying and updating
the service about currently playing song. To avoid complicating interface in
the main module they both fall back to noop if Last.FM integration is not set up.&lt;/p&gt;

&lt;p&gt;After this point I’m ready to say that Perlotto reached full functionality state,
and hopefully I won’t need to update it for another year or so. :)&lt;/p&gt;

&lt;p&gt;Finally, I hope some of the tricks described in the post will save few hours
to devs struggling with them.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Common lisp - good parts</title>
    <link href="https://can3p.github.io//blog/2017/05/12/common-lisp/"/>
    <updated>2017-05-12T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2017/05/12/common-lisp</id>
    <content type="html">&lt;p&gt;Since last summer I decided to invest some time into learning common lisp and
wasn’t disappointed.  Certainly, the language shows it’s age and there are some
issues with documentation and libraries, but the benefits are so great that it’s
hard to return to usual languages of choice.&lt;/p&gt;

&lt;p&gt;A week ago we had a first &lt;a href=&quot;https://www.meetup.com/Amsterdam-Lisp-Scheme-Meetup/&quot;&gt;Lisp and Scheme
meetup&lt;/a&gt; in Amsterdam
(please, join the next one!) and I’ve prepared a little talk to discuss what
features I find especially affordable and powerful.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/YfTh0O8e8F0?ecver=1&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Since I’m not a perfect speaker by any means, here is a text version.&lt;/p&gt;

&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;/h2&gt;

&lt;p&gt;Tech culture is really spoiled by hacker news syndrome. If some corp © has a
budget big enough to promote the language, we’ll see it all over the place, and
of course, everything starting from cli parser and editor and ending with
distributed systems will be implemented there. If they scream loud enough, it’s
really easy to assume that these concepts or methods were invented there.&lt;/p&gt;

&lt;p&gt;That leads us to a skewed perception of the world. We always think that we’re
moving from some sort of dark ages towards something looking like bright
future. It could be so but it’s not necessarily so.&lt;/p&gt;

&lt;p&gt;Having no formal CS education certainly does not help. I started my career as a
developer working with Pascal and Delphi, then moved on to the web development
path where I mostly lived inside of a page request with languages like PHP or
web page lifetime with Javascript. Although I did progress with understanding
the whole stack from bottom to the top I was exposed to one specific way
of doing things which is dynamic languages and it bothered me since web
development is detached from system programming and associated languages. At
some point I encountered clojure and it was no less than a turning point in my
life. The experience was so different that to this point I can’t imagine
spending my free time on something less powerful.&lt;/p&gt;

&lt;p&gt;The core of the experience was of course in the interactive development which
makes development so much faster, I’ll talk about it later.&lt;/p&gt;

&lt;p&gt;When you have a language that powerful you want to write basically everything
with it and having the jvm as a backend meant slower startup times which is not
acceptable for cli programs. For sure there are hacks to fix that, but I wanted
a cleaner solution and of course every flamewar around clojure had some sort of
comparison to common lisp. So in the end I decided to go back to the roots and
to try it out and learn the language.&lt;/p&gt;

&lt;p&gt;So, common lisp. Some of it’s concepts may look surprising but were not uncommon
in the early nineties. I’m not a Common Lisp expert, so the rest of the post is
not an authoritative source, but more my perception of the language at the
time of writing. If you see anything clearly wrong, please tell me.&lt;/p&gt;

&lt;p&gt;I’ve taken three different aspects which are notable for me one section for each.
Here we go.&lt;/p&gt;

&lt;h2 id=&quot;stability&quot;&gt;Stability&lt;/h2&gt;

&lt;p&gt;A small section for the big feature. Stability of Common Lisp as a language and
platform is remarkable. It’s specification was in the works for ten years and
hasn’t been changed since original publication. There are constant debates
around this, every year or two one more attempt to modernize language &lt;a href=&quot;http://cl21.org/&quot;&gt;pops
up&lt;/a&gt;. For sure they target some problematic areas but problems
are not so severe for these projects to get traction.&lt;/p&gt;

&lt;p&gt;From the other side specification is really complete, so you can see a defined
behavior for any corner case (even if it’s specified as an implementation
defined - these places usually talk about the internals of the language).  There are
around 10 different compilers that are actively developed and provide different
sets of features and most of them have been in development for years so you can
imagine the level of robustness there.&lt;/p&gt;

&lt;p&gt;I would say that sbcl is more or less a default choice at the moment, and there
are differences between implementations as there are compiler specific packages
and apis, so it’s not all roses.&lt;/p&gt;

&lt;p&gt;Code that was written fifteen or twenty years ago can run on any modern
compiler. How cool is that?&lt;/p&gt;

&lt;h2 id=&quot;interactive-development&quot;&gt;Interactive development&lt;/h2&gt;

&lt;p&gt;Interactive development is one of the major features of the common lisp and a
cornerstone of the language design. Everything was made in a way to allow this
method of development.&lt;/p&gt;

&lt;p&gt;The easiest way to show this is to outline how development happens in common
lisp and in classic languages.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/img/2017-05-12-common-lisp/usual_flow.png&quot; alt=&quot;:(&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Most programming environments do not provide too many tools to understand and
change the state of the running process, I’m not even talking about actual
development there. In some the only way is printing data into STDERR, in others
you can use a debugger to set a breakpoint and look around. Frontend developers
are luckiest in this case - web page is really easy to introspect by definition
and there are ways to do bits of interactive development there (just look at all
the excitement about css hotloading) and it’s also a place where it’s easy to
feel the difference. Once you have a dynamic page and build pipeline you
immediately lose in productivity and it requires more and more efforts to
compensate. In other environments you face a necessity of building program
interface from day one - write cli interface, make assumptions about the structure
of data and prepare some stubs, understand how this data gets into the program
and back from it. Depending on requirements it can take few hours or few days to
get this fully working, and that’s the time you could spend on solving the main
problem.&lt;/p&gt;

&lt;p&gt;Anyway, after finishing with that you get into a usual cycle of development. You
make a change, change all the data storage etc. to accommodate for new data
structures, compile and restart server / run tests, where you’ll load all stubs
and hopefully get the desired result. If not you penetrate your program with all
sorts of print statements just to understand what happens.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/img/2017-05-12-common-lisp/interactive_flow.png&quot; alt=&quot;:)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Interactive development in common lisp is different in sense that you start
developing by starting repl-process and you can already start solving problem
and write necessary business logic, all the stubs can be generated
programmatically and put into the variables as you go, No cli interface is
necessary. While being attached to the process it’s possible to access any
package, inspect any variable, change any data structure and define or change
any new function. That means that change cycle is reduced to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;change code -&amp;gt;
recompile function&lt;/code&gt; and all proper interface is moved to a later stage.&lt;/p&gt;

&lt;p&gt;All this does not deny testing, it’s just that you do not rely only on it and
can have much quicker troubleshooting cycle. Moreover, in many cases you can
live without a proper external interface for a long time or not need it at all if
your task is only to calculate something or prove the idea and you won’t
lose any flexibility because you’re calling your code directly by passing
language data structures. Btw, this is another advantage which is everywhere in
common lisp - in many cases you don’t need to parse strings or invent
serialization formats you can easily use language data structures.&lt;/p&gt;

&lt;p&gt;That’s actually how any lisp program works - lisp process starts, loads and
compiles some lisp code and runs some function on it. Why it works like this is
because CL is an image based language - during the interactive development
session developer shapes the program to a particular state where he can dump
the image into a binary file and this is a default way of compiling programs in
common lisp.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ql:quickload&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'trivial-dump-core&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'your-awesome-project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;trivial-dump-core:save-executable&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;awesome_project&quot;&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;your-awesome-project:main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What happens when you dump image?  All the environment is dumped there -&amp;gt; you
have a compiler etc when you run it again Startup time of the image is really
fast - ~10-30ms Dumping state means saving all the memory state like variables
and that means that theoretically you only need to dump image of problematic
environment to get all the information Dumped image has no dependencies, so it
can be safely deployed as one file.&lt;/p&gt;

&lt;h2 id=&quot;extensibility&quot;&gt;Extensibility&lt;/h2&gt;

&lt;p&gt;An important point is that the word “Common” in Common lisp means that the
language was created as a response to the existence of many slightly or very
different lisp dialects that had to be united. On one hand, it resulted in some
duplication (like structs and classes), on the other hand, extensibility is a
cornerstone there are ways to extend lots of different aspects of the
language or program.&lt;/p&gt;

&lt;h3 id=&quot;assignment&quot;&gt;Assignment&lt;/h3&gt;

&lt;p&gt;What can be simpler than plain assignment? Really, nothing to see there. We can
assign to variable, or maybe have a setter on the object.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;myObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In common lisp the operation is generalized to setting the result of form to a
place.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;place&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;form&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;form&amp;gt;&lt;/code&gt; is easy, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;place&amp;gt;&lt;/code&gt; is much more interesting, because variable is
only one of it’s possible meanings.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;aref&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;some-field&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-storage&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can define a function call as a setf, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(aref arr 0)&lt;/code&gt;, or
basically any other form via the macro, so that you can have mirrored get/set
interface in almost any imaginable case and it will work consistently via setf.
For example all the getters for the object fields are defined with setf
extensions.&lt;/p&gt;

&lt;h3 id=&quot;generic-functions&quot;&gt;Generic functions&lt;/h3&gt;

&lt;p&gt;Generic functions are one of the fundamental parts of the Common Lisp and they
are fantastic in a way that although they are dispatched against types they are
totally decoupled from them - all of them, not just the first one. Common Lisp
automatically orders implementations against declared types and chooses the most
specific implementation for every call. As a consequence new functionality can
be easily implemented against any existing class hierarchy, even defined in the
other library.&lt;/p&gt;

&lt;p&gt;For example we work with some kind of DOM tree and want to print
it in our specific way. We can define a generic function, give default
implementation for it and add any number of specific implementations for certain
kinds of elements.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defgeneric&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;print-xml&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;optional&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;indent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;print-xml&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;element&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;optional&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;indent&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;print-xml&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;dict-article&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;optional&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;indent&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that means that you don’t have to make a wrapper on such classes or do
inheritance just to add a method, you can just implement business logic against
it.&lt;/p&gt;

&lt;p&gt;Another advantage is that with generics we get another way to group
functionality - around functionality and not around objects.&lt;/p&gt;

&lt;p&gt;As if that wasn’t enough generic functions come with a complete support of
aspect-oriented programming with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:before&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:after&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:around&lt;/code&gt; modifiers.  That
adds yet another direction of extension and may libraries and common lisp as a
language define generic functions as external api so that clients can add hooks
in to particular points in the program without  passing lambdas around.&lt;/p&gt;

&lt;p&gt;Suppose there is a database of posts that exposes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete-post&lt;/code&gt; generic
function. We can remove all the I/O operations from this library and plug it
externally.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;delete-post&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;db&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;post&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By defining a generic function library exposes not only one but multiple
extensions points which can be used without touching the library code.&lt;/p&gt;

&lt;p&gt;When generic function is defined developer also defines a way how dispatching
works. By default the most specific method runs, but you can use several others
like sum, aggregate or even define your own.&lt;/p&gt;

&lt;h3 id=&quot;clos&quot;&gt;CLOS&lt;/h3&gt;

&lt;p&gt;CLOS is an object system for common lisp that supports many features that make
it very advanced.&lt;/p&gt;

&lt;p&gt;Class in common lisp is a bag of slots. During definition developer defines
properties of every slot, so that it’s possible to define how slots are read or
initialized.&lt;/p&gt;

&lt;p&gt;Since functions are detached from objects, it makes it easier to have multiple
inheritance and CL supports that. Every getter on the slot is defined as a
generic function with implementation specific to this class but nothing prevents
to redefine getter/setter.&lt;/p&gt;

&lt;p&gt;One of use cases for multiple inheritance is when you need to use two
independent concepts. Let’s look at the following example that I stole from the
“Art of Metaobject Protocol” book.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defclass&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;rectangle&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initform&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:height&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reader&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initform&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:width&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:accessor&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defclass&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;color-mixin&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cyan&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initform&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:cyan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;magenta&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initform&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:magenta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;yellow&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initform&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:initarg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defclass&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;color-rectangle&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&amp;lt;color-mixin&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;lt;rectangle&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defgeneric&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;paint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have a generic function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;paint&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;color-rectangle&amp;gt;&lt;/code&gt; class that
inherits from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;color-mixin&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;rectangle&amp;gt;&lt;/code&gt;. We can implement function
itself against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;rectangle&amp;gt;&lt;/code&gt; class and it will, well, draw a rectangle. After
that we can implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:before&lt;/code&gt; method against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;color-mixin&amp;gt;&lt;/code&gt; that will setup
brush properties and maybe &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:after&lt;/code&gt; method to reset them. By doing that we
decouple two separate concerns and it’s trivial to implement any other shapes
with color support.&lt;/p&gt;

&lt;p&gt;CL went one step further and made all the definitions in common lisp as
instances of standard objects, which means that e.g. class definition is an
instance of the class “class definition” and can be extended. With that CL
provides multiple generic functions that are dispatched against standard objects
and it’s possible to create a subclass of standard objects and change the
behavior of the particular objects. As a result CLOS covers not only one
specific way to implement an object system (although default way is provided)
but a wide spectrum of possible implementations.&lt;/p&gt;

&lt;h3 id=&quot;macros&quot;&gt;Macros&lt;/h3&gt;

&lt;p&gt;To get the idea about possible use cases for macros let’s consider the following
example in some javascript like language.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Widget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* useful stuff */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;API&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;registerWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Is there any code duplication there? In javascript you’ll say that there isn’t
since all the lines are the necessary boilerplate to make things work. Let’s
imagine a perfect world and choose the best syntax for this case. Wouldn’t it be
nice to write something like this?&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;defWidget&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* useful stuff */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is a possible implementation in case of Common Lisp.&lt;/p&gt;

&lt;div class=&quot;language-common_lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmacro&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;define-widget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;rest&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;progn&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defclass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;expand-params&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmethod&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;widget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;register-widget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;symbol-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Easy.&lt;/p&gt;

&lt;p&gt;Macros provide a way to fight with code duplication and inefficient computation
on the whole new level. Since macros are expanded and evaluated on the compile
level, we get two advantages: we can offload intense computations on the
compilation step if possible of course and code duplication can be chased now not
only on the logic level but really on the syntax level.&lt;/p&gt;

&lt;p&gt;In addition to that CL provides a way to define reader macros so that you can
tune your program code to your liking. The big difference between lisp macro
system and say c macros is that lisp macros work with a source code as a code
data structure that is easy to manipulate with all the power language provides
and not as a simple substitute.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I hope I’ve made a showcase for CL and you’re interested to try it out if you
haven’t done it before. It’s not all roses and I have to give a word of warning there -
the situation with documentation is mediocre, even some popular libraries can have
problems there, there is plenty of outdated code or broken home pages. The language
itself has duplication in some parts like mapping functions or binding variables
and lacks some essentials like complete library to work with strings, community
is much smaller than in any hype language.&lt;/p&gt;

&lt;p&gt;On the other side there are libraries to compensate shortcomings of standard
library and many interesting projects in different areas, some of them are
really brilliant (see &lt;a href=&quot;https://github.com/froggey/Mezzano&quot;&gt;Mezzano&lt;/a&gt; as an
example).&lt;/p&gt;

&lt;p&gt;If you want to read about language in general, the best book ever written on CL
on my opinion is Peter Seibel’s “Practical Common Lisp”. It’s an invaluable gift
to humanity and what’s more it is freely available
&lt;a href=&quot;http://gigamonkeys.com/book/&quot;&gt;online&lt;/a&gt;.  Another piece of art on the topic is &lt;a href=&quot;https://mitpress.mit.edu/books/art-metaobject-protocol&quot;&gt;“The Art
of Metaobject Protocol”&lt;/a&gt;
which is very interesting to check out  to understand decisions behind the CLOS
design.  First part of the book is dedicated to the implementation of simplified
CLOS and is amazing. And as I’ve told in “Stability” chapter, the fact that the
book is from 1991 doesn’t mean it’s outdated :) There are other books like &lt;a href=&quot;http://www.paulgraham.com/onlisp.html&quot;&gt;“On
Lisp”&lt;/a&gt; from Paul Graham, but they can be
read on much later stage to get to the new levels of enlightenment.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://quickdocs.org&quot;&gt;Quickdocs&lt;/a&gt; website is a place to go if you want to find
some library.&lt;/p&gt;

&lt;p&gt;If you want to get help #lisp and #lispgames on freenode.net have the highest
concentration of lisp devs alive, lisp reddit is worth checking.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Building electron app</title>
    <link href="https://can3p.github.io//blog/2016/09/13/electron/"/>
    <updated>2016-09-13T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2016/09/13/electron</id>
    <content type="html">&lt;p&gt;I used &lt;a href=&quot;https://github.com/radiant-player/radiant-player-mac&quot;&gt;Radiant player&lt;/a&gt; for a long time
and didn’t have any complaints till the moment most of Google Music interface stopped working
well with the app. I checked bugtracker multiple times and no one raised similar issues. Feeling
myself weak in Objective C land I didn’t have any desire to go and fix things myself and, as
a consequence found myself opening GM app in the browser.&lt;/p&gt;

&lt;p&gt;For some reason I really don’t like having apps openned in the background tabs. First of all
it makes restarting browser a pain, second multimedia keys are not supported. So, after a while
I cloned electron quickstart repository and started launching google music there and few days
ago finally found the time to make a complete app - &lt;a href=&quot;https://github.com/can3p/perlotto&quot;&gt;Perlotto&lt;/a&gt;.
It’s called like this just because this word sounds amazing and I wanted to name something
with it.&lt;/p&gt;

&lt;p&gt;Overall setup took less time than I expected although I had solve few common issues, and solutions
will be bellow.&lt;/p&gt;

&lt;h2 id=&quot;packaging&quot;&gt;Packaging&lt;/h2&gt;

&lt;p&gt;Somce it’s a real app it should be packaged as a real app, I have no desire to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm start&lt;/code&gt;
every morning to start my music player. The task is really simple with &lt;a href=&quot;https://github.com/electron-userland/electron-builder&quot;&gt;electron-builder&lt;/a&gt;,
although it require a bit more effort to get app packaged automatically, since why would anyone
want to package app manuall all the time, right?&lt;/p&gt;

&lt;p&gt;Building part is explained in electron builder &lt;a href=&quot;https://github.com/electron-userland/electron-builder/blob/master/README.md&quot;&gt;readme&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is publishing part (I care only about Mac OS X at the moment). &lt;a href=&quot;https://travis-ci.org&quot;&gt;Travis CI&lt;/a&gt;
can be used to automate packaging and publish attefact on github releases page.&lt;/p&gt;

&lt;p&gt;My .travis.yml&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;osx_image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xcode7.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;osx&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;node_js&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;node_js&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;6.1.0&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;npm run dist&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;directories&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;node_modules&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$HOME/.electron&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;test/fixtures/app-executable-deps/app/node_modules&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;releases&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skip_cleanup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;file_glob&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dist/mac/Perlotto-*.dmg&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$GH_TOKEN&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;can3p/perlotto&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Few things there:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Specifying osx_image alone didn’t helpe to make travis run build on OS X, I had to add os section.&lt;/li&gt;
  &lt;li&gt;I specified version 6.1.0 for node, however something like 5.8.0 was used. Doesn’t really matter for me.&lt;/li&gt;
  &lt;li&gt;script is the same command that is used to build package locally&lt;/li&gt;
  &lt;li&gt;cache section is not necessary but helps to speed up process&lt;/li&gt;
  &lt;li&gt;Builder produces image with name like Perlotto-0.1.dmg, so I used file_glob to workaround specifying precise filename&lt;/li&gt;
  &lt;li&gt;api_key is a must however I did not want to put key on github, so I ended up specifying env variable and passing it there
(it’s possible to specify env vars on settings section for the project)&lt;/li&gt;
  &lt;li&gt;on section specifies that I want to make build for tags only, however travis still makes builds for every
commit. electron-builder is smart though and publishes artifacts only if it’s a real release&lt;/li&gt;
  &lt;li&gt;electron builder takes version from package.json file, but github cares about tags only,
so I have to keep them sync myself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;travis&lt;/code&gt; cli tool to generate some parts of the file and stole others from the internet,
however it’s absolutely possible to do everything without the tool via web interface.&lt;/p&gt;

&lt;p&gt;So, this is how release process looks now:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Update package.json version and push&lt;/li&gt;
  &lt;li&gt;Go to github releases page and publish new version (same as in package json).
One thing to keep in mind that electron-builder wants versions to be like x.y.z and tags like vx.y.z,
an exception is thrown in other cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;closing-the-window&quot;&gt;Closing the window&lt;/h2&gt;

&lt;p&gt;Default behaviour of electron quick start app is to exit application when user clicks close button.
I’m not aware of any serious player that behaves like this hence this misbehaviour has to be fixed.&lt;/p&gt;

&lt;p&gt;First thought is of course to prevent this on window close event, but the result is that it’s not
possible to close app in any way other process manager.&lt;/p&gt;

&lt;p&gt;So, trick there is to raise flag on app ‘before-quit’ event and check it on window ‘close’ event:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;forceQuiteApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Create the browser window.&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;mainWindow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;BrowserWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;800&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;mainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maximize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// and load the index.html of the app.&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;mainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;loadURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;file://&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;__dirname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/index.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;mainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forceQuiteApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;mainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;before-quit&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;forceQuiteApp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I run maximize method right after the start because I don’t want to worry about saving
window dimensions anywhere&lt;/p&gt;

&lt;h2 id=&quot;media-keys&quot;&gt;Media keys&lt;/h2&gt;

&lt;p&gt;This one is simple, the only moment is that keys should be bound with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;globalShortcut&lt;/code&gt; object
available from electron module on app thread and actual dom manipulation happens in content thread.
Here is relevant app code part:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ... init part ...&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;globalShortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MediaPlayPause&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;mainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;webContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;play-control&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;play-pause&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MediaPlayPause binding failed&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;globalShortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MediaPreviousTrack&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;mainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;webContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;play-control&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;rewind&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MediaPreviousTrack binding failed&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;globalShortcut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MediaNextTrack&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;mainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;webContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;play-control&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MediaNextTrack binding failed&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use event api to transfer knowledge from app to thread, here is content part:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ipc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;electron&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ipcRenderer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;ipc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;play-control&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;webView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;webview#gpm-player&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;webView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;executeJavaScript&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;document.querySelector('#player-bar-&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;').click()&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since script has to be embedded into content, local file is needed, hence here is index.html&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Perlotto&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;overflow: hidden&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;webview&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;gpm-player&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://play.google.com/music/&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;height:100%;width:100%;position:absolute;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/webview&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;./content.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Margins had to be reset, otherwise webview is rendered with some margins.&lt;/p&gt;

&lt;p&gt;Easy.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It’s funny, but this post contains 99% of project’s code. I treat project feature complete
since I don’t care about notifications, tray icons and any other garbage that is useful only
for distractions.&lt;/p&gt;

&lt;p&gt;Electron ecosystem is surprisingly feature complete and stable for me, thanks guys!&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>On Common lisp</title>
    <link href="https://can3p.github.io//blog/2016/07/02/common-lisp/"/>
    <updated>2016-07-02T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2016/07/02/common-lisp</id>
    <content type="html">&lt;p&gt;Though I have some experience with clojure which is lisp I always
had a feeling that this experience is incomplete without trying other
dialects, especially common lisp. This post describes my experience
on starting with this language.&lt;/p&gt;

&lt;p&gt;I’ve read a couple of books, namely &lt;a href=&quot;http://www.gigamonkeys.com/book/&quot;&gt;Practical Common
Lisp&lt;/a&gt;, &lt;a href=&quot;http://landoflisp.com/&quot;&gt;Land of lisp&lt;/a&gt;,
&lt;a href=&quot;https://leanpub.com/fullstacklisp&quot;&gt;Full stack lisp&lt;/a&gt; and &lt;a href=&quot;http://www.letoverlambda.com/&quot;&gt;Let over
lambda&lt;/a&gt; and they all are beautiful. Macros part
is especially mind blowing because you simply don’t think that way in most other
languages, and I still not used to them in a sense that I don’t write my own.&lt;/p&gt;

&lt;p&gt;After reading them I decided to give it a try and write something. The result
is my shiny new incomplete &lt;a href=&quot;https://github.com/can3p/cl-journal&quot;&gt;cl-journal client for Livejournal.com
webservice&lt;/a&gt;. I set the following
requirements for the client:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It should work with command-line&lt;/li&gt;
  &lt;li&gt;Password should be stored in Mac OS Keychain or any other system level
application, not locally&lt;/li&gt;
  &lt;li&gt;The idea was that it should work as a pre-commit hook to replicate experience
of static blog engines as much as possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While I found a solution for all these points, I was left with the feeling of
very fragmented toolchain and ecosystem. If we compare it to clojure world, all
project management and build tassk are done either with Leiningen or with boot,
and &lt;a href=&quot;clojuredocs.org&quot;&gt;clojuredocs.org&lt;/a&gt; is mostly enough to get all standard
library documentation and finally most libraries have decent looking
documentation pages. Every package has well-defined project file that contains
configuration, dependency information etc.&lt;/p&gt;

&lt;p&gt;It’s not like this in common lisp world (according to my limited knowledge).
Dependency management is done with the help of &lt;a href=&quot;https://www.quicklisp.org/beta/&quot;&gt;Quicklisp&lt;/a&gt;,
project scaffolding is done with
&lt;a href=&quot;http://www.xach.com/lisp/quickproject/&quot;&gt;Quickproject&lt;/a&gt;. Building is a whole
different story since common lisp doesn’t build standalone binaries in usual
sense, here the way is to dump current state of lisp process (including compiler
and documentation) and define an entry point and run it whenever this image is
executed. There are several projects that aim to simplify building and running
applications, most notably &lt;a href=&quot;https://github.com/roswell/roswell&quot;&gt;Rosewell&lt;/a&gt; and
&lt;a href=&quot;http://www.xach.com/lisp/buildapp/&quot;&gt;Buildapp&lt;/a&gt;. Rosewell looks really great, my
problem with it was that I haven’t found a way to install scripts from local
repository, so I had to do manual symlinks here and there to make it work.&lt;/p&gt;

&lt;p&gt;Since there is no central place for the documentation, I mainly used Practical
Common lisp book, &lt;a href=&quot;http://lispcookbook.github.io/cl-cookbook/&quot;&gt;common lisp cookbook&lt;/a&gt; and
&lt;a href=&quot;http://quickdocs.org&quot;&gt;quickdocs website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of other unsolved issued for me was password prompt. I didn’t find a proper
nonhackish way to do it with common lisp itself, so I had to run &lt;a href=&quot;https://github.com/can3p/cl-journal/blob/master/client.lisp#L13&quot;&gt;external
command&lt;/a&gt; for exactly this purpose.&lt;/p&gt;

&lt;p&gt;For running commands I had to use
&lt;a href=&quot;https://gitlab.common-lisp.net/asdf/asdf/tree/master/uiop&quot;&gt;uiop&lt;/a&gt; library,
especially
&lt;a href=&quot;https://gitlab.common-lisp.net/asdf/asdf/blob/master/uiop/run-program.lisp&quot;&gt;uiop/run-program&lt;/a&gt;
and &lt;a href=&quot;https://gitlab.common-lisp.net/asdf/asdf/blob/master/uiop/os.lisp&quot;&gt;uiop/os&lt;/a&gt;
packages. Package there is more like a namespace  in clojure and clojure package
is a system in common lisp terms. uiop/os namespace was especially useful
because standard library doesn’t provide any functions to control current
working directory.&lt;/p&gt;

&lt;p&gt;Most of things described can be blockers only with first steps in the language
and I believe that once all they fit in head programming in common lisp gets not
harder than in any other language but with additional cool features.&lt;/p&gt;

&lt;p&gt;The coolest one is of course an interactive development. Basically I’ve started
repl just once or twice during development, all the features were written and
tested in it and that had huge consequences for development process. If we take
any usual language, you work there by continuously running the whole program or
firing one-liners to get result from desired function. The drawback of former is
that if initialization step is costly, your development speed can suffer from it
a lot. The drawback of latter is that most of function cannot work in isolation - they
need specific datastructures initialization of global data object etc, and if
it’s not necessary, developer still has to take care of proper printing of the
results. So, sometimes it’s just to much of work to run things in this way, so
people revert to the first way. First way also suffers from the fact that instead of
implementing business logic developer spends a lot of time on boilerplate code
for passing parameters, entry functions etc. Interactive development eliminates
all that hassle.&lt;/p&gt;

&lt;p&gt;My client had to load local database with information of
already published files before doing anything. Instead of loading it on every
change, I did it once and then simply tested new functions that I wrote with it.
And since I could do it, I did not implement boilerplate code till the very last
stage when I already had working business logic in place.&lt;/p&gt;

&lt;p&gt;Interactive approach is very different also in sence that bottom-up approach
feels really natural there. E.g. In order to create a post I need to read a
file. How do I do that? (= new function). After I read, how do I parse it (= new
function). After I parse it ho do I understand that it’s not a draft (= new
function predicate) etc etc. So I continued asking questions and putting my
answers as functions there till I get to very top where I wrote simple function
that really created the post.&lt;/p&gt;

&lt;p&gt;As a result most functions are really small and
easy to reason about. You can go bottom-up in other programming languages, but
since you either have to write a lot of boilerplate code to get feedback or
right a lot of tests which really slow you down when you’re just trying to find
a way to solve the problem.&lt;/p&gt;

&lt;p&gt;If you ever did any clojure programming you know that it provides similar level
of experience. Where common lisp wins is a startup time for a built program
(instant) and human readable stacktraces that are not polluted with java classes
etc and excellent restart functionality instead of simple exceptions. Restarts
are super cool, because they don’t just throw warnings, they also provide
options to recover and sometimes error can be just worked around with this.&lt;/p&gt;

&lt;p&gt;As a resume - language is definitely worth looking at and using for real things.
I have very limited exposure to the language to talk about good parts and bad
parts, but a scattered and some times very brief documentation adds to a
challenge of learning the language. Many libraries that are widely used have
there latest release a few years in the past, and documentation is not always
nice (e.g. I had to read the code to understand how to use uiop packages
properly. The whole ecosystem has a few active members which means that not much
happens in a public space.&lt;/p&gt;

&lt;p&gt;However there is a suprisingly big number of common
lisp implementations that are really high-performant and battle tested. I do
think that if language gets a proponent like Yehuda Katz, who will put effort
into popularisation of the language and systematic toolchain enhancements,
common lisp can get a great level of adoption.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Perl is nice</title>
    <link href="https://can3p.github.io//blog/2016/04/25/perl-is-nice/"/>
    <updated>2016-04-25T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2016/04/25/perl-is-nice</id>
    <content type="html">&lt;p&gt;There are many trendy things in wht wild and perl is definitely not one of them.
Many blame it to be write-only, and it really has some &lt;a href=&quot;http://stackoverflow.com/questions/35227290/determining-if-a-perl-scalar-is-a-string-or-integer&quot;&gt;ugly parts&lt;/a&gt;,
there are things where it definitely shines. There are lots of parts that make
data juggling easy.&lt;/p&gt;

&lt;h2 id=&quot;lists&quot;&gt;Lists&lt;/h2&gt;

&lt;p&gt;My personal favourite is a list concept of the language. By default all lists are squashed
to one level and every function receives nothing but a list of arguments. All hashes are lists
with odd values used as keys and even values used as values. And in the end,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&amp;gt;&lt;/code&gt; operator used in hash definition is absolutely equal to comma operator.&lt;/p&gt;

&lt;p&gt;If you want to get a nested structure you should use references.&lt;/p&gt;

&lt;p&gt;This is absolutely awesome concept that makes so many operations compact and natural that
other languages like javascript should be really jelaous about it, because it allows to get
things like destructuring trivially and makes conversions between lists and hash-map natural.&lt;/p&gt;

&lt;p&gt;First of all, there is no nesting:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;# equivalent&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s not only about raw lists, it’s about lists as a type, so all this is the same:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@arr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yeah, any expressions can do&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$all_good&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since hash map is initialized with the same lists (it’s stored as a hash map structure actually), all tricks still apply, the only restriction is
that number of list items is even.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;some_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;some_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;get_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All this allows to have idiomatic way to optionaly set key to hash:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;required_option&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$condition_holds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;optional_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or use map to transform list to a hash.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What happens is that we take a list and use map on it, where block is executed with every
element and returns a list &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;($item_of_list, 1)&lt;/code&gt;. In the end all returned lists are squashed
to a single one which is used to initialize a hash. As a result we have a hash of all members
of a list as keys and ones as values.&lt;/p&gt;

&lt;p&gt;It’s particularly cool comparing to javascript where there is no way to achieve the same level
of elegance:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// We can do like this:&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// or like this:&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;has&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For optional keys it’s the same level of awkwardness:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;condition_holds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While not a hard thing to do, it pollutes code with unnecessary variable declarations or loops
that do very simple things and distract attention for the main logic.&lt;/p&gt;

&lt;p&gt;Now, all this applies to any function calls, because what we pass is just a list of arguments:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# the same&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Inside function we can parse arguments in any way we want:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# one argument and the rest as a list&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# we can call this like func(1, key =&amp;gt; value) for example&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# one argument and the rest as a hash&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you see, we get simple form of destructuring easily with a natural
uniform syntax. Perl obsession with list went even further. E.g. you can
set multiple keys of the hash at the same time:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;@hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;qw(key1 key2)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you see, this pattern allows to cut out a lot of boilerplate code and
provides really effective tools for data structure manipulation.&lt;/p&gt;

&lt;h2 id=&quot;default-variables&quot;&gt;Default variables&lt;/h2&gt;

&lt;p&gt;This is one of the features the brought perl a fame of write-only language. The reason is that
It’s used for most language constructs and can make the code really dense and almost unpenetrable
to anyone not so similar with perl.&lt;/p&gt;

&lt;p&gt;I’m not aware of any other language that has default variables but with a wise use code actually looks
cleaner. There are two main variables that are used as default ones - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$_&lt;/code&gt;  and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@_&lt;/code&gt;, which are
scalar value (number, string, reference) and list.&lt;/p&gt;

&lt;p&gt;All function arguments in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@_&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;sub &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$arg1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$arg2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All loops and operators like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$_&lt;/code&gt; point to current item of a list&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@doubles&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you write this type of code in any language that does not have this feature you’ll most probably end up with lots
of variables named as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;item&lt;/code&gt; or something alike.&lt;/p&gt;

&lt;p&gt;What’s more some functions and operators work with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$_&lt;/code&gt; if no explicit argument was provided, and here is the place
when unfamiliar people can get really confused.&lt;/p&gt;

&lt;div class=&quot;language-perl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;chomp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;sr&quot;&gt;s/abc/def/g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# the same&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;chomp&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;s/abc/def/g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Looking at this example you may see that perl really doesn’t mind mutating existing variables. This again can save some keystrokes
but makes it even easier to shoot yourself in a foot.&lt;/p&gt;

&lt;p&gt;These are two features unique to perl as a language that allow to do lot’s of tricks that make code dense and expressive at the cost
of additional context that any developer should be aware of to successfully read the code. I don’t think that it’s a bad thing by
itself but most language developers chose different path to provide everything explicitly so decrease learning curve.&lt;/p&gt;

&lt;p&gt;Big thanks to Oleg Komarov for reviewing this post.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Free data</title>
    <link href="https://can3p.github.io//blog/2016/03/23/free-data/"/>
    <updated>2016-03-23T00:00:00+01:00</updated>
    <id>https://can3p.github.io/blog/2016/03/23/free-data</id>
    <content type="html">&lt;p&gt;Data rules the world, right? Vast amount of information can be used not only to
extracts some insights from it, it can be a weapon or a timebomb depending on
the circumstances. Bad news for ordinary people is that they rarely have access
or own information, they mostly ask for it. What if one refuses to answer?&lt;/p&gt;

&lt;p&gt;Some years ago it was possible to buy it at least, but in the age of ubiquitous
web services everything lives somewhere else and we have no way to control it.
Services that we use everyday belong to very few major players, and it’s hard
to imagine a scale of the disaster if google goes offline or is forced to stop
providing services to the users of the country that doesn’t fit to one’s
understanding of real democracy.&lt;/p&gt;

&lt;p&gt;A lot of web sites heavily rely on crowd sourced data, but the only way users
can access it is the way provided by this services. Is it fare?&lt;/p&gt;

&lt;p&gt;I’d say not. I firmly believe that future innovations heavily rely on free
access to different kind of information.&lt;/p&gt;

&lt;p&gt;If you remember there was a period in mid 2000s, so called Web 2.0 where the
trend was to create open APIs and give away as much information as possible to
enable interconnection of the web. After some time this trend turned into
decline, but we really need to get it back.&lt;/p&gt;

&lt;p&gt;Every service that takes data from users should make it possible to get it
back.  If the data is anonymous then it should be possible to get it all. Just
have a look on the amount of applications that use wikipedia data as a source.
Another example is OpenStreetMap project.&lt;/p&gt;

&lt;p&gt;Knowledge and Navigation is two pieces, communication is the third one. We
really nead open dictionary and translation engine the will prevent dependency
on single player and allow everyone to benefit.&lt;/p&gt;

&lt;p&gt;Two more field that come into mind are history and repairs. Talking about the
latter, how many times anyone got into situation the the devices he or she had
had only one detail broken and there was no way to fix it because it was no
longer supported/produced?  It shouldn’t be that way, especially with emerging
3d-printing technologies. We need and open service that will contain models for
parts of the devices that could be printed.  I can imagine the future where
every such repair is just a matter of downloading proper model and sending it
to a printer.&lt;/p&gt;

&lt;p&gt;And at last, what can we do with the history? The problem with it is that due to
amount of information and globalization it’s way to easy to hide important changes
behind silly news. How nice it would be to have an open database that will accumulate
information about all events happening, so that it will be possible to make
an analysis and separate real news from the garbage and see real connections between
events that would otherwise go unnoticed.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Browser will fix this</title>
    <link href="https://can3p.github.io//blog/2016/01/28/markup-bugs/"/>
    <updated>2016-01-28T00:00:00+01:00</updated>
    <id>https://can3p.github.io/blog/2016/01/28/markup-bugs</id>
    <content type="html">&lt;p&gt;After working many years in the frontend development area
I really got used to technology which led to some kind of
blindness to the errors that can happen, I don’t see certain
kind of errors anymore just because I got the habit of doing
things certain way and it doesnt (usually) get me into trouble.&lt;/p&gt;

&lt;p&gt;When you start writing markup you’re all about semantics and
compact code. Isn’t it beautiful?&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;My blog&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Great post!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;article&amp;gt;&lt;/span&gt;My awesome content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
...

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yeah, it is. And maybe it even makes sence if you’re working on the site
alone, or you’re planning to ship it to the client and run away because this
kind of markup implies selectors like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;section header h2 {}&lt;/code&gt;. And while
it looks nice and concise, things start breaking after you develop more than
one page with shared styles and a product owner who always wants to see this
block a little bit different.&lt;/p&gt;

&lt;p&gt;The bigger project grows the more effort is required just to keep semantics
on a proper level, which of course affects the velocity. After few more years
you start to understand that the only difference about html tags is that some
of them are inline and others are block-level. No, actually there are just
elements and there is css that can make anything you like. If I were to write
the same markup today, most probably I would come up with something like this:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;header&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;header__title&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;My blog&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post-head&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post-head__title&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Great post!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post-content&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;My awesome content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
...

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unique classnames, selectors with the lowest specificity possible. Doesn’t look
beautiful, but I know for sure, that I won’t get into troubles with selector clashes,
my css won’t consist on 50% of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!important&lt;/code&gt; rules and since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;span&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt;
element don’t get any default styles from the browser they usually have an overwhelming
majority on the page.&lt;/p&gt;

&lt;p&gt;The second benefit is that I can always move blocks around without breaking too much
stuff, nest blocks into each other etc. Nesting part is a tricky one, because browser
doesn’t really think that elements are the same. Here is the code:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;content-head&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    We start good
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;reusable&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Block&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    Ouch
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Apart from the fact that div looks odd inside of the span, what can go wrong?
Here is what browser thinks (&lt;a href=&quot;http://codepen.io/can3p/pen/RryEZO&quot;&gt;codepen example&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/img/2016-01-28-markup-bugs/nested_tags.png&quot; alt=&quot;ouch&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Yeah, semantics.&lt;/p&gt;

&lt;p&gt;The other nesting pitfall that is possible to get into is with forms(&lt;a href=&quot;http://codepen.io/can3p/pen/OMZrQP&quot;&gt;codepen&lt;/a&gt;):&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;form1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/post&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;i-dont-know-how-i-got there&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/get&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;login&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/public/img/2016-01-28-markup-bugs/nested_forms.png&quot; alt=&quot;omg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The last one is my personal favourite, because the misbehaviour
can be introduced a loong time before you will introduce the changes
that actually trigger the bug:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;form1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/post&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;login&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;password&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;spacer&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Mind the closing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;/div&amp;gt;&lt;/code&gt; in the middle of the form. It can really happen
if you have a huge project with lots of nested templates. Here is inspector view:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/img/2016-01-28-markup-bugs/form_div_intersect.png&quot; alt=&quot;oh no&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You see, last input field falls out of the form. The browser magic is in the fact
that it still belongs to form (check &lt;a href=&quot;http://codepen.io/can3p/pen/NxMeBr&quot;&gt;another codepen&lt;/a&gt;)!&lt;/p&gt;

&lt;p&gt;The bug becomes visible if you insert another input dynamically, like this:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#spacer&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;input id=&quot;out&quot; name=&quot;out&quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This input won’t belong to the form at all (&lt;a href=&quot;http://codepen.io/can3p/pen/OMZray&quot;&gt;codepen&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;If you think about it, all this absolutely makes sence, because javascript works with
already fixed DOM tree and the element is inserted outside of the form, but it can take
quite some time to spot this in a huge codebase.&lt;/p&gt;

&lt;p&gt;Forgot to mension, ids are here only for test purposes and ideally should be used only
in situations where html spec wants them (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;label for=&quot;&quot; /&amp;gt;&lt;/code&gt;).&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Advent of code quiz reflections</title>
    <link href="https://can3p.github.io//blog/2016/01/05/advent-of-code/"/>
    <updated>2016-01-05T00:00:00+01:00</updated>
    <id>https://can3p.github.io/blog/2016/01/05/advent-of-code</id>
    <content type="html">&lt;p&gt;This December a big chunk of my free time was spent on
solving quize from &lt;a href=&quot;http://adventofcode.com/&quot;&gt;Advent of code&lt;/a&gt;
challenge. While I wasn’t anywhere amongst the leaders
it was a pure joy to see how clojure helps to gradually
build solutions without constant switching between the editor
and console.&lt;/p&gt;

&lt;p&gt;If the only thing you have is a hammer then everything looks
like a nail and majority of tasks ended as some sort of list
generation which is somehow reduced to the to the answer value.
If you look at the task from this point of view you will discover
that it really helps to avoid most of side effects or temporary states
which can be a big source of bugs.&lt;/p&gt;

&lt;p&gt;If you think about everything as list it becomes immediately evident
how rich and cool core library is. Apart from it I used only
&lt;a href=&quot;http://clojuredocs.org/clojure.string&quot;&gt;clojure.string&lt;/a&gt;,
&lt;a href=&quot;http://clojuredocs.org/clojure.set&quot;&gt;clojure.set&lt;/a&gt; and
&lt;a href=&quot;https://github.com/clojure/math.combinatorics&quot;&gt;math.combinatorics&lt;/a&gt; namespaces.&lt;/p&gt;

&lt;p&gt;The main disadvantage for me was that with certain number of
map/reduce/filter functions in one expression code becomes much
harder to read for anyone who is not very familiar with them
and sometimes it’s evident that in some cases types can really help
because it’s hard to say how structure looks like at given point
of transformation. May record is 9 map/reduce calls in one function.&lt;/p&gt;

&lt;p&gt;The other pain point is emacs cider and changing project dependencies.
Maybe I don’t read documentation correctly but the only good way to
load new dependencies is to restart repl and to do that I had to kill
it. Sad news is that after that in most cases I ended up with restarting
emacs which meant losing undo history and repl logs.&lt;/p&gt;

&lt;p&gt;Ok, back to interesting stuff. I want to share a few incredibly useful
functions from the core library that I discovered while doing the challenge.
Although simple sometimes they drastically simplify the code.&lt;/p&gt;

&lt;h3 id=&quot;reductions&quot;&gt;&lt;a href=&quot;http://clojuredocs.org/clojure.core/reductions&quot;&gt;reductions&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Used in &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day1.clj&quot;&gt;day 1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This function takes the same arguments as reduce but instead of final
value returns a sequence of all intermideary reduction states. It’s
useful if every reduction step has it’s own meaning, e.g. distinct
state that you want to analyze. Or if you have an infinite sequence
you can use reductions to count the steps to get to the desired state.&lt;/p&gt;

&lt;h3 id=&quot;every-pred&quot;&gt;&lt;a href=&quot;http://clojuredocs.org/clojure.core/every-pred&quot;&gt;every-pred&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Used in &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day5.clj&quot;&gt;day 5&lt;/a&gt; and &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/e2b130ee64d9a638554238414dc209a3ce8081b6/src/advent_of_code_2015/day11.clj&quot;&gt;day 11&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Takes any number of functions and returns new function that will return
true only if all the functions return true with arguments passed. Very useful
to write conditions in compact way:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ans1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;every-pred&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has-wovels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has-double&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has-good&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;str/split&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;partition&quot;&gt;&lt;a href=&quot;http://clojuredocs.org/clojure.core/partition&quot;&gt;partition&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Used in &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day5.clj&quot;&gt;day 5&lt;/a&gt;, &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day11.clj&quot;&gt;day 11&lt;/a&gt; and &lt;a href=&quot;[day 5](https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day13.clj)&quot;&gt;day 13&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Function allows to get a sequence of list every of which is a
subsequence from original of some width and with some offset from
the begining. In my case it was very useful to check any connections
between sequence items.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chmap&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hash-map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;char-range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;\a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;\z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;\a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This expression generates a hashmap with keys that are individual characters
and the values that are the charecters that alphabetically follow them, except
a follows z. partition takes (\a \b .. \z \a) list as input and returns
sequence like ((\a \b) (\b \c) … (\y \z) (\z \a)). After that I transform
this sequence to the flat list and feed it to hash-map function.&lt;/p&gt;

&lt;h3 id=&quot;group-by&quot;&gt;&lt;a href=&quot;http://clojuredocs.org/clojure.core/group-by&quot;&gt;group-by&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Used in &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day12.clj&quot;&gt;day 12&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Groups sequence according to return values of predicate.&lt;/p&gt;

&lt;h3 id=&quot;iterate&quot;&gt;&lt;a href=&quot;http://clojuredocs.org/clojure.core/iterate&quot;&gt;iterate&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Used in &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day18.clj&quot;&gt;day 18&lt;/a&gt; , &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day17.clj&quot;&gt;day 17&lt;/a&gt; and &lt;a href=&quot;[day 5](https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day25.clj)&quot;&gt;day 25&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Function takes value and another function and returns a sequence that starts from
the value and continues with applying function to previous sequence item to generate
next one. Helps to avoid loops in all situations where you need to build next value
on top of current one.&lt;/p&gt;

&lt;h3 id=&quot;re-seq&quot;&gt;&lt;a href=&quot;http://clojuredocs.org/clojure.core/re-seq&quot;&gt;re-seq&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Used in &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day19.clj&quot;&gt;day 19&lt;/a&gt;, &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day21.clj&quot;&gt;day 21&lt;/a&gt;, &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day22.clj&quot;&gt;day 22&lt;/a&gt;, &lt;a href=&quot;https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day23.clj&quot;&gt;day 23&lt;/a&gt; and &lt;a href=&quot;[day 5](https://github.com/can3p/adventofcode2015/blob/master/src/advent_of_code_2015/day25.clj)&quot;&gt;day 25&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Works like re-find but returns a lazy sequence of all consequtive matches.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Optimizing code for V8</title>
    <link href="https://can3p.github.io//blog/2015/11/17/v8-optimization/"/>
    <updated>2015-11-17T00:00:00+01:00</updated>
    <id>https://can3p.github.io/blog/2015/11/17/v8-optimization</id>
    <content type="html">&lt;p&gt;One of the things that I love to do the most is to take a complex system and
start digging through it to discover some subtle details that reveal how the
system really works. This writeup is about one of recent tasks where I wanted to
understand how to accelerate execution of some arbitary code for node. I coudn’t
come up with better algorythm to solve the task but suspected that the code
itself could have been written in a better, more efficient way from
interpreter’s point of view.&lt;/p&gt;

&lt;p&gt;So, first I decided that I wanted to measure how fast the code was to be able to
understand if I do any progress at all. console.time api is a very good way to
do it:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;mycode&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// ... code to be measured&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;mycode&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this I can see if I do any better with the changes I introduce, the only
thing is that execution time is not deterministic and it always makes sense to
do multiple measurements and work with average. The other thing that can affect
the measurements is the way they are done. For example, V8 engine really
consists of two compilers - the former of them compiles source code to intermediate
code that can be executed, and the latter works with the code that is considered
to be &lt;em&gt;hot&lt;/em&gt; (executed many times through the program run) and compiles it to the
machine code. This has it’s own implications, e.g. people like to do benchmarks
like this:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;c1&quot;&gt;// ok, let's test the perf of implementation&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;superfunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you see, the average execution time of this function can be affected by V8
implementation details. The other thing is that not all code can be optimized
and it may really matter how you iterate on arrays and objects what language
features are used and if V8 cannot optimize the code or cannot successfully
guess types of variables the code will suffer from additional performance penalty.&lt;/p&gt;

&lt;p&gt;After all this, how can one gather the knowledge of such optimizations? A very
good source on this can be found
&lt;a href=&quot;https://github.com/petkaantonov/bluebird/wiki/Optimization-killers&quot;&gt;here&lt;/a&gt; or in
the awesome &lt;a href=&quot;http://mrale.ph/&quot;&gt;blog&lt;/a&gt; by Vyacheslav Egorov.&lt;/p&gt;

&lt;p&gt;Additionally, profiling is always helpful and it is possible for node with help
of &lt;a href=&quot;https://github.com/node-inspector/v8-profiler&quot;&gt;v8-profiler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I started my investigation from the profiler results and saw a signigicant time
taken by garbage collector (50ms out of ~600) and a lot of anonymous functions.
The latter were useless and I went through all anonymous functions in the code
and added names to them to get meaningfull profiler report.&lt;/p&gt;

&lt;p&gt;After some search I’ve figured out that the reason of the former was the use of
array map and reduce methods. When I replaced that with ordinary loop garbage
collector time reduced to 5ms on average.&lt;/p&gt;

&lt;p&gt;Another speedup was with big object that came to be processed. On initial
implementation I created another and populated with the results. Two things
helped - reusing existing object and overwriting key values and using loop over
Object.keys() array instead of for..in loop.&lt;/p&gt;

&lt;p&gt;Other discovery was that function .bind method is an optimization killer and
once I replaced &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_reg_exp.test.bind(my_reg_exp)&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function(a) { return
my_reg_exp.test(a); }&lt;/code&gt; in a code that was executed lot’s of times I immediately
got big speedup.&lt;/p&gt;

&lt;p&gt;Yet another speedup with iteration over big array came when I replaced a call to
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object.keys()&lt;/code&gt; method with ‘Object.getOwnPropertyNames()`. This method also
gives keys of the object but does not check the prototype and this is probably a
reason of speedup.&lt;/p&gt;

&lt;p&gt;After all these changes I got 5x-6x acceleration in the execution time without
changing the algorythm or and data structures.&lt;/p&gt;

&lt;p&gt;As a conclusion: I wouldn’t to such optimizations on the code the is executed
only once throught the lifetime of the program or operates only on small amounts
of data but the fact the node can have such a drastic difference in run times
depending on such subtle differences really forces to keep this in mind when
dealing with the code that has strict performance requirements.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>FPV drone building experience</title>
    <link href="https://can3p.github.io//blog/2015/11/07/drone/"/>
    <updated>2015-11-07T00:00:00+01:00</updated>
    <id>https://can3p.github.io/blog/2015/11/07/drone</id>
    <content type="html">&lt;p&gt;This summer I saw some videos with FPV races that were so awesome that I decided
that I want build my own drone for racing.&lt;/p&gt;

&lt;p&gt;The least straightforward part was to actually select parts to build from. If
you have no experience in hobby electronics you will be amazed by amount of
what’s available on the market. There are all sorts of different frames, motors,
distribution boards etc. And if you start choosing something while you’re still
not sure about essential list of parts you’ll encounter a whole lot of
additional options: what about led lights? If so, what kind of lights,
whaterproof or not? What about onboard HD camera? And so on. All this questions
take time to check compatability, voltage, connectors and other options
available.&lt;/p&gt;

&lt;p&gt;After a while I understood that the best way might be to go with bare minimum
required to make drone that flies and iterate on it. There is &lt;a href=&quot;https://www.youtube.com/watch?v=8jbpwqCCVbs&quot;&gt;awesome
series&lt;/a&gt; of tutorial videos on the
topic that cover all building process with just a few parts that you might want
to do differently and it was vital for the success of my build.&lt;/p&gt;

&lt;p&gt;Now, here is the list of the details that I used. I’ve split them in a sections
with bare minimum on top and additional options below.&lt;/p&gt;

&lt;h2 id=&quot;parts-list&quot;&gt;Parts list&lt;/h2&gt;

&lt;p&gt;Required parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.banggood.com/H250-ZMR250-250mm-Carbon-Fiber-Mini-Quadcopter-Multicopter-Frame-Kit-p-933185.html&quot;&gt;H250 ZMR250 250mm Carbon Fiber&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.banggood.com/Distribution-Board-ESC-Hub-For-QAV250-KIM250-250Pro-Frame-p-948634.html&quot;&gt;Distribution
board&lt;/a&gt;
Any board will do, this was not the best one. Actually, there are
&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=23140&quot;&gt;options&lt;/a&gt;
that allow make everything without soldering at all, because they already have
XT60 male plug soldered in and bullet plugs for ESCs.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.banggood.com/XT60-Male-Plug-10AWG-10cm-With-Wire-p-77537.html&quot;&gt;XT60 Male Plug
12AWG&lt;/a&gt; In case you bought a distribution board that requires soldering&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.banggood.com/Wholesale-1X-20cm-Tie-Down-Strap-For-11_1-3S-2200-LiPo-Battery-AKKU-TREX-450-RC-helicopter-p-40249.html&quot;&gt;Tie Down Strap for 11.1 3S 2200
Battery&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.banggood.com/FlySky-FS-i6-2_4G-6CH-AFHDS-RC-Transmitter-With-FS-iA6-Receiver-p-922606.html&quot;&gt;FlySky FS-i6 2.4G 6CH AFHDS RC Transmitter With FS-iA6
Receiver&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.banggood.com/120pcs-M3-Nylon-Hex-Spacers-Screw-Nut-Assortment-Stand-off-Accessories-Kit-Set-p-987434.html&quot;&gt;120pcs M3 Nylon Hex Spacers Screw Nut Assortment Stand off Accessories Kit Set&lt;/a&gt; you need just a few of those, entire set is overkill.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=68813&quot;&gt;AfroFlight Naze32 Acro AbuseMark FunFly Controller - Soldered version&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=61433&quot;&gt;DYS BE1806-13 Brushless Motor for Multirotor (2300KV) 24g&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=55241&quot;&gt;Afro ESC 12Amp Ultra Lite Multi-rotor Motor Speed Controller (SimonK Firmware) Version 3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=11409&quot;&gt;HK-171 Thread Locker &amp;amp; Sealant Medium Strength&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=9374&quot;&gt;Polyester Velcro Peel-n-stick adhesive side V-STRONG&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FPV gear. If you want to start from bare minimum this is totally not required.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=66474&quot;&gt;Skyzone Plug &amp;amp; Play FPV 500-Set With TS58500 TX, RC832 RX, Sony 700CCD and C/P Antennas&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=53706&quot;&gt;Quanum DIY FPV Goggle Set with Monitor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expendables:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=8934&quot;&gt;Turnigy 2200mAh 3S 25c&lt;/a&gt; I bought this one, but it’s rather heavy and maybe an overkill for the start&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=22410&quot;&gt;HobbyKing® B3AC Compact Charger&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.banggood.com/Gemfan-5x3-Inch-Plastic-5030-Propeller-CWCCW-For-240-250-Frame-p-937866.html&quot;&gt;Gemfan 5x3 5030 Propeller 250 Frame&lt;/a&gt;
Probably you might want to buy lots of these&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Misc stuff:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hobbyking.com/hobbyking/store/uh_viewItem.asp?idProduct=50287&quot;&gt;HobbyKing™ Universal Propeller Balancer, For T Style and Std Propellers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.banggood.com/FlySky-Data-Cable-USB-Download-Line-For-FS-i6-FS-T6-Transmitter-Firmware-Update-p-982289.html&quot;&gt;FlySky Data Cable USB Download Line For FS-i6 FS-T6 Transmitter Firmware
Update&lt;/a&gt; The cable should enable you to use the transmitter for firmware update and for using it with simulators.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;a-few-notes-about-how-i-chose-parts&quot;&gt;A few notes about how I chose parts.&lt;/h2&gt;

&lt;p&gt;The ideal way was to go throught every part
that might be necessary and look for best options, but after a week of long
evening spent by googling stuff and reading forums I decided that this strategy
wil not lead me anywhere. So, checked several tutorials and selected the most
popular cheap frame and combination of ESCs and motors that sort of made sense.
I didn’t do any calculations about the load these motors can lift, so it was a
total guess.&lt;/p&gt;

&lt;p&gt;FPV gear was chosen because it was a) cheap and b) literally plug n play. Maybe
Quanum DIY FPV goggleset will not give the best video ever but it does the job
and you can really forget about FPV parts or order them later if you’re just
making your first training flights.&lt;/p&gt;

&lt;p&gt;Velcro tape is super useful to attach anything to anything. Accumulators,
cameras - everything that should be detachable will benefit from it, because it
is really sticky and connects parts well.&lt;/p&gt;

&lt;p&gt;Propeller. There are different modifications of propellers but 5x3 should be the
mildest option.&lt;/p&gt;

&lt;p&gt;Accumulators. If you’re without FPV gear two accumulators should be more than
enough for you and their capacity can be smaller and hence save space. And one
charger is enough! If you’ll take the same transmitter like I did you can try to
adopt one more accumulator to be inside of it, you can check tutorials on the
net.&lt;/p&gt;

&lt;h2 id=&quot;assembling-process&quot;&gt;Assembling process&lt;/h2&gt;

&lt;p&gt;The assembling process was more or less straightforward and you can follow video
tutorial I mentioned earlier. For me the main difference with tutorial was usage
of power distribution board and actual setup of the controller. None of them are
really hard especially if you’re comfortable with soldering.&lt;/p&gt;

&lt;p&gt;For flight controller I used Cleanflight firmware and it’s easy enough to setup.&lt;/p&gt;

&lt;h2 id=&quot;few-difficulties&quot;&gt;Few difficulties.&lt;/h2&gt;

&lt;p&gt;Radio reciever requires input voltage of 5V and I didn’t have a 2 cell
accumulator. The solution was to actually plug naze32 board and use it as power
source.&lt;/p&gt;

&lt;p&gt;The other issue was with receiver was that when I started plugging wires between
receiver and controller I couldn’t make it right before I stopped makeing the
assumptions that channels on the receiver match channels on controller. In my
cases channels like 5-6 were mixed.&lt;/p&gt;

&lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/public/img/drone.jpg&quot; alt=&quot;drone&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It was really fun and I’m eager to start studying how to fly drone better and
there is also a lot of autopilot staff that I want to adress.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>clj-carbonapi internals</title>
    <link href="https://can3p.github.io//blog/2015/10/28/clj-carbonapi/"/>
    <updated>2015-10-28T00:00:00+01:00</updated>
    <id>https://can3p.github.io/blog/2015/10/28/clj-carbonapi</id>
    <content type="html">&lt;p&gt;A while ago I decided to implement something close to the real world in clojure.
The easiest way to do that was to take some existing non-toy project and try
to replicate it. My choice was to reimplement parts of
&lt;a href=&quot;https://github.com/dgryski/carbonapi&quot;&gt;carbonapi&lt;/a&gt; and the result is called
&lt;a href=&quot;https://github.com/can3p/clj-carbonapi&quot;&gt;clj-carbonapi&lt;/a&gt;. The overall experience
was so good that I decided to write a blog post with finding and pitfalls
encountered during the implementation.&lt;/p&gt;

&lt;p&gt;The first and the biggest finding is that REPL provides absolutely life
changing experience and any other development process feels like a bleak shadow
regarding the speed of prototyping and development especially if we talk about
nontrivial code. For me the truth is that for complex tasks I sometimes don’t
have any idea about how implementation should look like, what data structures
should be used, what is the correct way to use new library etc. and combination
of decent editor and a repl (Emacs + Cider in my case) allows to start writing
some code that can be immediately tested in the repl and start building complex
solution from the small pieces without a need to implement boilerplate code
that should be written in any other project just to start doing something. In
my case I started from implementing parser, then moved to the fetching part,
added functions support after that and finally implemented graphs, server and
cli interface.&lt;/p&gt;

&lt;p&gt;As you can see, usual points that sound like first steps in most
languages, just because you you need them to start seeing any results are not
really necessary in clojure. When I was working on any of parts I was able to
quickly iterate on the results and make things work piece by piece from small
to big until I get everything right.&lt;/p&gt;

&lt;p&gt;Having said all that I want to admit that clojure repl is not a replacement of
unit tests, it’s just super cool prototyping tool. The pro of it is that you can
test things continuously without any additional code to support and anyone who
wrote anything with TDD techique knows that units test take a lot of time to
support especially when you aim for full coverage. From the other hand repl is
not a code that you can save in the repo to rerun later and tests are still
needed after implementation is ready because at this moment it’s important to be
sure that things will keep working as expected. In my case I wrote
&lt;a href=&quot;https://github.com/can3p/clj-carbonapi/blob/master/test/carbonapi/parser-test.clj&quot;&gt;test&lt;/a&gt;
for parser immediately when I decided that I covered all the possible syntax
scenarios. The other benefit of the repl comparing to the unit tests is that you
can actually run function from any namespace you have to get an idea how it
works. Final benefit of the repl that I want to admit is the fact that you can
use any functions to test results of the function call and that mean that you
can play with the data with all the functions available to the core and then
write real code when you’re confident with the path you want to take.&lt;/p&gt;

&lt;p&gt;The application is split into several modules and most of them are surprisingly
small. Here are the few notes on each.&lt;/p&gt;

&lt;h2 id=&quot;carbonapiparser&quot;&gt;carbonapi.parser&lt;/h2&gt;

&lt;p&gt;Parsing is one of the tasks that can be harder or easier depending on whether
you know the right way to solve it. For parsing the right approach is to use
context free grammars and clojure ecosystem provides beautiful
&lt;a href=&quot;https://github.com/Engelberg/instaparse&quot;&gt;instaparse&lt;/a&gt; library for this purpose.
Library takes a grammar that can be written in one of two languages, compiles it
and uses it to parse a string into nested list and also supports
transformations, so it’s really easy to get the proper structure.  I won’t go
into the details here, because there are already
&lt;a href=&quot;http://www.walkwithoutrhythm.net/blog/2013/05/16/instaparse-parsing-with-clojure-is-the-new-black/&quot;&gt;some&lt;/a&gt;
&lt;a href=&quot;http://gigasquidsoftware.com/blog/2013/05/01/growing-a-language-with-clojure-and-instaparse/&quot;&gt;tutorials&lt;/a&gt;
on the net.&lt;/p&gt;

&lt;p&gt;The second interesting part is actual tree execution. While it’s quite possible
to make a nested list that can be then evaluated there is a problem with this
apptoach - all these functions won’t have any context in them and that means
that AST should contain all input data (or have side effects) to make it
possible to execute it with eval.&lt;/p&gt;

&lt;p&gt;This issue is solved with postwalk function from clojure.walk package. Function
takes a nested list and a function as an input and calls the function with the
lists stored in the source list starting from the most nested one and goes up to
the root replacing parts of the list with results of the function call. Thus
it’s relatively easy to eval list manually with any logic.&lt;/p&gt;

&lt;h2 id=&quot;carbonapifunctions&quot;&gt;carbonapi.functions&lt;/h2&gt;

&lt;p&gt;carbonapi.functions namespace provides functions to work with cabonapi metrics
objects. The interesting part was that at least in simple cases I could abstract
way metrics object format and allow to define operations on the data with a
&lt;a href=&quot;https://github.com/can3p/clj-carbonapi/blob/master/src/carbonapi/functions.clj#L47&quot;&gt;simple
function&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I didn’t implement complex cases but I think that this is not a problem of
complex algorythm but just a matter of implementation.&lt;/p&gt;

&lt;h2 id=&quot;carbonapiremote&quot;&gt;carbonapi.remote&lt;/h2&gt;

&lt;p&gt;This namespace provides an interface to fetch data from carbonzipper or graphite
instance. The json is implemented in a quite straightforward way, the most
interesting part is in protobuf support. The complication with it is that
protobuf has it’s own minilanguage and a compiler that turns this language in
the source code in java. While definition itself was
&lt;a href=&quot;https://github.com/dgryski/carbonzipper/blob/master/carbonzipperpb/carbonzipper.proto&quot;&gt;found&lt;/a&gt;
in carbonzipper repo, the integration part erased few evening hours from my
life.&lt;/p&gt;

&lt;p&gt;The main problem was that in clojure there are two packages needed for this -
&lt;a href=&quot;https://github.com/ninjudd/clojure-protobuf&quot;&gt;clojure-protobuf&lt;/a&gt; and
&lt;a href=&quot;https://github.com/ninjudd/lein-protobuf&quot;&gt;lein-protobuf&lt;/a&gt; and you can be as
unlucky as I was to choose incompatible versions of these two packages and get a
compiled java file that doesn’t play nicely with library. So, I warned you.&lt;/p&gt;

&lt;p&gt;The other tricky thing with protobuf was that for some reasons I couldn’t see
all the data in a hash returned by protobuf-load and I had to implement a
function that took response parsed from protobuf input and transformed it into
the structure in a format returned in json format. After inspection of compiled
java file I found out that there is a method valAt that &lt;a href=&quot;https://github.com/can3p/clj-carbonapi/blob/master/src/carbonapi/remote.clj#L14&quot;&gt;can be
used&lt;/a&gt;
to the data that is hidden otherwise.&lt;/p&gt;

&lt;h2 id=&quot;carbonapichart&quot;&gt;carbonapi.chart&lt;/h2&gt;

&lt;p&gt;This part was done thanks to &lt;a href=&quot;http://incanter.org/&quot;&gt;incanter&lt;/a&gt; library and
&lt;a href=&quot;http://www.jfree.org/jfreechart/&quot;&gt;JFreeChart&lt;/a&gt; library that powers plotting
functionality of incanter.&lt;/p&gt;

&lt;p&gt;I once again had to massage data before it could be passed to the plotting
library but otherwise the library was a pleasure to work with. I easily got a
graph in a new window (which is super useful in a repl usage) and at the same
time I could get and image object that can be processed and
&lt;a href=&quot;https://github.com/can3p/clj-carbonapi/blob/master/src/carbonapi/server.clj#L18&quot;&gt;sent&lt;/a&gt;
by server to the client browser.&lt;/p&gt;

&lt;h2 id=&quot;carbonapiserver&quot;&gt;carbonapi.server&lt;/h2&gt;

&lt;p&gt;I used &lt;a href=&quot;http://www.http-kit.org/&quot;&gt;http-kit&lt;/a&gt; to implement server and the only
note I can leave that it was easy and straightforward and integration with
charts was suprisingly simple and painless.&lt;/p&gt;

&lt;h2 id=&quot;carbonapicore&quot;&gt;carbonapi.core&lt;/h2&gt;

&lt;p&gt;This is a namespace that provides a top level api to show graphs and is supposed
to be an obvious entry point for any repl usage.&lt;/p&gt;

&lt;h2 id=&quot;carbonapimain&quot;&gt;carbonapi.main&lt;/h2&gt;

&lt;p&gt;This one is obvious entry point for cli usage. The cool part is the usage of
clojure.tools.cli package that is the greatest cli parsing library I ever made.&lt;/p&gt;

&lt;h2 id=&quot;conslusion&quot;&gt;Conslusion&lt;/h2&gt;

&lt;p&gt;Well, it was fun!&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Stop pushing the specs forward</title>
    <link href="https://can3p.github.io//blog/2015/09/29/moratorium/"/>
    <updated>2015-09-29T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2015/09/29/moratorium</id>
    <content type="html">&lt;p&gt;Peter-Paul Koch wrote a &lt;a href=&quot;http://www.quirksmode.org/blog/archives/2015/07/stop_pushing_th.html&quot;&gt;post&lt;/a&gt;
a while ago, and there was a
&lt;a href=&quot;https://www.nczonline.net/blog/2015/09/is-the-web-platform-getting-too-big/&quot;&gt;follow-up&lt;/a&gt; from
Nicholas Zakas. While reading the first post I found myself almost agreeing with the author. Almost,
because I really think that the amount of new features is not the problem for me and I can explain
why, because the reason is very simple.&lt;/p&gt;

&lt;p&gt;Throughout my career I work on high-traffic complex sites with lots of users and all that means that
all they use different browsers. From my perspective that means that I either have to use graceful
degradation for new features or just you the least common denominator of all specs, and in my case
it is Inernet Explorer 7 and 8.  It’s not sexy, it’s slow etc., but it’s users still bring a load of
money and while version 7 is slowly fading away, version 8 will be with us for another year or two.
Another aspect is that we do a lot of small changes for the product and graceful degradation in pure
css just does not scale. It’s relatively simple with the shadows or basic transforms, but anything a
bit more complex means that there is no graceful degradation for that, the solution requires
different markup for both cases. And it’s not even about flexbox (which is btw not very new, first
spec was &lt;a href=&quot;http://www.w3.org/TR/css-flexbox-1/&quot;&gt;released&lt;/a&gt; in 2009!), it may be just
&lt;a href=&quot;http://caniuse.com/#feat=calc&quot;&gt;calc&lt;/a&gt; property and a very &lt;a href=&quot;http://caniuse.com/#compare=ie+8&amp;amp;compare_cats=CSS&quot;&gt;big
number&lt;/a&gt; if other spec that this browser doesn’t
support. Even if we throw IE8 out, there is IE9 which is much better but still &lt;a href=&quot;http://caniuse.com/#compare=ie+9&quot;&gt;not
perfect&lt;/a&gt;. There is an additional consideration: not all specs are
equally good. IE6 era was perfect in sense of true research. You remember all these sites with true
grids, bulletproof solutions for sticky footers and vertical alignment? One could imagine that all
this should result in one true layout specification that will be flexible enough to cover all them.
But it’s not the case, people make mistakes, people make APIs that are not perfect. And this means,
that the thing that was once thought as solution for everything becomes obsolete artifact of the
past. I remember when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;calc&lt;/code&gt; property was introduced everyone was pretty sure that that’s all we
need. Er, may be also &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;box-sizing&lt;/code&gt; and that’s it, all layout problems solved. And then we got
flexbox, &lt;a href=&quot;http://www.w3.org/TR/2015/WD-css-grid-1-20150917/&quot;&gt;css grids&lt;/a&gt;, &lt;a href=&quot;http://www.w3.org/TR/css-template-3/&quot;&gt;css template
layouts&lt;/a&gt;, &lt;a href=&quot;http://www.w3.org/TR/css3-multicol/&quot;&gt;css columns&lt;/a&gt;
and probably will get more. There are some specs that went out of fashion before they got
implemented by major browser vendors, like &lt;a href=&quot;http://www.w3.org/TR/css-variables/&quot;&gt;css variables&lt;/a&gt;.
I am enumerating all this just to prove my point - at least in CSS part we use technologies that
are with us more or less since IE6 - floats, absolute positioning etc. And no matter how big the web
platform is we use only tiny fraction of it because it is the only one that works.&lt;/p&gt;

&lt;p&gt;Another problem with new browser features is that they are all added in append-only mode. Features
can only come in, there is no way to take them out without breaking the web somewhere. And that
means that even unpopular or badly designed specs are here forever to annoy users and bloat
browsers. Time passes and now it’s more and more clear that even cascade part of CSS was not the
best possible solution. And even more, if you ask me, what relatively new CSS specs are really worth existing,
I would mention media queries and transitions and this is it. But I can easily mention at least
two libraries types that really changed the landscape of styling - css grid libraries and css
preprocessors. There was no need to write a spec about them and implement them for evey vendor,
because these two technologies happily live on top of existing standards.&lt;/p&gt;

&lt;p&gt;If we consider JS part of web platform it appears that the situation is a bit better in some parts
but fundamentally the same. Features keep being added, but we still have to send ecma 232/3rd ed. to
browser because that is the only version everyone supports. And again we are years behind from
latest and greatest and all innovation again happens in libraries - we have all sorts of languages
compiled to javascript, we have &lt;a href=&quot;https://babeljs.io/&quot;&gt;transpilers&lt;/a&gt; or even
&lt;a href=&quot;http://sweetjs.org/&quot;&gt;macros&lt;/a&gt; that give us the latest version of ES standard in any
browser without a need to implement all that in the browsers. Even module loaders happily existed as
external libraries for years before we got ES6 modules spec, and this spec still has whole bunch
of questions to solve - aliases, bundles, slices and a lot of features that existing module loaders
already have.&lt;/p&gt;

&lt;p&gt;One would say that current fragmentation only exists because of IE, and once these guys figure out
how to quickly update their browser the problem will be solved. Certainly it will help, but mobile
fragmentation still exists and some browsers even start to implement -webkit- properties, because of
total dominance of different webkit based browsers in some markets.&lt;/p&gt;

&lt;p&gt;So, my point is that new standards have a very long way to go before any wide adoption for natural
reasons and the only way to get through this mess is to abstract away and build something on top.
And this is the exact reason why we see a real explosion of influentual libraries like Bootstrap or
React.JS. They just work for majority of cases, they fix big problems now without the need to wait
for indefinite future. But do I want to see them or their approach to be adopted in browsers
as a spec? No, because that will kill the major benifit of the libraries - you can use them as long
as they fit the purpose and you can throw them out if something better appears on the market.&lt;/p&gt;

&lt;p&gt;So, what is the conclusion of all this writeup? I think that the future is not after new platform
extensions, but in libraries that will provide standard ways to solve standard problems. Dialogs,
lightboxes, calendars, layouts, endless battles about module formats, server communication  - that’s
what bothers most of people and wastes billions of work hours and solving these issues will push the
web forward for real. And there are &lt;a href=&quot;http://elm-lang.org/&quot;&gt;plenty&lt;/a&gt; of
&lt;a href=&quot;https://github.com/clojure/clojurescript&quot;&gt;more&lt;/a&gt; or &lt;a href=&quot;https://www.dartlang.org/&quot;&gt;less&lt;/a&gt;
&lt;a href=&quot;://www.meteor.com/&quot;&gt;successful&lt;/a&gt; attempts to solve these issues.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Img.js - first optimizations</title>
    <link href="https://can3p.github.io//blog/2015/03/30/imgjs-start/"/>
    <updated>2015-03-30T00:00:00+02:00</updated>
    <id>https://can3p.github.io/blog/2015/03/30/imgjs-start</id>
    <content type="html">&lt;p&gt;This winter I studied the fascinating &lt;a href=&quot;https://www.coursera.org/course/images&quot;&gt;course&lt;/a&gt;
about image and video processing by Guillermo Shapiro and
decided to make my own javascript library with bells and whistles
and also try a few approaches to code that were not battle tested by me.&lt;/p&gt;

&lt;p&gt;From the start I wanted to get several things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;es6 library code with umd es5 code at the end so that it could be used in any browser environment.&lt;/li&gt;
  &lt;li&gt;monadic approach to api, so that all the operations on images can be both synchronous and asynchronous,
and we still get all this beautiful chaining api.&lt;/li&gt;
  &lt;li&gt;internal api, that will consists from the set of operators, that can be composed together before
applying to the actual image. The rationale is that if you for example make two consequent inverse
operations than the library should not process image at all in this case. And also it should be very
fun if I can get that far.&lt;/li&gt;
  &lt;li&gt;very little public api that is really public only for transformation plugins. In ideal situation
I want to be able to build the library for any number of transformations if I want to and don’t
ship everything under the sun just because it’s easier. And who nows, may be I will make a img.jspm some day :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First two goals were relatively easy ones and probably I’ll write about them in next post,
but the last one required at least some understanding about what kind of transformations I will want
to make with the image so that I could start defining some solid api.&lt;/p&gt;

&lt;p&gt;The first and the most trivial transformation is &lt;a href=&quot;https://github.com/can3p/img.js/blob/731b8989a49ca441508018902a7e9752123f6ad7/dist/img.js#L65&quot;&gt;identity&lt;/a&gt;
where you transform the image in such a way that the result will be identical
to the source. I got there very easily and decided to move to the next one
after that and use the identity filter only in cases if I’m changing core api
and want to test on something that doesn’t contains potential algoritmic errors
for sure.&lt;/p&gt;

&lt;p&gt;The next easy filter was grayscale. The first thing to notice there is that there
is no such thing as canonical grayscale. If you check &lt;a href=&quot;http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/&quot;&gt;this&lt;/a&gt;
post than you will see that there are at least three of them and there should be
more because the only requirement is equality of R, G and B values for each pixel
at the end. I took the path of luminosity approach. I have no idea about where
the coefficients come from, but here is the first naive implementation:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;lift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;grayscale&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;grayscale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;new_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;create_image_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cur_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;new_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;luminosity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;new_arr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;new_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;cur_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;new_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;luminosity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;mf&quot;&gt;0.21&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cur_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
                    &lt;span class=&quot;mf&quot;&gt;0.72&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cur_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
                    &lt;span class=&quot;mf&quot;&gt;0.07&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cur_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

      &lt;span class=&quot;nx&quot;&gt;new_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;luminosity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;new_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;luminosity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;new_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;luminosity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;new_arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cur_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;new_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is the client code to run this:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./test.jpg&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;grayscale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;outputTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.output&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The facts to mention here is that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the code is asynchronous&lt;/li&gt;
  &lt;li&gt;I don’t care about browser compatability at all
right now and it was one of the starting design decisions
for the library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve been looking on the grayscale code again and again
and it was apparent that it’s nothing more than just
matrix multiplication.&lt;/p&gt;

&lt;p&gt;Wouldn’t it be nice if I’ll just right this:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;lift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;grayscale2&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;grayscale2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;new_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;clone_image_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;multiply_matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.07&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                                     &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.07&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                                     &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.07&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                                     &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is the first implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multiply_matrix&lt;/code&gt; function:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;multiply_matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;filter matrix can be only 4 by 4&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;max&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The main problem with it was the performance (grayscale2: around 350ms
and grayscale around 125ms on my MBP for &lt;a href=&quot;https://github.com/can3p/img.js/blob/731b8989a49ca441508018902a7e9752123f6ad7/example/test.jpg&quot;&gt;1024x768
image&lt;/a&gt;)
and I was not really sure was it about algorythm or about js
interpreter itself.&lt;/p&gt;

&lt;p&gt;Searching the internet revealed that matrix multiplication is not a
really
&lt;a href=&quot;https://en.wikipedia.org/wiki/Coppersmith%E2%80%93Winograd_algorithm&quot;&gt;easy&lt;/a&gt;
task to optimize in terms of performance and I decided to focus on js internals.&lt;/p&gt;

&lt;p&gt;Profiler showed that Chrome was really busy with garbage collecting
and setting length of array to zero costed me around 100ms. Wow.&lt;/p&gt;

&lt;p&gt;Ok, I’ve tried to assign new array at the end of each loop pass:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The perfomance fell to miserable 550ms but very fun fact was
that profiler didn’t show anything specific and garbage collection
was reducde by and order of magnitude.&lt;/p&gt;

&lt;p&gt;Ok, let’s try to go from the other side and not reset the length of
array at all. Memory is cheap.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And it was the first success: around 260ms! Much better but still not
as good as the original thing.&lt;/p&gt;

&lt;p&gt;I decided not to give up and started looking for the hints about v8
interpreter internals that can give me the speedup. The
most interesting &lt;a href=&quot;https://www.youtube.com/watch?v=UJPdhx5zTaw&quot;&gt;video&lt;/a&gt;
I’ve found gave me all sorts of hints about how I can shoot myself
in a foot in terms of performance but I didn’t get much after
discovering the fact the multiply_matrix function is getting
deoptimized constantly. Maybe I’ll return to this later, but
one cool fact that I’ve discovered is that you can get the
fresh instance of the Chrome browser by just starting it
with different working directory and it will hold the version
that was with it right after install before updates happened.
Also you can pass parameters right to the v8 engine and get all
sorts of debug output.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/Applications/Google&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Chrome.app/Contents/MacOS/Google&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Chrome &lt;span class=&quot;nt&quot;&gt;--user-data-dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/tmp/chrome_dev&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--js-flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--trace-opt --trace-deopt --trace-bailouts&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here –user-data-dir option will give the fresh instance (even if there is
a running one) and –js-flags will be passed to v8.&lt;/p&gt;

&lt;p&gt;The main point for the talk is that the more information you give to browser
about types in your code the more speedup you may theoretically gain. I remembered
that typed arrays were introduced for all sorts of low-level number crunching
and found a very good &lt;a href=&quot;https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/&quot;&gt;article&lt;/a&gt;
on Mozilla Hacks.&lt;/p&gt;

&lt;p&gt;I decided to start with just Uint8ClampedArray:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ArrayBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;buf8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Uint8ClampedArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;buf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;buf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It gave me blazing 120ms, as fast as original, but I was curious
about using Uint32Array and decided to try it:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ArrayBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;buf8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Uint8ClampedArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;buf32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Uint32Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;buf32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;real_pos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;buf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m not detecting endiannes here just measuring speed. And measurements
gave me the incredible 20-25ms, five times faster than original function!&lt;/p&gt;

&lt;p&gt;Probably there is a way to make even this magic number lower, but
I decided to stop there for a while.&lt;/p&gt;

&lt;p&gt;Another speedup I got at the image rendering at the end. At first I
wanted to get a proper img tag at the end and my code looked like this:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;lift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;outputTo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;outputTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2d&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;putImageData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toDataURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;image/png&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Chrome on averaged spend up to 200ms on this and at least half of this
time was spend on the toDataUrl method execution. Firefox
behaved even worse with 600ms. It was too bad and I decided
to drop image part for now and output just canvas:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;lift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;outputTo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;outputTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2d&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;putImageData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the timing went to 5ms at firefox and less the one ms in Chrome.&lt;/p&gt;

&lt;p&gt;With all these optimisation the image processing part became barely
noticable which is very good and opens space for more advanced
filters that will run in a reasonable time now.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Adopting Microservices at Netflix: Lessons for Team and Process Design</title>
    <link href="https://can3p.github.io//links/2015/03/11/nginx-lessons/"/>
    <updated>2015-03-11T00:00:00+01:00</updated>
    <id>https://can3p.github.io/links/2015/03/11/nginx-lessons</id>
    <content type="html">&lt;p&gt;Article tells the story of Netflix’s approach to the development process and deployment.
There are some really good parts like making assumtions explicit and validating them
from time to time. From my own experience I’ve seen that kind of things multiple time.
Organizations get bigger, people get older care more about vacations and overall
stability and care less about company future or new technologies at all.&lt;/p&gt;

&lt;p&gt;I cannot really judge the part about using cloud solutions everywhere, but continuous
delivery process and independent serivces, that are somewhat easy to deploy is what
I experience now and I don’t want to go back.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Learning how to learn: my walkthrough</title>
    <link href="https://can3p.github.io//blog/2015/01/26/learning-howto/"/>
    <updated>2015-01-26T00:00:00+01:00</updated>
    <id>https://can3p.github.io/blog/2015/01/26/learning-howto</id>
    <content type="html">&lt;p&gt;I decided to write down my own thoughts and conclusions from the
course “Learning how to learn” as a final project. My own part will
represent some ideas that I have and that provide more context to
the course on my opinion.&lt;/p&gt;

&lt;p&gt;First of all, the motivation part. Why do we learn? What do we learn?
This term has a very broad definition and it’s very easy to end up
learning lot’s of things of very little importance or use for us. The world
around us is fascinating and there are tons of things that are worth
knowing. In reality the number of these areas is even bigger
and every learning subject hides lots of books, videos and courses
behind it. You can take photography, drawing, biology, artificial
intelligence - any field you can imagine, and it will be really huge
and can be split into smaller independent pieces that are huge on itself.
When we learn we’re trying to make ourselves better in this sense or
another and it’s very easy to start learning everything at once and
it’s obvious that the possibility of learning everything at once is
much lower than in situation when you have only one subject that you
can master. So, failure is very much possible and this might bring
the illusion that the ability to learn has vanished and basically nothing
new finds its way into the head, which is just not true.&lt;/p&gt;

&lt;p&gt;As was stated in the course we all have two types of memory - shortterm
and longterm one, and what happens is that we place some initial knowledge
into the shortterm memory and then switch to another subject which will
mostly replace the previous one with new information. The solution is to
have a focus and it’s very important on my opinion. The focus in this
situation is an ability to prioritize items that you want to learn and
prevent yourself to learn anything that is not a top priority.&lt;/p&gt;

&lt;p&gt;How to get the focus? The most important thing on my opinion is to have
an idea what exactly do you want to reach at the end or at least to know
the direction where you want to go. To have the goal
or to feel the meaning is important in all areas of life, but it’s really
important in learning, because it will help to structure studies and
to define a roadmap for future.&lt;/p&gt;

&lt;p&gt;These are the most important things that I want to add on my own. If
we talk about individual courses or books that are to be mastered I would
like to introduce my own favorite set of ideas from the course, that
I treat as very important.&lt;/p&gt;

&lt;p&gt;The two most important concepts that I got from the book are chunking
and the notion of long and shortterm memory. These terms are not actionable
by themselves but different methods described in the course help use one
concept or another.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Chunking is  is a process of forming self-contained bundles
out of any ideas and terms so that they can be used on their own in different
context without figuring out all underlying details every time.&lt;/li&gt;
  &lt;li&gt;Shortterm memory is the part of our memory where we hold the information
that we found out right now and we can easily forget.&lt;/li&gt;
  &lt;li&gt;Longterm memory is the general storage of our knowledge. All things that
we know very well end up in this type of memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the process of learning from my understanding is a continuous process
of forming chunks and putting them in the longterm memory where we can use it.&lt;/p&gt;

&lt;p&gt;Which of the methods shown in the course are most helpful on my opinion and
from my experience? I would say that they are &lt;em&gt;practice, (self)explanation
and the ability to switch off&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;First of all, &lt;em&gt;practice&lt;/em&gt;. The fact we understand something doesn’t necesary
mean that we know it or can do it. Most of the times our understanding
is superficial or doesn’t take into account all aspects of reality, but
that matters a lot and not only for learning new subject but also for
using it in real life. I would take the example that is closest to me
at the moment. When we talk about algorythms in robotics there are lots
of layers in them. You can learn high level planning solutions, but if
you try to implement them, you will find that they do not just work by
themselves, there are several layers below them, each closer to the
hardware than the previous one. On the top level you can produce
command &lt;em&gt;clean(floor)&lt;/em&gt;, but level below there is a whole new set of
problems that include navigation on the middle level and actual interaction
with the hardware on the level below that, and the complexity of the
subjects is not evident when looking on the topmost algorythms. Given that
the practice does not only allow to get the ideas from the shortterm memory
to a longterm one, it also provides lots of valuable insights of how
this idea interacts with others and can probably suggest the next steps
in the studies and maybe even can influence on future projects that you
may have.&lt;/p&gt;

&lt;p&gt;With all these properies practice is helpful in conquering both chunking
and putting subject in a longterm memory for later wild use. Practice really
helps to get the feeling of the constraints, limitations and dependencies of
the ideas. The important point is that not every type of practice is helpful.
It’s very easy to solve simple problems but only finding solutions to difficult
ones makes us smarter.&lt;/p&gt;

&lt;p&gt;The second important part for me is (self)explanation. I would say that
it’s a somewhat natural part of practicing, but if with doing exercises you can
find the limitations of the methods and ideas, with explaining ideas you
can immediately see the parts that are still weak and are needed to be studied
better.&lt;/p&gt;

&lt;p&gt;The last thing is an ability to switch from the subject and be able to keep
rhythm and relax. If we find the goal that we strongly want to achieve it’s
very tempting to try to bruteforce it and learn it through several very long
sprints by devoting all energy to learning. The sad truth for me is that
whenever I tried to do that I failed because I got exousted very soon after
beginning. The correct way that really works for me is to develop the rhythm
of doing something and try to do my best during scheduled hours and then switch
to some very different task such as movies or (especially) physical exercises.
A believe that doing sports involves very different parts of the brain that
studies and sometimes I can really turn myself into kind of thoughtless mode
when I just do what I do and all thoughts are shifted to the background. I think
it exactly correlates with the diffuse thinking mentioned in the course. But
for me it’s not only the way to process information in the background. Switching
activity is also a way to relax and besides allowing as to grow in several directions
it also allows to fill the progress in different things you’re doing and that
helps to keep the motivation on the higher level.&lt;/p&gt;

&lt;p&gt;At the end I want to dwell a bit on the procrastination. I think all people face
it at some level or another. Sometimes the task is not that pleasant to do or it’s
just too big to grasp or we’re just tired or didn’t get enough sleep. As was said
in the course, for everyone there are cues that trigger something within us
sending us to spend another twenty minutes in the facebook. But what’s good is that
we can build them to help us to work. Pomodorro technique is especially helpful in this
sense that it puts the focus on the work. It may seem that 25 minutes is not that
big time, but if you try to setup timer during the work, you can suddenly find out
that in some days it can happen that you accomplish just two or three sets of pomodorro.
Even with this technique there is still a room for emprovement and it’s about planning.
Simple todo list will help a lot, because you there will be no excuse that there is
nothing to do and even it you are not in the best shape in this particular day you
can just take one task after another and complete them even mechanically - anyway you
still make a progress and you wouldn’t otherwise.&lt;/p&gt;

&lt;p&gt;I think I’ll finish here. All the things described here are a prototype of my ongoing
learning experiment. I really struggle to implement it in full scale in my life and
will be very glad if anyone can find this document helpful in any sense.  Thanks for your attention.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Taming the Asynchronous Beast with CSP in JavaScript</title>
    <link href="https://can3p.github.io//links/2014/09/09/js-csp/"/>
    <updated>2014-09-09T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/09/09/js-csp</id>
    <content type="html">&lt;p&gt;Another amazing article by James Longster, that continues his series of blog post about
web development from future, where he speaks about all kinds of features, techniques and
new abilities of Javascript language that completely change the way how we can deal with
common problems during the development.&lt;/p&gt;

&lt;p&gt;One of the problems is constant dealing with events, wich leads to rather complicated
code that balances between different events by containing intermediate state somewhere
and the more complex interaction gets the more complex code we get. Another source of
asynchronous complexity is actual async part which comes from any ajaxy activity.&lt;/p&gt;

&lt;p&gt;Js-csp is a neat way of dealing with all these two issues in a uniform way. The way it
unified if that all the differences are abstracted away by channels where any value
can be put in and read from. The key difference, that these operations are &lt;strong&gt;blocking&lt;/strong&gt;
with proper try/catch support etc. Hence you get several blocking blocks of code that
actually only block themselves and communicate via channel messages.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Number Words and Number Symbols: A Cultural History of Numbers</title>
    <link href="https://can3p.github.io//books/2014/08/17/numbers/"/>
    <updated>2014-08-17T00:00:00+02:00</updated>
    <id>https://can3p.github.io/books/2014/08/17/numbers</id>
    <content type="html">&lt;p&gt;I had the russian edition. The book consists of several sections and begins
with author’s explanation about how numbers had possibly emerged in the
languages of different nations. They appeared not at once, but one after another
anf often not sequencially but when they were needed. That’s why round numbers
(tens, hundreds) emerged earlier then their lower non round neighbours and the
names of round numbers affected the names of their neighbours too. In some
cases people even didn’t need the actual names for the numbers and used signs
or special names connected with the field they were used at. Right now we all
count in the decimal system, but much earlier people have used names based on
five or on twenty. Even the definition of the number was evolving. Not only
zero wasn’t treated as a number, one was also not treated by europeans as a
number for a long time.&lt;/p&gt;

&lt;p&gt;There are myriads of different facts represented in the book and all they are
accompanied with lots of photos and cross references. The density of them sometimes
is so high, that it was a bit hard to get through all of them while reading.
Also, some examples or descriptions of the calculation techniques and algorythms
are not well defined and may contain errors (may be it’s only true for russian
edition) that it takes significant time to get it right. For example to really
understand how calculations were done by Sumer I just took anothe book (“History
of Mathematics” by A. Yushkevich).&lt;/p&gt;

&lt;p&gt;The big advantage of this book is that during exmplanations author constantly
shows connections with current languages and shows how different words or
terms were born like bankrupt, bureau, the names and symbols of currencies etc.&lt;/p&gt;

&lt;p&gt;It’s very interesting to read section devoted to calculations and numbers usage
in Middle Ages. We are so used to arabic numbers (that are actually form India)
that it’s hard to believe that in the middle ages most of european countries
used roman numbers for writing and special calculation table for actual calculations,
because roman letters do not provide any machanics to use them in the calculations
directly. These tables and their analogs were wide spread everywhere from Japan
to Western Europe and were used even in the twentieth century.&lt;/p&gt;

&lt;p&gt;It may also be a revelation that some nations used non-decimal numbering systems
for the calculations, based on twenty or sixty, and it affected the way
the calculations were done by them. The consequence for us now is that
we have 60 seconds in a minute now.&lt;/p&gt;

&lt;p&gt;The last cool thing that I want to mention is how calculations were made when the
paper was expensive and not widespread. All sort of counting rods were used in
the Europe and one type of them was called Split tally sticks and was used by
British exchecquer until 1826. It was used for tax collection or debt recording.
Every stick with the number of tax to be paid or debt to be returned was split
into to part and by joining them again one could verify that the payment is
correct and should be done.&lt;/p&gt;

&lt;p&gt;The book has a lot of useful information and it is a drastic difference comparing
with most of modern popular science books that usually have the same volume but
contain just few facts and just repeat them through the pages again and again
in different variations.&lt;/p&gt;

&lt;p&gt;I highly recommend this book for all those who are interested to know more about
how we ended up with all the numbers and the things around them that we have now.
The only advice I can give is not to dig very deep into the sections connected
with the representation of numbers in different languages. This section has so many
comparisons and crossreferences between different language that it really doesn’t
give a lot to the general idea but it really takes a lot of time to understand
the whole thing.&lt;/p&gt;

&lt;p&gt;As a conclusion I want to say that I was surprised to find very little information
about the author in the internet, and there is actually rather small number of
references to this book, at the same time it’s quite old. All this raises some
suspictions, that this book is not a classic one on the subject, so probably
it may differ from “official” points of view and it may be worth to double check
any information of any importance in different sources before actually using it
anywhere seriously.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Getting it done with Haskell</title>
    <link href="https://can3p.github.io//links/2014/08/09/real-haskell/"/>
    <updated>2014-08-09T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/08/09/real-haskell</id>
    <content type="html">&lt;p&gt;A presentation about real-life experience with Haskell development. The language
is more than 20 years old but still the real applications of the language are
rather rare, so such links may be very useful.&lt;/p&gt;

&lt;p&gt;I don’t know a lot about language itself, so some of the slides are rather cryptic
to me, but interesting parts are athe beginning where the overview of the literature
is and the end of the presentations where one can find general suggestions about
how to sturcture the haskell development if you want to do it.&lt;/p&gt;

&lt;p&gt;May be worth looking again in some future where I and Haskell will meet together.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>IBM Chip Processes Data Similar to the Way Your Brain Does</title>
    <link href="https://can3p.github.io//links/2014/08/08/ibm-neurosynaptic/"/>
    <updated>2014-08-08T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/08/08/ibm-neurosynaptic</id>
    <content type="html">&lt;p&gt;IBM has just presented the computer chip with the fundamentally new architecture
that is really different from Von Neuman arctitecture in all the ways. It’s
still built with transistors but at the end it’s a hardware made neural network,
where these transistors work as perceptrons or synapses.&lt;/p&gt;

&lt;p&gt;On the tech demo it was demonstrated that such a chip can do image recognition
from a live video stream with a very little energy consumed and at the same time
the decent notebook could only deal with 10 times slow video stream and consumed
10,000 more power.&lt;/p&gt;

&lt;p&gt;Since it’s a neural network it requires very specific programming. It has not distinct
processor and memory, because such network does both. And to program it correctly
it’s required to program how parts blocks of the chip should be interconnected and
how much influence they will have on each other. And then one should run the code
in the chip simulator and only after that the code could be run on chip.&lt;/p&gt;

&lt;p&gt;So the chip is really great in terms of future potential but provides a very big
challenge in terms of programming because it’s so different from anything else.&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Building the Firefox browser for Firefox OS</title>
    <link href="https://can3p.github.io//links/2014/08/08/firefox-os/"/>
    <updated>2014-08-08T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/08/08/firefox-os</id>
    <content type="html">&lt;p&gt;Long and interesting story of a project that ended up as a firefox OS. Probably
it’s only a part of the full picture, because author only talks about building
browser for the system, but it’s still very interesting who it all went from
a few men project with a quick and dirty prototype to a fully featured browser
that will later integrate with the system almost entirely to diminish the borders
between apps and websites.&lt;/p&gt;

&lt;p&gt;The story is interesting also in sense that the development happens really around
the world from Paris to San Francisco and Tai Pei which reminds my somehow the
novels of Neal Stevenson.&lt;/p&gt;

&lt;p&gt;The other thing worth looking at is a number of improvements that was made in Gecko
in order to make it possible to beal fully featured browser web app.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>The wind rises</title>
    <link href="https://can3p.github.io//movies/2014/08/06/the-wind-rises/"/>
    <updated>2014-08-06T00:00:00+02:00</updated>
    <id>https://can3p.github.io/movies/2014/08/06/the-wind-rises</id>
    <content type="html">&lt;p&gt;Animation film by Hayao Miyadzaki. Film is absolutely perfect
if you think about it without historical context of that time
and I would highly recommend to watch it carefully instead of
reading another one big article about how to fight ro live with
procrastination.&lt;/p&gt;

&lt;p&gt;The main figure, Jiro Horikoshi dreams about planes all his
childhood and spends all his animated life by creating them and
devotes all his time to his dream. His desire to build the
perfect plane is so high that when he gets the mission to build
a war plane he thinks more of how to build it than on the fact
that Japan is going to attack surrounding contries to expand
it’s influence. Or probably he approves these actions, but
I think that Miyadzaki purposely omitted this in the movie.&lt;/p&gt;

&lt;p&gt;If we will put the war away from the story then at the end
we get a story of a man who loves his job to death who dreams
about it even when he doesn’t work. And who loves his wife
but even given that she’s very ill he can get from his obsession
with planes and spend a bit more time with her.&lt;/p&gt;

&lt;p&gt;And it’s the situation that make the film so magnicient to me. Jiro
works on his plane even when she comes to him to spend her last
days with him. The illness is on the final stage and what happens
is that he just accepts this fact and continues to work on his
plane. His wife Nahokoalso accepts this but also accepts the
fact the even these days Jiro spends most of his time pursuing
his dream. We even see that they both see the pain of all this,
but he doesn’t stop working even through the tears.&lt;/p&gt;

&lt;p&gt;The movie reminds me the life of such people as Sergey Korolev
who work to make their dream come true and even big struggles
cannot stop them from doing it.&lt;/p&gt;

</content>
  </entry>
  
 
  
  
  <entry>
    <title>Refactoring in Java, Scala, and Clojure</title>
    <link href="https://can3p.github.io//links/2014/08/06/refactoring/"/>
    <updated>2014-08-06T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/08/06/refactoring</id>
    <content type="html">&lt;p&gt;Talk that is interesting only because it’s an example of attempt to somehow compare
expressiveness of three JVM languages - Java, Scala and Clojure. Author represents
a simple problem with solutions on all languages and then modifies the problem to
show how different languages can easily adopt to them.&lt;/p&gt;

&lt;p&gt;Author shows that Java solution is always bigger in terms of LoC
and requires the developer to work more on the type definitions than on solution
of the problem. The thing is that problem described in the talk is a toy one, so
it has nothing to do with real big systems and gives only feel of how it can be
in different languages. The other fact that decreases the value of the talk is
that at the end author says that although java looks very verbose and awkward
comparing to two other languages he still use it the most and will continue to
do it for any real projects in future. Given that there is a doubt that he really
believes in what he is talking about.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Out of the Tar Pit</title>
    <link href="https://can3p.github.io//links/2014/07/28/tar-pit/"/>
    <updated>2014-07-28T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/07/28/tar-pit</id>
    <content type="html">&lt;p&gt;Article is rather abstract at start and begins with defining what the accidental and essential complexities are in terms of program systems. The aproach is rather radical, because the essential complexity is treated only as a complexity that directly derives from the user specifications, so all language and infrastructure complexity is accidental which is some sort of bitter true. The proposed approach is to eliminate all possible accidental complexity if possible and separate remaining from so called essential logic and state si that they should not ever know about it’s existance. The proposed way to define such a system is to use relational model which can help defining all the system essential requirements and constraints in a purely declarative manner and just provides endpoints to extend the system with accidental complexity parts if needed for performance or usability reasons.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Station Q</title>
    <link href="https://can3p.github.io//links/2014/07/25/station-q/"/>
    <updated>2014-07-25T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/07/25/station-q</id>
    <content type="html">&lt;p&gt;This is article is related to one of the Microsoft Research project but doesn’t look like ordinary promo page from Microsoft. The purpose of article to clarify the purpose of attempts the Microsoft is doing in the field of quantum computing. What they are trying to do is to develop a scalable method to build quantum computers. Today a lot of labs have an ability to make some kind of quantum computation, but noone can build a stable computer and the reason is that any kind of distraction to one of qubits during computation destroys all the information, and they try to apply a topology to the field to make a protected version of qubit that will be able to resist distraction or at list its distraction will not lead to the failure of the computation. In 2000 a theoretical particle Majorana was proposed to exist and later one of the researches got evidence of their existance. If it’s true it should be more easy to build a protected qubit. It’s just a chance but if it succeeds we will get a scalable way to build quantum computers. Why is it imortant? Theoretically quantum computers are exponentioally more performant than ordinary ones, so the problems that can take thousends of year could take days or even hours and we will see a real explosion of scientific discoveries&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Meet Ray, the self-driving forklift that is parking cars at a German airport</title>
    <link href="https://can3p.github.io//links/2014/07/25/ray/"/>
    <updated>2014-07-25T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/07/25/ray</id>
    <content type="html">&lt;p&gt;Dusseldorf airport adopted the first system for automatic car parking called Ray. Person can just pay for the parking and the robot will lift the car and move it to the parking space. Ray device is fully autonomous and use lasers to navigate. Author claims that with this system the parking can be used up to 40% more efficiently with it than without&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>The Secret Government Rulebook For Labeling You a Terrorist</title>
    <link href="https://can3p.github.io//links/2014/07/23/rulebook/"/>
    <updated>2014-07-23T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/07/23/rulebook</id>
    <content type="html">&lt;p&gt;Very detailed article about how US government places people (in us and world-wide) in the lists of the terrorists suspect. Most worries are connected with the fact that the conditions to get in a such list are very wide (the term reasonable suspicion is declared, but it has a very wide definition, a lot of exceptions and if that also does not help, there are exception, that allow to place e person in a list even without any real facts), there is no way for a person to figure out if he is in such a list and there is no public procedure to get out of this list. Moreover existing mechanisms have nothing to do with the official court system and exists inside of military or intelligence agencies without any access from outside. If person was placed in such a list it immediately gets problems with transportation, job and any other area that has something related to security. If we think about other articles that show the scale of monitoring systems developed by US government it becomes really scary and cyberpunk novels become really close to the reality. The last fact that is worth mentioning is that if the person gets in the list all government or government related structures a forced to collect all the possible data of such a person and interestingly it’s not about military and intelligence agencies but also the organizations that should not have any connection with politics and monitoring people like USAID.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Riding the Juggernaut That Left Print Behind</title>
    <link href="https://can3p.github.io//links/2014/07/21/print/"/>
    <updated>2014-07-21T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/07/21/print</id>
    <content type="html">&lt;p&gt;Another article that pokes around the question that print media becomes more the part of the past. It cannot compete with social networks in terms of reaction on events, because new tweets and videos go online almost immediately. The interesting thing that is mentioned by author is that Internet provides different style of reading than print. With internet you just can get enough and always switch to the new content. As a consequence, one can hardly remember the information that he got from all the links he has read today. Probably, it’s not needed, but what is the reason for reading it anyway?&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Love People, Not Pleasure</title>
    <link href="https://can3p.github.io//links/2014/07/21/people-pleasure/"/>
    <updated>2014-07-21T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/07/21/people-pleasure</id>
    <content type="html">&lt;p&gt;Third NYTimes article in a day. This one deals with the question of one’s happiness, no more no less. One statement is that it’s not only happiness and unhappiness, it’s happiness and all shades of unhapiness. Fame, money, sex even make the person more unhappy than he was before, and it’s not the right way to go if the only thing you want to achieve is a happy life. Author states, that all these things work more as adrenaline than a cause for happiness, the more you get it the more you needed the less happy you are. And the only way to avoid this trap is to stop trying to reach happiness though this things.&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Letter of Richard Feynman to the graduate</title>
    <link href="https://can3p.github.io//links/2014/07/21/feynman/"/>
    <updated>2014-07-21T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/07/21/feynman</id>
    <content type="html">&lt;p&gt;The letter Richard Feynman wrote to one of his graduates to explain that it’s worth to work no only with world-scale problems but with all problems that you are capable solving of. If you’re not noticed by most of the world, you’re still known and loved and recognized amongst friends, relatives and colleages and you can always do something about that. Probably it’s worth to print this letter and to hang it on the wall in my room. The stie is worth additional exploration&lt;/p&gt;
</content>
  </entry>
  
 
  
  
  <entry>
    <title>Africans Open Fuller Wallets to the Future</title>
    <link href="https://can3p.github.io//links/2014/07/21/africa/"/>
    <updated>2014-07-21T00:00:00+02:00</updated>
    <id>https://can3p.github.io/links/2014/07/21/africa</id>
    <content type="html">&lt;p&gt;Article raises the discussion around current growth of african economies. The country are still mostly poor, but their economies are booming and western companies invest big money not just in mining but also in foods and manufacturing. Also the number of people with middle income raises and they are not the only one close to the ruling party. South Africa still performs the best, but Nigeria also shows rapid growth. They even launched several satellites which is almost unique amongst african countries. In the next century the whole region can become a driver of growth of the world.&lt;/p&gt;
</content>
  </entry>
  
 

</feed>
