<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Home on Rakhim's homepage</title><link>https://rakhim.org/</link><description>Recent content in Home on Rakhim's homepage</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://rakhim.org/index.xml" rel="self" type="application/rss+xml"/><item><title>The Yearly Run (sci-fi short story)</title><link>https://rakhim.org/the_yearly_run/</link><pubDate>Mon, 09 Nov 2020 00:00:00 +0000</pubDate><guid>https://rakhim.org/the_yearly_run/</guid><description>
&lt;style>article p {text-indent: 1.25em;}&lt;/style>
&lt;p>&amp;ldquo;Pochards&amp;rdquo;, was the first word spoken today. Emil looked up cautiously, keeping the corner of his eye on the road. Several dots on the dark-blue morning sky, no clouds. He was never quite sure whether dad could really recognize particular species of birds at this distance.&lt;/p>
&lt;p>&amp;ldquo;Are they flying to the village?&amp;rdquo;, he asked dad.&lt;/p>
&lt;p>&amp;ldquo;Yes, they are,&amp;rdquo; said Tapio. &amp;ldquo;We&amp;rsquo;ll meet them when we get back.&amp;rdquo;&lt;/p>
&lt;p>Their steel bicycles did a good job dampening the imperfections of the old concrete service road running hundreds of kilometers through the dead forest. Thick tree trunks in all directions, unnaturally straight like lampposts, echoing the similarly unnatural straightness of the road. The sun was up for hours, slowly crawling away from its origin in the north-east. This time of year it&amp;rsquo;s going to be bright for another twenty six hours, which should be enough for them to get to the old port ruins. The trip was not very important, casual even, which is why Tapio decided to take his teenage son along. With just 450 kilometers one way, the plan was to bike roughly three hundred on the first day, camp the &amp;ldquo;night&amp;rdquo;, get to the port, collect the inessential electronics scavenged by scouts months earlier, then bike back to the same camp before sunset, sleep and, finally, get back to the village.&lt;/p>
&lt;p>They were silent for few more hours, trying their best to keep a steady pace. Tapio knew this area very well, although, there wasn&amp;rsquo;t much to know. Terraformed in the cheapest possible way, the planet was mostly flattened, covered with service roads and genetically enlarged trees optimized for rapid oxygen production. This northern part reminded him of the Old Country from movies, yet he could not get rid of this intrusive thought that some new age obsessive-compulsive god straightened up all angles and fixed all organic imperfections, thus making the final strike in the soul of the old pagan deities.&lt;/p>
&lt;p>In all of two decades he&amp;rsquo;s been taking this service road to the old port, he never met another traveller. He&amp;rsquo;d be surprised to do so, as most of the settlers have left a long time ago, only the desperate, or hard-working, or insane stayed on this failed project of a world. Tapio&amp;rsquo;s ancestors settled on a barren land, too, and managed to thrive, but that was a simpler world. No barren lands were toxic then, no bad place was as bad as this. With batteries keeping good charge in both their bicycles, Tapio wasn&amp;rsquo;t worried too much about air filtration and temperatures in their suits, but still had occasional anxiety about the future.&lt;/p>
&lt;p>Their village was a good place, at least. Isolated from the outside with three levels of increasingly livable layers, it was home to a dozen families, all good, quiet, hard-working people. Scouts did a good job finding electronics, batteries, even canned food sometimes, and Tapio did his best on these runs to get them back home. A trip for inessentials was his favorite kind of trip. It&amp;rsquo;s the closest he could get to &amp;ldquo;outdoors relaxation&amp;rdquo;, an alien concept. It also helped his anxiety and hopelessness, or at least he believed it did.&lt;/p>
&lt;p>When the port was in operation, you could hear it from far away. But now it stood silently in a bald concrete patch of land surrounded by equally silent trees. None of the towers had any depth left in them, only skeleton-like inner shards and tubes visible, leaving sharp, strange shadows with the help of the bright sun on a cloudless sky.&lt;/p>
&lt;p>Packages left by scouts were marked with bright orange flags. Tapio took a thin pack with lightweight electronics from the top and opened it. Inside were fuses, extension cords, LED strips and g-drive-screens, which allowed for long-term ghost-recording storage and playback. Tapio took one out, connected to his eyepiece and pressed play. The screen filled with the green lawns of village land. Emil came into the frame, all busy getting ready for the trip.&lt;/p>
&lt;p>&amp;ldquo;Look,&amp;rdquo; said Tapio and turned the screen towards his son. Emil smiled.&lt;/p>
&lt;p>&amp;ldquo;Have you been recording me this whole time?&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Yes. Need data to test these drives. And it&amp;rsquo;s nice, too, right? Our first trip together.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Yeah. We can show mom.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Yeah.&amp;rdquo;&lt;/p>
&lt;p>The father was proud of his kid whose steady pace would&amp;rsquo;ve impressed a port officer. Emil put three marked boxes into a little trailer, then paused for a moment to investigate a long tube from one of the other boxes, then proceeded to load the rest. Tapio was standing nearby and watching silently.&lt;/p>
&lt;p>&amp;ldquo;Yeah, I saw them on the horizon,&amp;rdquo; said Emil. Tapio brushed off his haze and said &amp;ldquo;More birds&amp;rdquo; to himself quietly. The sun was now directly on top and it wasn&amp;rsquo;t a good idea to look up. Sharp tiny shadows and almost sterile air made the scene look like a video game, a simulation of too much detail, too realistic to be real. It was time to leave.&lt;/p>
&lt;p>&amp;ldquo;Dad, I can&amp;rsquo;t breathe, something is wrong&amp;rdquo;, said Emil. His battery failed silently, without any warning. Tapio knew this would happen, but didn&amp;rsquo;t want to think about, as if hoping the inevitable would somehow change its mind. He was calm, yet his hands and back were tight and sweaty. He turned off his headset and waited roughly four minutes while his son was panicking in total silence.&lt;/p>
&lt;p>Soon, when he turned the radio back on, both regained their calmness and were biking again. Turns out, both their batteries died at the same time, probably due to overexposure to some of the port&amp;rsquo;s legacy security systems. Normally, travelers take precautions, even though such problems are rare. Too excited, if that term can be applied to Tapio&amp;rsquo;s nordic cool, he forgot to discharge the capacitors. Probably. He wasn&amp;rsquo;t sure. It didn&amp;rsquo;t matter anyway.&lt;/p>
&lt;p>It wasn&amp;rsquo;t too bad though. The energy generated from their pedaling was just barely enough to keep life support systems running, as long as they booth keep their speed above 18 kilometers per hour. With about a hundred kilometers left until their campsite, they just need to keep going without stopping. Their tent is still there, so, perhaps, they could rest in turns, while one person keeps pedaling in place and the other resting in the tent. They both need rest, because there&amp;rsquo;s not much choice: they have to bike all the way to the village without stopping now. No real night camping, no food.&lt;/p>
&lt;p>The sight of the camping ground brought some reassurance to both travelers. Both knew it doesn&amp;rsquo;t make sense. Emil disconnected his suit from the bike and headed into the tent. Technically, cached energy would last up to five minutes. Tapio stopped near the tent, connected his bike to the tent&amp;rsquo;s in-port via a splitter, laid on the ground as comfortable as he could and started pedaling in the air.&lt;/p>
&lt;p>&amp;ldquo;Good?&amp;rdquo; he asked?&lt;/p>
&lt;p>&amp;ldquo;Yes, going up, steady,&amp;rdquo; replied Emil a whole minute later. Tapio thought he&amp;rsquo;s getting sloppy with this. But then, maybe it&amp;rsquo;s for the best.&lt;/p>
&lt;p>They left the campsite in the last hour of sunlight. Night was creeping in.&lt;/p>
&lt;p>Completely exhausted, running on low oxygen air for miles, Emil fell down with his bicycle into a pile of black soil, and his body disappeared. Father turned the radio off again, taking in the numbing silence. He couldn&amp;rsquo;t listen to the horrifying sounds of his son asphyxiating. These last minutes don&amp;rsquo;t matter, however &amp;ldquo;right&amp;rdquo; such vivid mental self-mutilation may have seemed to him earlier.&lt;/p>
&lt;p>Then Tapio turned his life support back on. The screens showed almost full charge and normal operation. There is no rush now. He sat down near the pile, looking into the flat slightly light horizon. Dry lake, this god damn place. Fucking useless planet&amp;hellip; He gently touched an old wooden cross on top of the pile so that it stands straight up again, then put a tiny portable light nearby. These lights flicker pleasantly when you fiddle with the charging port, just like an old-world candle. A blurred shadow of the cross danced violently on the surface of the pile. &amp;ldquo;End of playback. 34 hours 17 minutes elapsed. Repeat?&amp;rdquo; His eyepiece started blinking with a mechanical rhythm, in contrast with almost humane flickering &amp;ldquo;candles&amp;rdquo;. Tapio&amp;rsquo;s barely vocal &amp;ldquo;no&amp;rdquo; has stopped the ghost-recording projection and left him absolutely alone.&lt;/p>
&lt;p>Suvi didn&amp;rsquo;t come to meet her husband when he entered the house. She just kept soldering the keyboard in the main room, silent. Minutes later, when he changed and sat down near the fireplace, she asked &amp;ldquo;Everything alright?&amp;rdquo;. He didn&amp;rsquo;t answer, but she knew it was alright. It was the same last year, and the year before. They usually just sat in silence like that for an hour or so, then had dinner and the next day it was just normal again. But this time Tapio broke the silence: &amp;ldquo;I&amp;rsquo;m a bad father.&amp;rdquo;&lt;/p>
&lt;p>She stopped and said loudly &amp;ldquo;Stop it now. Nobody thinks that. No gods punish you. You can dwell and slowly die inside. Or you can be a good father.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Not to Emil,&amp;rdquo; he said.&lt;/p>
&lt;p>A quiet cry of a baby came from the backroom. &amp;ldquo;She is up. We need a proper crib now.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;I know. Will make one. Got a nice thin trunk from nearby. It&amp;rsquo;s in the workshop.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Good&amp;rdquo; she said.&lt;/p>
&lt;p>&amp;ldquo;Good&amp;rdquo; he echoed.&lt;/p>
&lt;p>The fireplace now seemed misplaced as morning sun projected bright rays onto the walls with relentless strength.&lt;/p>
&lt;div style="text-align: right; margin-top:3em;">&lt;small>(Rakhim Davletkaliyev, Helsinki, Finland, 2020)&lt;/small>&lt;/div></description></item><item><title>Blogging vs. blog setups</title><link>https://rakhim.org/honestly-undefined/19/</link><pubDate>Thu, 05 Nov 2020 00:00:00 +0000</pubDate><guid>https://rakhim.org/honestly-undefined/19/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/blogging.jpg"></description></item><item><title>The wrong good solution</title><link>https://rakhim.org/the-wrong-good-solution/</link><pubDate>Wed, 26 Aug 2020 09:53:30 +0000</pubDate><guid>https://rakhim.org/the-wrong-good-solution/</guid><description>
&lt;p>City planners know that you can&amp;rsquo;t solve traffic problems by building more roads. We could apply this line of thinking in our personal lives, too. I&amp;rsquo;ve written about &lt;a href="https://rakhim.org/small-battery-productivity/">small battery productivity&lt;/a> before, and the more I think about the pattern, there more missed opportunities I see.&lt;/p>
&lt;p>One day I thought that my smartphone&amp;rsquo;s small screen makes it too uncomfortable to read text. An obvious good solution is to get a larger smartphone, which is what I did. Now I&amp;rsquo;m convinced that was the wrong good solution. The right one would be to realize that no handheld device is appropriate for reading in the first place. Increasing the screen size allowed me to read more with less discomfort, but it&amp;rsquo;s still a bad way to read. I have optimized a bad solution, prolonged its lifespan.&lt;/p>
&lt;p>It&amp;rsquo;s like getting a bigger fork to eat soup. The correct solution is to ditch the fork and get a spoon.&lt;/p>
&lt;p>So I downsized to an even smaller smartphone than before. I&amp;rsquo;ve optimized to &lt;em>not&lt;/em> read at all, nor watch videos or do any other work. It&amp;rsquo;s uncomfortable to the point where I don&amp;rsquo;t see the point of struggling and just walk to my computer or wait until I get to one.&lt;/p>
&lt;p>Finally, I can taste the soup again.&lt;/p></description></item><item><title>Eventual Consistency (sci-fi short story)</title><link>https://rakhim.org/eventual-consistency/</link><pubDate>Sat, 01 Aug 2020 00:00:00 +0000</pubDate><guid>https://rakhim.org/eventual-consistency/</guid><description>
&lt;style>article p {text-indent: 1.25em;}&lt;/style>
&lt;p>They housed me in a small metallic room and gave me pants, a small towel and soap. Others from my group were close by in similar rooms, all in the same building where we woke up. Someone had asked if we could go outside, but I didn&amp;rsquo;t hear the reply.&lt;/p>
&lt;p>Few days later I started thinking and realized I haven&amp;rsquo;t been thinking before. It was sort of a second awakening, with a disappointing taste. Not thinking was pleasant, but you realize it only once it stops.&lt;/p>
&lt;p>The absurdity of it all stopped me from thinking I was in prison. Yet, nobody could leave, nobody had seen any sunlight. A network of tunnels connected all sleeping blocks, a big warehouse, a dining area and showers. Floors and walls hummed steadily: you could feel your skull vibrating when you roll away from the pillow at night. Perhaps, we were underground.&lt;/p>
&lt;p>We started talking to each other. Nobody had any recollection of the past or even their names. Both men and women of average size and weight, we spoke the same language and looked similar.&lt;/p>
&lt;p>&amp;lsquo;Human experimentation&amp;rsquo; and &amp;lsquo;we are dead&amp;rsquo; were common theories. I couldn&amp;rsquo;t take any of it seriously. Too plain to explain anything.&lt;/p>
&lt;p>The machines in the dining area generated enough food for everyone. Scheduled eating was the only thing giving structure to our days. At other times, most of us walked around, participated in shallow conversations and slept. No desire, no creativity, no lust. If I hadn&amp;rsquo;t seen blood and feces I wouldn&amp;rsquo;t have thought we were humans at all.&lt;/p>
&lt;p>The labyrinth of tunnels seemed endless. Some doors were unlocked and lead to other similar places, only configured differently. Same empty rooms, warehouses, dining areas and showers. I always came back before dinner, and one night I noticed an empty seat where there&amp;rsquo;s usually a bald guy. Nobody had seen him after.&lt;/p>
&lt;p>The rumors of people getting lost in the labyrinths became reality.&lt;/p>
&lt;p>Sometimes I went very far. Further away the configurations started to change slightly: some doors went directly into small rooms, others into warehouses of various sizes. Some walls had finger-sized holes leading into dark emptiness.&lt;br>
A month or two later I made a bag out of a towel, saved up enough food and left on an expedition. Smaller rooms had two doors only, so I just went through. Larger ones had several doors, so I marked the one I came from with a piece of food. The first hundred or so rooms were almost identical. Then I started noticing small differences. Sometimes I found small metallic things on the floor, like bolts of small washers. I collected them and used to mark doors. Sometimes rooms had a different lighting color, as if someone had replaced the light bulbs at some point.&lt;/p>
&lt;p>The first night I realized that not bringing a pillow was a mistake.&lt;/p>
&lt;p>The next day was the same. New rooms, new doors, new pieces on the floor. New lighting. One room quickly filled with metallic echoes of my scream. A neatly placed pieces of a human corpse lying in the corner. Bones and what&amp;rsquo;s left of hardened skin, covered with a piece of clothing similar to mine.&lt;/p>
&lt;p>Angrily, I kicked it, hoping to find some signs of&amp;hellip; something.&lt;/p>
&lt;p>I had food for around three days, so I had to start walking back on the second day. Risking it, I continued forward half of the third day, but it was all the same, hopeless and suffocating.&lt;/p>
&lt;hr>
&lt;p>I wasn&amp;rsquo;t the only one attempting to explore the labyrinths. Several people and a few groups tried other directions. Our lethargic discussions filled with dread as everybody shared the same experience. Nobody talked about dead bodies, but it was implied.&lt;/p>
&lt;p>One group has reported meeting another group, the one supposedly leaving a long time ago and just crossing their path accidentally. The earlier group was rough, angry.&lt;/p>
&lt;p>Months later some people starting remembering their lives, houses, children. I did not. Their stories seemed artificial. Their worlds did not intersect at all. No common lands, culture or even types of names.&lt;/p>
&lt;p>I met a guy who called himself Tuoni. He went on two sole expeditions: once for four days, once for almost two weeks. The deeper he went, the more dead bodies he found. None had any sings of struggle, all neatly put in a corner and covered with cloth.&lt;/p>
&lt;p>We went together once. The food was enough for about three weeks. After ten days we found a fresh body lying against the wall, arms around the chest, hugging itself for the last time. We moved it to a corner and covered with its shirt.&lt;br>
Two weeks later we were back, hungry and dreadful.&lt;/p>
&lt;p>For a long time nobody went. Slowly, a group was forming, the one that decided to go and not come back. Food was the only concern, otherwise, all parts of the labyrinth are as good as our base. Some of the staff operating the food generators joined the group. We were all in the same boat, it turns out. The staff was like us, only waken up earlier. The staff that woke them up was an older generation, some of them already dead of old age. Grandstaff and grandgrandstaff were rumored to have left the base for good, never seen again.&lt;/p>
&lt;p>I joined the first group of about fifteen people, with an ever-growing chain of food supply. We&amp;rsquo;d pass food up the chain with regular runners. A rotation system was put in place so that people don&amp;rsquo;t get crazy going back and forth all the time. Gradually, people from at start of the chain, closer to the food generators, were to move forward and replace the ones that got tired of exploration or lost or died.&lt;br>
With rumors of more and more people waking up, we had a steady supply of new blood, and in a few years nobody knew for certain how long the chain was and how far did the tip go. I always kept myself at the tip. Still mostly lethargic, we haven&amp;rsquo;t developed any rigid hierarchy or discipline. The whole organization was pretty inhuman, insect-like. Nobody was hopeful, nor depressed. We were just doing our jobs, however superficial.&lt;/p>
&lt;p>Every few years or so we&amp;rsquo;d intersect a chain of people without knowing for sure whether it&amp;rsquo;s our chain or a completely different group from a completely different origin. It didn&amp;rsquo;t matter much, at this point the goal was to continue as long as there&amp;rsquo;s new unmarked territory.&lt;/p>
&lt;hr>
&lt;p>One night I remembered my birth.&lt;/p>
&lt;p>The taste of metal, dry mouth, burning sensations somewhere below the waist, intermittent sounds of some machine. It was as if my body was turned on by a team of mechanics following strict instructions. Treating me like a machine, with complete disregard for humanity. Moments later I was able to ask the person above, &amp;ldquo;What?&amp;rdquo;&lt;/p>
&lt;p>The person seemed to understand me. Perhaps, something had happened, and this is a doctor. His clothes did not match my assumption. Old gray shirt did not inspire confidence.&lt;/p>
&lt;p>I seem to be regaining logic quickly, I thought.&lt;/p>
&lt;p>&amp;ldquo;Breathe, do not sit up,&amp;rdquo; he said. I ignored the advice and started to move what seemed like a set of cotton pillows — my useless hands. It took all of my energy to raise them slightly, and they fell on my chest with a senseless thump.&lt;br>
The burning sensation was steadily rising, and soon I felt salty beads of sweat running into my eyes. It was that pain-induced, sick, feverish kind of sweat. The dry mouth generated a creepy screaming sound, barely loud enough to make an echo. We were in some sort of warehouse or hangar.&lt;/p>
&lt;p>Nothing. Then I woke up again, who knows how many minutes or days later, with five plain-looking short guys standing around. &amp;ldquo;He&amp;rsquo;s up. Let&amp;rsquo;s go,&amp;rdquo; one of them said, and my bed started rolling forward. I felt a bit stronger this time and tried to tilt my head to look ahead. Horror had filled my reality as I stared at two black stumps in place of my legs. Violently bitten and burned, it looked too horrendous to be realistic. Dark, broken gaps followed upwards to under a gown. I was devoured like a tree by lightning. Physical pain has never been real, it was just a warm-up act before the true thing: fear. Fear had filled all of my world. I wasn&amp;rsquo;t in hell, I was hell.&lt;/p>
&lt;p>Woke up in a small room. Blue light coming from behind. My legs are fine, yet, I have no volume for hope. The bed started moving again, I went back into nothingness.&lt;/p>
&lt;hr>
&lt;p>Then it happened.&lt;/p>
&lt;p>We reached a large empty hall with gigantic doors and heavy levers. By pushing the levers we opened the doors and found, for the first time, something that didn&amp;rsquo;t resemble thousands of prior rooms. There were only six of us — a typical supertip expedition.&lt;/p>
&lt;p>For the first time since the birth I had sick salty sweat again. A mere fact of this place being different to absolutely everything I had ever seen made me shiver with a new breed of despair. Wasn&amp;rsquo;t I hoping for a change?&lt;br>
We closed the door behind us and the lights started to turn on, machines whirring. The ceilings and floors became screens filled with symbols and lines. Several chairs, slightly oversized for our bodies, moved up from underneath. Finally, one whole wall opened up to show us a blinding void of a starry sky. We reached the cockpit of this endless spaceship.&lt;/p>
&lt;p>The excitement quickly turned into hollow misery. Now we were certain there is no way out. It&amp;rsquo;s not a building we could escape, not an underground jail we could dig our way out of. It&amp;rsquo;s a true prison.&lt;/p>
&lt;p>The controls in the cockpit were undecipherable. Even worse: buttons, levers, knobs, screens — none of them did anything. It appeared like the ship was in a working condition, but we could not operate it.&lt;/p>
&lt;p>And so we lived. Eating, exploring new directions, trying more button combinations. Aging, dying, waking up more people. Our group of six was aging slower than the ones behind. Those who stayed at the food generators were long dead, as far as we knew, replaced by newly woken up.&lt;/p>
&lt;p>It went on until the day we saw another ship in the window. It just appeared and immediately connected with us via a video screen. We saw other people just like us, in a similar room, same clothes, same despair and emptiness. It was like looking into a dream mirror.&lt;/p>
&lt;p>As soon as the connection was established and before we could even realize what had happened, the cockpit lights went red and the screens started showing weapons being engaged. Some sort of a gun with a visible target locked on the other ship. In a moment, we saw their ship almost growing a limb — a huge brutalist section shifting out of the bottom and slowly rotating towards us. Nobody said anything yet, but everybody realized that we&amp;rsquo;re pointing deadly weapons at each other.&lt;/p>
&lt;p>&amp;ldquo;Please, don&amp;rsquo;t shoot, we&amp;rsquo;re not in control over here,&amp;rdquo; I said.&lt;/p>
&lt;p>&amp;ldquo;We have no idea what&amp;rsquo;s going on,&amp;rdquo; one out of a dozen faceless humans replied.&lt;br>
Could this be true? If they started like us, but then somehow managed to control the ship, it&amp;rsquo;s possible that they would try to trick us. They could be bad people, destroying ships like ours, taking our supplies. Why though? I imagine their ship is as barren as ours. Maybe, their food generators are broken. If they think we are in control of our ship, not disengaging the guns could be a good reason to destroy us.&lt;/p>
&lt;p>&amp;ldquo;If neither of us can control the guns, neither can change anything,&amp;rdquo; I said. It sounds wise.&lt;/p>
&lt;p>&amp;ldquo;What do we do?&amp;rdquo; they said.&lt;/p>
&lt;p>&amp;ldquo;We&amp;rsquo;re not in control&amp;hellip;&amp;rdquo;, I repeated. Then it all ended.&lt;/p>
&lt;hr>
&lt;p>&amp;ldquo;That is the most cruel, most inhumane and existentially horrific idea I&amp;rsquo;ve ever heard!&amp;rdquo; said Manager The Third. Others nodded with that kind of &amp;ldquo;Yes, but&amp;hellip;&amp;rdquo; kind of nod. A long presentation of project &amp;lsquo;Eventual Consistency&amp;rsquo; was finished, room filled with nods like dots of an ellipsis&amp;hellip;&lt;/p>
&lt;p>&amp;ldquo;Let&amp;rsquo;s forget for a second the fundamental problem of endlessly decreasing entropy in a relatively small region of The Universe. You know that non-entropy is a limited resource, right? But hey, let&amp;rsquo;s forget about it. Let&amp;rsquo;s just talk efficiency. How in the world are you going to justify the idea of sending out a myriad of extremely energy-hungry ships that support millions of human-like entities, and that&amp;rsquo;s before the fact that most of the ships will be destroying each other, endlessly, and rebuilding themselves, endlessly, while consuming the non-entropy of The Universe. Is your plan to accelerate the heat death?&amp;rdquo;&lt;br>
New wave of nods.&lt;/p>
&lt;p>&amp;ldquo;I will address your points in order,&amp;rdquo; said Presenter. &amp;ldquo;First, yes, non-entropy is limited in a universe, but remember where the self-healing ship technology comes from. Not this world. We don&amp;rsquo;t know how it works, but we assume that the energy does not come from this universe, either.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;That claim is not proven,&amp;rdquo; said someone from the long table.&lt;/p>
&lt;p>&amp;ldquo;Incorrect. That claim is not provable. Big difference. We can&amp;rsquo;t prove it comes from this universe, and the only way to prove it is to literally monitor every particle, which isn&amp;rsquo;t feasible at this stage of development. We&amp;rsquo;re going to continue under the assumption that the energy comes from outside, and I take full responsibility for this.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;You take full responsibility for this universe?&amp;rdquo; said another voice.&lt;/p>
&lt;p>&amp;ldquo;After all, that is my job. If I make a mistake, we will inevitably move onto another project. Now, with that out of the way, the heat death and energy consumption are no longer relevant discussions. I understand that your main concerns, Manager, is time efficiency and cruelty. I can&amp;rsquo;t speak about cruelty: consciousness is not my area of expertise. As for time efficiency, this is truly the best way our models yield. The goal is consistent exploration of all regions of space. But we don&amp;rsquo;t know what&amp;rsquo;s out there. We cannot plan, we do not possess enough computing power to account for all possibilities. The only rational alternative is to postpone computation, postpone any work related to adaptation to unknown conditions.&lt;/p>
&lt;p>&amp;ldquo;However we dislike the organic nature of evolution, it must be applied to semi-organic systems like space and semi-organic goals like exploration. It&amp;rsquo;d be foolish not to use this weird alien resource we&amp;rsquo;d been gifted. Let the ships move in random directions. Let the organic entities on board evolve into true pilots. Let them fight for resources. Let those who can’t evolve die and be reborn, better. Or, I should say, different.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;How long until we see results?&amp;rdquo; said Manager The Third.&lt;/p>
&lt;p>&amp;ldquo;With a million ships right away and an odd number of autonomous factories in place, it&amp;rsquo;ll take a billion years in local time to completely encompass what&amp;rsquo;s reachable. Each ship is expected to die and be reborn at least ten million times. But we don&amp;rsquo;t need to wait, that&amp;rsquo;s the beauty. We can hibernate here or leave entirely, checking in a few days later.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;I still can&amp;rsquo;t bear the burden of that much conscious pain. All these people&amp;hellip;&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Organic entities, Manager. They are not people.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;I&amp;rsquo;d argue about that if I had the energy. They might be more people than we are.&amp;rdquo;&lt;/p>
&lt;p>No nods this time.&lt;/p>
&lt;p>&amp;ldquo;Regardless, these entities are created and kept alive by the ships. We&amp;rsquo;re not inflicting pain on entities, we&amp;rsquo;re simply not disallowing entities to be created. Big difference.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;So you say. Would you take full responsibility for that, too?&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Nothing to take here, Manager. As I said, this is not my area of expertise. As far as the legislation goes, as you well know, the things created by alien ships are alien and not of our concern.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Well. I have no better alternative, do I?&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Seems that way, Manager. Do we proceed?&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Yes. Let&amp;rsquo;s try and see. Hopefully, next week another team will have a better idea.&amp;rdquo;&lt;/p>
&lt;p>&amp;ldquo;Not if our idea succeeds first.&amp;rdquo;&lt;/p>
&lt;hr>
&lt;p>They housed me in a small metallic room and gave me pants, a small towel and soap. Others from my group were close by in similar rooms, all in the same building where we woke up. Someone had asked if we could go outside, but I didn&amp;rsquo;t hear the reply.&lt;/p>
&lt;p>All these people&amp;hellip; If they remember as little as I do, then I can be whoever I want. Their King even. Look at them. Barely walking. Lost to uncertainty. Weak.&lt;br>
This is what freedom is, I realized. Where they see nothing, I see anything.&lt;/p>
&lt;p>Putting my head on the pillow, I feel my skull vibrating off of the floor. I can feel the enormous power of this place, whatever this is.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter what it is, I think to myself, and fall asleep, smiling.&lt;/p>
&lt;div style="text-align: right; margin-top:3em;">&lt;small>(Rakhim Davletkaliyev, Helsinki, Finland, 2020)&lt;/small>&lt;/div>
&lt;hr>
&lt;div>(A Russian translation is available &lt;a href="https://rakh.im/model_soglasovannosti/">here&lt;/a>.)&lt;/div></description></item><item><title>Small battery productivity</title><link>https://rakhim.org/small-battery-productivity/</link><pubDate>Wed, 29 Jul 2020 00:00:00 +0000</pubDate><guid>https://rakhim.org/small-battery-productivity/</guid><description>
&lt;p>A Scottish writer Matt Gemmell &lt;a href="http://mattgemmell.com/small-screen-productivity/">described&lt;/a> a technique of “small screen productivity”. He was talking about either a small laptop or a tablet which doesn’t allow him to see many contexts simultaneously, thus boosting creative output. One window, small screen, no distractions.&lt;/p>
&lt;p>I dislike laptops, but can appreciate the benefits of small screens. However, lately I’ve been enjoying another limitation: a small battery.&lt;/p>
&lt;p>I got a used Thinkpad x230. Its old battery is not great, and my choice of OS (OpenBSD) made it even worse. I get maybe 2 hours of text editing with zero other apps, no sound, WiFi disabled and screen brightness at 50%. A few days ago I traveled into the city without a charger and managed to write quite a lot. To save battery, I first outline everything in a paper notebook, and when I feel ready, I commit to the laptop.&lt;/p>
&lt;p>I know from experience how hard it is to maintain limitations like this in the long run. However beneficial, we always want things to be quantifiably better: longer hours, larger resolutions, bigger storage. I won’t plan to change my life around this approach. I don’t believe in anything like that anymore. It seemed to work this time, so I’ll ride the wave while I can.&lt;/p></description></item><item><title>Fundamental Attribution of Ability Error</title><link>https://rakhim.org/fundamental-attribution-of-ability-error/</link><pubDate>Wed, 01 Jul 2020 18:29:00 +0200</pubDate><guid>https://rakhim.org/fundamental-attribution-of-ability-error/</guid><description>
&lt;p>There&amp;rsquo;s this thing called &lt;strong>fundamental attribution error:&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>In &lt;a href="https://en.wikipedia.org/wiki/Social_psychology">social psychology&lt;/a>, &lt;strong>fundamental attribution error&lt;/strong> (&lt;strong>FAE&lt;/strong>), also known as &lt;strong>correspondence bias&lt;/strong> or &lt;strong>attribution effect&lt;/strong>, is the tendency for people to under-emphasize situational explanations for an individual&amp;rsquo;s observed behavior while over-emphasizing dispositional and personality-based explanations for their behavior. This effect has been described as &amp;ldquo;the tendency to believe that what people do reflects who they are&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>Basically, when you make a mistake — it&amp;rsquo;s due to some good reason, but when someone else makes a mistake — it&amp;rsquo;s because they are bad people who make mistakes.&lt;/p>
&lt;p>I was thinking about this pattern of observing reality when reading other people&amp;rsquo;s blog posts describing life situations, technical setups, whatever. Let&amp;rsquo;s say I&amp;rsquo;m reading about some developer&amp;rsquo;s perfect programming environment setup. They share all the steps involved in creating this extremely complex system with automation, lots of inter-connected software, scripts and hardware addons. In the end, they conclude with how happy they are about the setup, how it helped their productivity and everything is peachy.&lt;/p>
&lt;p>Inspired, I try to reproduce a &lt;em>portion&lt;/em> of the described system, but quickly face the reality: things aren&amp;rsquo;t connecting as nicely as I expected, bugs and compatibility issues force me to write dirty hacks, and in the end the whole thing is just waiting to break.&lt;/p>
&lt;p>I&amp;rsquo;m not doing this anymore nowadays. Every time I see a cool setup, be it OS, Terminal, Emacs, Vim or even some iOS automations, I just think how hard it is to maintain all of that and go to my Emacs config looking for something new to disable.&lt;/p>
&lt;p>But for the longest time I seem to have been making a Fundamental Attribution of Ability Error. I was thinking that people&amp;rsquo;s setups are in fact perfect. That there&amp;rsquo;s something wrong with me, that my attempts to build a convoluted architecture fail because I&amp;rsquo;m a bad programmer. With experience and after talking to a lot of people, I realized how much of a facade many of us create when describing our creations.&lt;/p>
&lt;p>No, your Emacs setup does not have &amp;ldquo;zero issues&amp;rdquo;. Sorry, I don&amp;rsquo;t believe you. It has issues and it breaks and you fix it sometimes. No, your Karabiner + Hammerspoon + AppleScripts + KeyboardMaestro setup with two hundred custom keyboard shortcuts is not &amp;ldquo;maintenance free&amp;rdquo;. No, your dotfiles repo with Ansible script does not guarantee a one-command-setup on a new machine.&lt;/p>
&lt;p>As a result, I&amp;rsquo;m finding myself more and more conservative and pragmatic. &amp;lsquo;Vanilla&amp;rsquo; and &amp;lsquo;default are my favorite words. I don&amp;rsquo;t get into zsh or fish, even though I understand the benefits over Bash. Bash is okay. My small config with a bunch of aliases had served me well. I love Doom Emacs, but I come back to my minimalist personal configuration with continuously shrinking number of customizations. I use Make and Bash-scripts for building.&lt;/p>
&lt;p>Mythical &amp;ldquo;reproducible configuration&amp;rdquo; does not exist. I mean, it does, with scripts, Ansible, Docker or nixOS, sure, but then you have to constantly maintain the thing that creates maintenance-free environments. Is there a catch here?..&lt;/p></description></item><item><title>It's all just fashion shows</title><link>https://rakhim.org/its-all-just-fashion-shows/</link><pubDate>Fri, 26 Jun 2020 18:29:00 +0200</pubDate><guid>https://rakhim.org/its-all-just-fashion-shows/</guid><description>
&lt;p>I always thought these fashion runway shows are kind of silly.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/2020/06/Modeling.jpg"
alt="Are you guys okay?..">&lt;figcaption>
&lt;p>Are you guys okay?..&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>I just don&amp;rsquo;t want to deal with this stuff in any way. Luckily, I can ignore it for the most part. Although, random changes in &amp;ldquo;trendiness&amp;rdquo; affects my personal ability to buy a simple t-shirt or a hoodie without spending half an hour in a store (the last thing I want to do in life).&lt;/p>
&lt;p>But I can&amp;rsquo;t isolate myself from these kind of fashion runway shows:&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/2020/06/macos-big-sur-review-pic-5.jpg"
alt="New macOS, new style of icons, new trend in UI design">&lt;figcaption>
&lt;p>New macOS, new style of icons, new trend in UI design&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/2020/06/macos-big-sur-3-of-22.jpg"
alt="This is better than before, right? RIGHT?!">&lt;figcaption>
&lt;p>This is better than before, right? RIGHT?!&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>It&amp;rsquo;s as if the industry forces me to throw away all my clothes and get used to new things, until it inevitably changes again in a few years. I don&amp;rsquo;t want to. Not because older icons are better, but because the change itself has very little value. The new UI style is not making my computing experience any better or faster or simpler. In fact, it&amp;rsquo;s making it worse: getting used to new design takes time. Who&amp;rsquo;s gonna pay for it?&lt;/p>
&lt;p>The main benefactors of constant UI changes are the designers and product managers. I sometimes feel like we&amp;rsquo;re just donating our attention and energy so that they can keep their jobs. I know it sounds awful, and I am sorry, but this is how I feel. Collectively, we somehow decided this kind of taxation for social security is okay.&lt;/p>
&lt;p>Now, these things could be esthetically pleasing and subjectively beautiful. That&amp;rsquo;s cool! Just like art. But again, I can ignore art and it doesn&amp;rsquo;t affect me or my ability to work. And I am not forced to support art and artists journeys.&lt;/p>
&lt;p>Nothing really changed in desktop computing fundamentally since, heck, Windows 3. For 30 years we have icons, folders, windows, bars, menus and the mouse. Fundamentally, we just get new dresses every year.&lt;/p>
&lt;p>At least the music is sometimes nice at these shows&amp;hellip;&lt;/p></description></item><item><title>Weird Finnish elative case</title><link>https://rakhim.org/weird-finnish-elative-case/</link><pubDate>Mon, 01 Jun 2020 18:29:00 +0200</pubDate><guid>https://rakhim.org/weird-finnish-elative-case/</guid><description>
&lt;p>Finnish language has &lt;a href="https://en.wikipedia.org/wiki/Finnish_noun_cases">15 noun cases&lt;/a> which pack a lot of expressive power. For example, let&amp;rsquo;s take the word &amp;ldquo;kissa&amp;rdquo; (cat). Here are some of the cases:&lt;/p>
&lt;ul>
&lt;li>kissa&lt;strong>n&lt;/strong> — of cat (kissan koko; the size of the cat)&lt;/li>
&lt;li>kissa&lt;strong>ssa&lt;/strong> — in cat (hiiri on kissassa; the mouse is in the cat)&lt;/li>
&lt;li>kissa&lt;strong>sta&lt;/strong> — from inside the cat (kakka tuli kissasta; poop came out of the cat)&lt;/li>
&lt;li>kissa&lt;strong>na&lt;/strong> — as cat&lt;/li>
&lt;li>kissa&lt;strong>tta&lt;/strong> — without cat&lt;/li>
&lt;/ul>
&lt;p>This third one is the &lt;a href="https://en.wikipedia.org/wiki/Elative_case">elative case&lt;/a>. It&amp;rsquo;s one of the locative cases which are used to indicate physical direction, along with inessive (kisa&lt;strong>ssa&lt;/strong>).&lt;/p>
&lt;p>So, you learn this stuff and in your mind the ending -sta becomes associated with &amp;ldquo;from&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>Apteekista — from the pharmacy&lt;/li>
&lt;li>Maasta — from land&lt;/li>
&lt;li>Talosta — from the house&lt;/li>
&lt;/ul>
&lt;p>And then you see this:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>tästä syystä&lt;/strong>&lt;br>
for this reason&lt;/p>
&lt;/blockquote>
&lt;p>It means &amp;ldquo;for this reason&amp;rdquo;. Notice how both &amp;ldquo;this&amp;rdquo; and &amp;ldquo;reason&amp;rdquo; get the illative ending. This is something pretty alien to English speakers, but usual for languages such as Finnish or Russian (which are not related, but share this property). No matter how many words describe the &amp;ldquo;reason&amp;rdquo;, they all should be in the same case:&lt;/p>
&lt;blockquote>
&lt;p>Tä&lt;strong>stä&lt;/strong> suure&lt;strong>sta&lt;/strong> tärkeä&lt;strong>stä&lt;/strong> mielenkiintoise&lt;strong>sta&lt;/strong> syy&lt;strong>stä&lt;/strong>&lt;br>
For this big important interesting reason.&lt;/p>
&lt;/blockquote>
&lt;p>Anyway, you look at &amp;ldquo;tästä syystä&amp;rdquo; and think, well, &amp;ldquo;from a reason&amp;rdquo; — it kinda makes sense. In English, &amp;ldquo;for a reason&amp;rdquo; is actually pretty weird. We&amp;rsquo;re used to it, but something like &amp;ldquo;by a reason&amp;rdquo; would make more sense than &amp;ldquo;for&amp;rdquo;. So, &amp;ldquo;from a reason&amp;rdquo; is actually alright.&lt;/p>
&lt;p>But then you see this:&lt;/p>
&lt;blockquote>
&lt;p>Pidän kissasta&lt;br>
I like the cat&lt;/p>
&lt;/blockquote>
&lt;p>Wait, what? Literally, &lt;em>&amp;ldquo;I like from cat&amp;rdquo;&lt;/em>?! So, illative is used to indicate the object of an action (to like, to love, to hate, to know, etc). Why?!&lt;/p>
&lt;p>And it goes deeper!&lt;/p>
&lt;blockquote>
&lt;p>Puhu kissasta&lt;br>
To talk about the cat&lt;/p>
&lt;/blockquote>
&lt;p>&amp;ldquo;About&amp;rdquo; is the same as &amp;ldquo;from&amp;rdquo;!&lt;/p>
&lt;p>This property is one of many weird things I find about Finnish as I learn this wonderfully strange language.&lt;/p></description></item><item><title>Summary of Truth about Types, a talk by Bartosz Milewski</title><link>https://rakhim.org/summary-of-truth-about-types-a-talk-by-bartosz-milewski/</link><pubDate>Mon, 10 Feb 2020 18:29:00 +0200</pubDate><guid>https://rakhim.org/summary-of-truth-about-types-a-talk-by-bartosz-milewski/</guid><description>
&lt;script src="{{ "js/mathjax-config.js" | absURL }}">&lt;/script>
&lt;script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS_HTML">&lt;/script>
&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>Category theory abstracts many branches of math. You can describe topology, logic, Banach spaces and other concepts in terms of category theory. Why is it so? Does category theory describe the nature, the intrinsic reality, and we happened to discover it like physicists discover the laws of the universe?&lt;/p>
&lt;p>The main idea of category theory is composition. This is why it is so relevant in programming. Composition is the essence of category theory.&lt;/p>
&lt;p>Is composability intrinsic in nature? It seems like that on a larger scale, and classical physics shows how everything is neatly composable. The ancient idea of atoms is the idea of perfect composability: things are composed with indivisible elementary particles, and the higher you go, the more composable everything is. Atoms make molecules, molecules make objects, object make bigger objects and interact using laws that are themselves composable. Newtonian mechanics describes objects in the universe that follow the rules of interaction that are clearly affecting each other, acting like independent agents.&lt;/p>
&lt;p>But the deeper you go, the less composable it becomes. Two quantum particles don&amp;rsquo;t interact in a simply composable fashion, their wave functions do not just affect each other in a stable manner. Two quantum particles near one another start to behave like a special two-particle state, not two wave functions. This is why quantum theory is so hard to describe and this is probably why physicists struggle with these theories. They go against our intuition of a composable universe.&lt;/p>
&lt;p>So, maybe composability is not intrinsic in nature, maybe it is intrinsic for our limited monkey brains. We need composability in order to deal with the complex reality. Maybe, higher super-beings wouldn&amp;rsquo;t invent category theory at all.&lt;/p>
&lt;h2 id="truth">Truth&lt;/h2>
&lt;p>In logic, there is an axiom that basically says &amp;ldquo;truth is true&amp;rdquo;, and you don&amp;rsquo;t need to prove it.&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
\frac{}{\top true}\top_I&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>In type theory, it corresponds to unit type:&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
():()&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>And in category theory it corresponds to the terminal object, which we will cover later.&lt;/p>
&lt;p>Three things look different, but they are actually just different notations for the same concept.&lt;/p>
&lt;p>As for proofs, in logic you&amp;rsquo;d prove a proposition A by providing some statements:&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
\frac{[&amp;hellip;]}{A}&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>In type theory, you&amp;rsquo;d prove that type &lt;strong>A&lt;/strong> is inhabited (has members):&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
\Gamma\vdash x : A&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>In the context of categories, a proof would be a morphism from terminal object:&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
1\rightarrow A&lt;br>
\end{eqnarray}&lt;/p>
&lt;h2 id="category-theory">Category theory&lt;/h2>
&lt;p>A &lt;a href="https://en.wikipedia.org/wiki/Category%5Ftheory">category&lt;/a> is a collection of objects and arrows.&lt;/p>
&lt;p>Similar to a directed &lt;a href="https://en.wikipedia.org/wiki/Graph%5Ftheory">graph&lt;/a>. Nodes are called &lt;strong>objects&lt;/strong> (a, b, c,&amp;hellip;). Arrows between objects are called &lt;strong>morphisms&lt;/strong>: &lt;code>f :: a -&amp;gt; b&lt;/code>.&lt;/p>
&lt;h3 id="composability">Composability&lt;/h3>
&lt;p>Arrows are composable:&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
f :: a \rightarrow b&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
g :: b \rightarrow c&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
g \circ f :: a \rightarrow c&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>The notation \(g \circ f\) means &amp;ldquo;apply &lt;code>g&lt;/code> after &lt;code>f&lt;/code>&amp;rdquo;.&lt;/p>
&lt;p>So, composition is associative.&lt;/p>
&lt;h3 id="identity-arrows">Identity arrows&lt;/h3>
&lt;p>There always exist identity arrows that point to the same object:&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
id_a :: a \rightarrow a&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
id \circ f = f&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
g \circ id = g&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>There could be multiple arrows from &lt;code>a&lt;/code> to &lt;code>a&lt;/code>, but at least one is identity.&lt;/p>
&lt;h3 id="set">Set&lt;/h3>
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Set%5Ftheory">Set&lt;/a> is an example of a category. Set is a category in which:&lt;/p>
&lt;ul>
&lt;li>Objects are sets&lt;/li>
&lt;li>Arrows are functions&lt;/li>
&lt;/ul>
&lt;p>In category theory, you cannot &amp;ldquo;look&amp;rdquo; inside objects, you only care about their connections. So, two sets are considered to be the same or different based on the arrows, but not the contents.&lt;/p>
&lt;h2 id="universal-constructions-in-category-theory">Universal constructions in category theory&lt;/h2>
&lt;h3 id="initial-object">Initial object&lt;/h3>
&lt;p>An &lt;a href="https://en.wikipedia.org/wiki/Initial%5Fand%5Fterminal%5Fobjects">initial object&lt;/a> is an object that has a unique arrow to all other objects. Only one arrow per object.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/truth_about_types/initial_object.png">
&lt;/figure>
&lt;p>In Set, the equivalent would be an empty set \(\varnothing\). A function \(varnothing \rightarrow a\) is a function that maps an element of empty set to any other element of other set. This doesn&amp;rsquo;t make sense: an element of empty set? But this is just an explanation in terms of set theory, and while it doesn&amp;rsquo;t make sense, we can think about this nevertheless, as of &amp;ldquo;objects defined by their arrow property&amp;rdquo;.&lt;/p>
&lt;p>You can always create a function from an empty set, but you&amp;rsquo;ll never be able to call this function. In the abstract, in vacuum this is true.&lt;/p>
&lt;p>In Haskell there&amp;rsquo;s Absurd function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-haskell" data-lang="haskell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#0b0;font-weight:bold">Absurd&lt;/span> &lt;span style="color:#a2f;font-weight:bold">::&lt;/span> &lt;span style="color:#0b0;font-weight:bold">Void&lt;/span> &lt;span style="color:#a2f;font-weight:bold">-&amp;gt;&lt;/span> a
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Which is a reference to &amp;ldquo;ad absurdum&amp;rdquo;: from falsehood, you can derive anythings. &amp;ldquo;If pigs can fly, then I am the president&amp;rdquo;.&lt;/p>
&lt;p>&lt;code>Void&lt;/code> here represents a type &lt;code>Void&lt;/code>, an uninhabited type. This is not the same void-type we use in C/C++.&lt;/p>
&lt;h3 id="terminal-object">Terminal object&lt;/h3>
&lt;p>The inverse of initial object is the &lt;a href="https://en.wikipedia.org/wiki/Initial%5Fand%5Fterminal%5Fobjects">terminal object&lt;/a>. An object with a unique arrow from all objects.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/truth_about_types/terminal_object.png">
&lt;/figure>
&lt;p>In set, it&amp;rsquo;s a singleton set. Unique function: for every element of set &lt;strong>a&lt;/strong> return the single element of the singleton set. In Haskell, the unit function ignores the argument and returns the one element from the set. The unit type &lt;code>()&lt;/code> has one element &lt;code>()&lt;/code>.&lt;/p>
&lt;p>Type:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-haskell" data-lang="haskell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#00a000">unit&lt;/span> &lt;span style="color:#a2f;font-weight:bold">::&lt;/span> a &lt;span style="color:#a2f;font-weight:bold">-&amp;gt;&lt;/span> &lt;span style="color:#a2f">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Element of this type:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-haskell" data-lang="haskell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#00a000">unit&lt;/span> &lt;span style="color:#a2f;font-weight:bold">_&lt;/span> &lt;span style="color:#a2f;font-weight:bold">-&amp;gt;&lt;/span> &lt;span style="color:#a2f">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If there are many singleton sets, they are isomorphic (i.e., a set of one apple is the same as a set of one orange).&lt;/p>
&lt;h2 id="product">Product&lt;/h2>
&lt;p>We need these universal constructions because they provide a way of defining things (since you cannot look inside the objects). One of the things that can be defined is the product.&lt;/p>
&lt;ul>
&lt;li>&lt;code>c&lt;/code> is a product of &lt;code>a&lt;/code> and &lt;code>b&lt;/code>&lt;/li>
&lt;li>two arrows &lt;code>p&lt;/code> and &lt;code>q&lt;/code> (projections)&lt;/li>
&lt;li>in Set: Cartesian product, pairs of elements&lt;/li>
&lt;li>in logic: logical &lt;code>AND&lt;/code> (conjuction elimination)&lt;/li>
&lt;/ul>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/truth_about_types/product.png">
&lt;/figure>
&lt;p>The only thing we know is that there are two morphisms. If we translate this into logic, it corresponds exactly to the elimination (AND). If &lt;code>a AND b&lt;/code> is true, then &lt;code>a&lt;/code> is true:&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
\frac{a \wedge b}{a}&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>If &lt;code>a AND b&lt;/code> is true, &lt;code>b&lt;/code> is true:&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
\frac{a \wedge b}{b}&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>This corresponds to morphisms like so:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-nil" data-lang="nil">c :: (a, b)
p(a, b) = a
q(a, b) = b
&lt;/code>&lt;/pre>&lt;p>There can be many objects like &lt;code>c&lt;/code> with such two projections. Only one could the product. Which one? We need to pinpoint the correct &lt;code>c&lt;/code> that corresponds to the Cartesian product. We need a way to evaluate the instance of this pattern of projections.&lt;/p>
&lt;p>If we can get all similar patterns in the category, we need a way to distinguish the &amp;ldquo;top&amp;rdquo; one, the one that matches the the best. That one would be the product.&lt;/p>
&lt;p>Here we have two examples of such pattern:&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/truth_about_types/candidate.png">
&lt;/figure>
&lt;p>&lt;code>c'&lt;/code> is a candidate product and it has &lt;code>p'&lt;/code> and &lt;code>q'&lt;/code> projections. &lt;code>c&lt;/code> is better if there is a unique arrow (morphism) &lt;code>m&lt;/code> from &lt;code>c'&lt;/code> to &lt;code>c&lt;/code> that fulfils these conditions:&lt;/p>
&lt;p>\begin{equation}&lt;br>
\begin{aligned}&lt;br>
m :: c&amp;rsquo; \rightarrow c \\\&lt;br>
p&amp;rsquo; = p \circ m \\\&lt;br>
q&amp;rsquo; = q \circ m&lt;br>
\end{aligned}&lt;br>
\end{equation}&lt;/p>
&lt;p>Both of these have the common factor &lt;code>m&lt;/code> (but instead of multiplication you have composition). So they are not really elementary, since we can factor out a common &lt;code>m&lt;/code> out of them. This makes &lt;code>c&lt;/code> better (&amp;ldquo;more pure&amp;rdquo;).&lt;/p>
&lt;p>This forms ranking among candidates. In some cases you can indeed use ranking to get the unique best option and that will be the product. Not all categories would have this.&lt;/p>
&lt;p>Again, there is a connection to logic:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-nil" data-lang="nil">if a follows from c&amp;#39; (p&amp;#39;)
and b follows from c&amp;#39; (q&amp;#39;)
a ^ b (c) follows from c&amp;#39; (m)
&lt;/code>&lt;/pre>&lt;h2 id="function-object">Function object&lt;/h2>
&lt;p>Another universal construction.&lt;/p>
&lt;p>What is type function in Haskell? It&amp;rsquo;s not really a morphism. Morphism is not an object. A set of morphisms between two objects corresponds to type &lt;code>a-&amp;gt;b&lt;/code>. Is there an object in the category that corresponds to the set of morphisms? If so, it&amp;rsquo;s called the Function Object.&lt;/p>
&lt;p>In order to define the function object (in Haskell, it&amp;rsquo;s &lt;code>a-&amp;gt;b&lt;/code>, which is a type), you have to have products.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/truth_about_types/function.png">
&lt;/figure>
&lt;p>If you take a function &lt;code>a-&amp;gt;b&lt;/code> and apply argument &lt;code>a&lt;/code>, you get &lt;code>b&lt;/code>. Similar to product, if there is another candidate object &lt;code>z&lt;/code>, that evaluates to the same &lt;code>b&lt;/code>, and you can find unique morphism &lt;code>h&lt;/code> then you can factor it out and prove that &lt;code>a-&amp;gt;b&lt;/code> candidate is the best.&lt;/p>
&lt;p>This corresponds to modus ponens in proof theory.&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
\frac{(a=&amp;gt;b) \wedge a}{b}&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>If you have a function from &lt;code>a&lt;/code> to &lt;code>b&lt;/code> &lt;strong>and&lt;/strong> &lt;code>a&lt;/code> is true, then &lt;code>b&lt;/code> is also true.&lt;/p>
&lt;h2 id="negation">Negation&lt;/h2>
&lt;p>Another thing you can construct is negation. &lt;code>Not A&lt;/code> corresponds to an arrow (morphism) &lt;code>A -&amp;gt; Void&lt;/code>. Why is this a negation?&lt;/p>
&lt;p>In the context of type theory:&lt;/p>
&lt;ul>
&lt;li>If &lt;code>A&lt;/code> is inhabited (has elements), then &lt;code>A-&amp;gt;Void&lt;/code> is not inhabited. Because otherwise you&amp;rsquo;d be able to create an element of &lt;code>Void&lt;/code>, which suppose to have no elements.&lt;/li>
&lt;li>If &lt;code>A&lt;/code> is not inhabited (is Void), then &lt;code>Void-&amp;gt;Void&lt;/code> is \(id_{void}\).&lt;/li>
&lt;/ul>
&lt;h2 id="cartesian-closed-category">Cartesian Closed Category&lt;/h2>
&lt;p>Combining these three things, we get a Cartesian Closed Category (CCC):&lt;/p>
&lt;ul>
&lt;li>Has all products (Cartesian)&lt;/li>
&lt;li>Has all function objects (exponentials) (this makes it closed)&lt;/li>
&lt;li>Has terminal object (nullary product)&lt;/li>
&lt;/ul>
&lt;h2 id="curry-howard-lambek">Curry-Howard-Lambek&lt;/h2>
&lt;p>This is an extension of Curry-Howard isomorphism. CCC is a model for simply typed lambda calculus. In addition to the fact that simply typed lambda calculus can be a model for logic.&lt;/p>
&lt;ul>
&lt;li>Objects are types&lt;/li>
&lt;li>Morphisms are terms (in logic/programming)&lt;/li>
&lt;li>Environment \(\Gamma\) is a prodict of judgements &lt;code>a:A&lt;/code>&lt;/li>
&lt;li>Empty environment is &lt;code>() : ()&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="logical-universes">Logical universes&lt;/h2>
&lt;p>There are two major kinds of logic: Classical and Intuitionistic.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/truth_about_types/logics.png">
&lt;/figure>
&lt;p>In classical logic, any proposition is either true or false. But then Kurt Gödel comes along and says &amp;ldquo;no, there are some statements that are neither false nor true&amp;rdquo;, which leads to intuitionistic logic where a statement can be true, can be false, but you can also have some gray area where you ask what &amp;ldquo;is&amp;rdquo; means.&lt;/p>
&lt;h3 id="intuitionistic-logic">Intuitionistic logic&lt;/h3>
&lt;ul>
&lt;li>No LEM: law of excluded middle (either A or not A). &lt;code>A | (A-&amp;gt;Void)&lt;/code> is not provable.&lt;/li>
&lt;li>We cannot eliminate double negation: &lt;code>Not Not A&lt;/code> is not the same as &lt;code>A&lt;/code> (&lt;code>(A-&amp;gt;Void)-&amp;gt;Void&lt;/code> is not the same as &lt;code>A&lt;/code>)&lt;/li>
&lt;/ul>
&lt;p>Curry-Howard equivalence says that simple typed lambda calculus is equivalent to intuitionistic logic. You cannot prove LEM or double negation using lambda calculus.&lt;/p>
&lt;p>So, we cannot do classical logic? This means it&amp;rsquo;s not relevant to programming then?&lt;/p>
&lt;h3 id="goedel-gentzen">Goedel Gentzen&lt;/h3>
&lt;p>Turns out, classical logic can be embedded in intuitionistic logic. Classical logic = Intuitionistic + double negation elimination (or LEM).&lt;/p>
&lt;p>You can take a proposition in classical logic that is provable, you can translate it into some other proposition in intuitionistic logic, and then translate the proof also that does not use double negation elimination or LEM, and you will get something. There is a correspondence between propositions in two types of logic.&lt;/p>
&lt;p>The transformation of proposition is simple: invert everything by applying double negation to all assumptions. Get a new theorem, prove it in intuitionistic logic, apply double negation to it, you get back the result of that first theorem.&lt;/p>
&lt;p>Double twist, prove, double twist!&lt;/p>
&lt;p>What does it mean? It means &lt;strong>continuations&lt;/strong>. Double negation is &lt;code>(a-&amp;gt;Void)-&amp;gt;Void&lt;/code>, or more general:&lt;/p>
&lt;p>\begin{eqnarray}&lt;br>
(a\rightarrow r)\rightarrow r&lt;br>
\end{eqnarray}&lt;/p>
&lt;p>This \((a\rightarrow r)\rightarrow r\) is a continuation. If you extend lambda calculus with continuation (or do CPS transformation), you have a way to embed classical logic into programming.&lt;/p>
&lt;!--list-separator-->
&lt;ul>
&lt;li>
&lt;p>Yoneda&amp;rsquo;s Lemma&lt;/p>
&lt;p>This is a deeper topic in category theory, and Bartosz mentions it without explanation just to show the importance of continuation in category theory. &lt;a href="https://en.wikipedia.org/wiki/Yoneda%5Flemma">Read more about Yoneda&amp;rsquo;s lemma on Wikipedia&lt;/a>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="conclusions">Conclusions&lt;/h2>
&lt;p>Type theory (typed lambda calculus), category theory (cartesian closed) and logic are all the same, just different notations. There is a lot of cross-polination between these areas. Is the Grand Unified Theory coming soon? &lt;a href="https://en.wikipedia.org/wiki/Homotopy%5Ftype%5Ftheory">Homotopy Type Theory&lt;/a>?&lt;/p>
&lt;p>Maybe.&lt;/p>
&lt;hr>
&lt;p>This is a complete summary of an excellent talk by Bartosz Milewski &lt;a href="https://www.youtube.com/watch?v=dgrucfgv2Tw">Truth about types&lt;/a>. Illustrations and diagrams are recreated; formulas taken verbatim from the slides, except for comments, which were extended in some places.&lt;/p>
&lt;p>&lt;em>(You can get a set of PDF/HTML/epub/Kindle versions below. Name your price, starting from $1.)&lt;/em>&lt;/p>
&lt;script src="https://gumroad.com/js/gumroad.js">&lt;/script>
&lt;p>&lt;a class="gumroad-button" href="https://gum.co/zmXzO?wanted=true" target="_blank" data-gumroad-single-product="true">Get set&lt;/a>&lt;/p></description></item><item><title>Summary of Transducers, a talk by Rich Hickey</title><link>https://rakhim.org/summary-of-transducers-a-talk-by-rich-hickey/</link><pubDate>Fri, 31 Jan 2020 14:39:00 +0200</pubDate><guid>https://rakhim.org/summary-of-transducers-a-talk-by-rich-hickey/</guid><description>
&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>What are transducers? The basic idea is to extract the &lt;strong>essence&lt;/strong> of map, filter and other functions that transform sequences and collections, and reuse this essence so that it can be applied elsewhere; to recast them as &lt;strong>process transformations&lt;/strong>.&lt;/p>
&lt;p>The kind of process for which we can use transducers is a succession of steps, where each step is ingesting an input. Building a collection is just one example of a process of that shape. But let us not focus on the idea of a process which performs &lt;em>building&lt;/em>. Some processes build particular things, other processes are infinite.&lt;/p>
&lt;p>Why it&amp;rsquo;s called transducer? It&amp;rsquo;s a real world and it&amp;rsquo;s better than reduce or ingest, which could be considered.&lt;/p>
&lt;ul>
&lt;li>reduce: lead back, brings something back&lt;/li>
&lt;li>ingest: carry into&lt;/li>
&lt;li>transduce: lead across; as inputs are carried into the process, they are led through a series of transformations&lt;/li>
&lt;/ul>
&lt;p>Transducers are not a programming thing. We have them in real life, where we usually call them &amp;ldquo;instructions&amp;rdquo;. For example, consider the instruction to baggage carriers &amp;ldquo;put the baggage on the plane&amp;rdquo;. While you&amp;rsquo;re doing it:&lt;/p>
&lt;ul>
&lt;li>break apart pallets&lt;/li>
&lt;li>remove bags that smell like food&lt;/li>
&lt;li>label heavy bags&lt;/li>
&lt;/ul>
&lt;p>There could be different circumstances (conveyances, sources, sinks). They are irrelevant. Does baggage come and go on trolleys or conveyor belts? We don&amp;rsquo;t care and we don&amp;rsquo;t want to care. This is the real world: unspecified and flexible.&lt;/p>
&lt;p>In programming we have something like this — collection function composition:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">comp&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">partial mapcat &lt;/span>&lt;span style="color:#b8860b">unbundle-pallet&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">partial filter &lt;/span>&lt;span style="color:#b8860b">non-food?&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">partial map &lt;/span>&lt;span style="color:#b8860b">label-heavy&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There&amp;rsquo;s a big difference between real world and this programming approach. &lt;code>map&lt;/code>, &lt;code>filter&lt;/code>, &lt;code>mapcat&lt;/code> are functions of sequence → sequence. We model the rules, but they only work on sequences. When we get something new, like stream, channel or observable, none of the rules apply.&lt;/p>
&lt;p>Another problem is that this code creates intermediate sequences between steps. It&amp;rsquo;s as if we told the baggage guys to take the baggage off &lt;strong>the trolley&lt;/strong>, unbundle, put bags on another trolley, check for food smell, put bags on another trolley, etc.&lt;/p>
&lt;p>There is no reuse. Every new collection or a process defines its own version of map, filter, mapcat etc. As a result, composed algorithms are needlessly specific and inefficient.&lt;/p>
&lt;h2 id="creating-and-using-transducers">Creating and using transducers&lt;/h2>
&lt;p>Let&amp;rsquo;s start with the value propositions right away. This is what we want to achieve. First, we create a transducer &lt;code>process-bags&lt;/code>, which is a composition of three transducers.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">def &lt;/span>&lt;span style="color:#b8860b">process-bags&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">comp&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">mapcatting&lt;/span> &lt;span style="color:#b8860b">unbundle-pallet&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">filtering&lt;/span> &lt;span style="color:#b8860b">non-food?&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">mapping&lt;/span> &lt;span style="color:#b8860b">label-heavy&lt;/span>)))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>mapcatting&lt;/code>, &lt;code>filtering&lt;/code> and &lt;code>mapping&lt;/code> return &lt;strong>transducers&lt;/strong>. &lt;code>process-bags&lt;/code> is a transducer. Transducers modify a process by transforming its reducing function. Once done, we can go to a completely different context and reuse them.&lt;/p>
&lt;p>&lt;strong>Building a concrete collection&lt;/strong>. Standard function &lt;code>into&lt;/code> now has another arity: it can accept a transducer to generate a collection out of something else.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f">into &lt;/span>&lt;span style="color:#b8860b">airplane&lt;/span> &lt;span style="color:#b8860b">process-bags&lt;/span> &lt;span style="color:#b8860b">pallets&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>sequence&lt;/code> can now take a transducer as well and generate a lazy sequence of values.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">sequence&lt;/span> &lt;span style="color:#b8860b">process-bags&lt;/span> &lt;span style="color:#b8860b">pallets&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A new function &lt;code>transduce&lt;/code> works like &lt;code>reduce&lt;/code>, but takes a transducer, operation, initial value and the source. Here we count the total weight of the bags:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">transduce&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; transducer&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; ↓&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">comp &lt;/span>&lt;span style="color:#b8860b">process-bags&lt;/span> (&lt;span style="color:#00a000">mapping&lt;/span> &lt;span style="color:#b8860b">weigh-bag&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; operation&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; ↓&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f">+ &lt;/span>&lt;span style="color:#666">0&lt;/span> &lt;span style="color:#b8860b">pallets&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; ↑ ↑&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; | source&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; initial value&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This also should work for channels, observables and other contexts:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; a CSP channel that processes bags&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">chan&lt;/span> &lt;span style="color:#666">1&lt;/span> &lt;span style="color:#b8860b">process-bags&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; it&amp;#39;s an open system&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">observable&lt;/span> &lt;span style="color:#b8860b">process-bags&lt;/span> &lt;span style="color:#b8860b">pallet-source&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We call processes that can work with transducers (like &lt;code>into&lt;/code>, &lt;code>sequence&lt;/code>, &lt;code>chan&lt;/code>, etc) &lt;strong>transducible processes&lt;/strong>. They accept a transducer and use it to transform whatever they do. So, the notion of a transducer has two parts:&lt;/p>
&lt;ol>
&lt;li>Functions that create transducers.&lt;/li>
&lt;li>In context where they make sense, you accept transducers.&lt;/li>
&lt;/ol>
&lt;p>Each process takes a transducer and their internal processing function. What&amp;rsquo;s the internal processing function of &lt;code>into&lt;/code>? It&amp;rsquo;s &lt;code>conj&lt;/code> (in Clojure). Channels are inherently the same, they take input and somewhere inside they have a step function: add the item to the buffer. That step function has the same shape as &lt;code>conj&lt;/code>. So, &lt;code>into&lt;/code> and &lt;code>channel&lt;/code> can transform their internal operations with the help of transducers.&lt;/p>
&lt;p>Two papers which describe this idea are:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.cs.ox.ac.uk/files/3390/PRG69.pdf">Lectures on Constructive Functional Programming&lt;/a>, by Richard S. Bird&lt;/li>
&lt;li>&lt;a href="https://www.cs.nott.ac.uk/~pszgmh/fold.pdf">A tutorial on the universality and expressiveness of fold&lt;/a>, Graham Hutton&lt;/li>
&lt;/ul>
&lt;h2 id="how-do-we-get-there">How do we get there?&lt;/h2>
&lt;p>Let&amp;rsquo;s now discuss how can we achieve this. We&amp;rsquo;ll start by re-defining map, filter and mapcat.&lt;/p>
&lt;p>One of the things discussed in the first mentioned paper is the relationship between list processing operations and fold. There&amp;rsquo;s some interesting math that proves the equivalency of lists and operations that construct them. Map and filer can be defined via foldr, which encapsulates recursion and makes it easier to reason about and transform data:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">mapr&lt;/span> [&lt;span style="color:#b8860b">f&lt;/span> &lt;span style="color:#b8860b">coll&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">foldr&lt;/span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">x&lt;/span> &lt;span style="color:#b8860b">r&lt;/span>] (&lt;span style="color:#a2f">cons &lt;/span>(&lt;span style="color:#00a000">f&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>) &lt;span style="color:#b8860b">r&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> () &lt;span style="color:#b8860b">coll&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">filterr&lt;/span> [&lt;span style="color:#b8860b">pred&lt;/span> &lt;span style="color:#b8860b">coll&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">foldr&lt;/span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">x&lt;/span> &lt;span style="color:#b8860b">r&lt;/span>] (&lt;span style="color:#a2f;font-weight:bold">if &lt;/span>(&lt;span style="color:#00a000">pred&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>) (&lt;span style="color:#a2f">cons &lt;/span>&lt;span style="color:#b8860b">x&lt;/span> &lt;span style="color:#b8860b">r&lt;/span>) &lt;span style="color:#b8860b">r&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> () &lt;span style="color:#b8860b">coll&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can similarly re-define these functions in terms of foldl, which is just left reduce. Right reduce implies laziness, right reduce implies loops. It turns up that the loop path is faster, better and more general for the goals we try to achieve.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">mapl&lt;/span> [&lt;span style="color:#b8860b">f&lt;/span> &lt;span style="color:#b8860b">coll&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">reduce &lt;/span>(&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>] (&lt;span style="color:#a2f">conj &lt;/span>&lt;span style="color:#b8860b">r&lt;/span> (&lt;span style="color:#00a000">f&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>)))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [] &lt;span style="color:#b8860b">coll&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">filterl&lt;/span> [&lt;span style="color:#b8860b">pred&lt;/span> &lt;span style="color:#b8860b">coll&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">reduce &lt;/span>(&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>] (&lt;span style="color:#a2f;font-weight:bold">if &lt;/span>(&lt;span style="color:#00a000">pred&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>) (&lt;span style="color:#a2f">conj &lt;/span>&lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>) &lt;span style="color:#b8860b">r&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [] &lt;span style="color:#b8860b">coll&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">mapcatl&lt;/span> [&lt;span style="color:#b8860b">f&lt;/span> &lt;span style="color:#b8860b">coll&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">reduce &lt;/span>(&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>] (&lt;span style="color:#a2f">reduce conj &lt;/span>&lt;span style="color:#b8860b">r&lt;/span> (&lt;span style="color:#00a000">f&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>)))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [] &lt;span style="color:#b8860b">coll&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In both left and right cases, it&amp;rsquo;s mostly boilerplate, and the fundamental thing all functions do is the same: here&amp;rsquo;s the thing that to do to all the things that come through. The problem here is &lt;strong>&lt;code>conj&lt;/code>&lt;/strong>. It is a specific operation in the middle of those generic ideas of &lt;code>map&lt;/code>, &lt;code>filter&lt;/code> and &lt;code>mapcat&lt;/code>. &lt;code>conj&lt;/code> might not be what we want to do.&lt;/p>
&lt;p>To turn those inner functions into transducers we need to add another layer, and allow passing arbitrary step operation instead of hard-coding &lt;code>conj&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">mapping&lt;/span> [&lt;span style="color:#b8860b">f&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">step&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>] (&lt;span style="color:#00a000">step&lt;/span> &lt;span style="color:#b8860b">r&lt;/span> (&lt;span style="color:#00a000">f&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>)))))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">filtering&lt;/span> [&lt;span style="color:#b8860b">pred&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">step&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>] (&lt;span style="color:#a2f;font-weight:bold">if &lt;/span>(&lt;span style="color:#00a000">pred&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>) (&lt;span style="color:#00a000">step&lt;/span> &lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>) &lt;span style="color:#b8860b">r&lt;/span>))))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">def &lt;/span>&lt;span style="color:#b8860b">cat&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">step&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>] (&lt;span style="color:#a2f">reduce &lt;/span>&lt;span style="color:#b8860b">step&lt;/span> &lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>))))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">mapcatting&lt;/span> [&lt;span style="color:#b8860b">f&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">comp &lt;/span>(&lt;span style="color:#a2f">map &lt;/span>&lt;span style="color:#b8860b">f&lt;/span>) &lt;span style="color:#b8860b">cat&lt;/span> ))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now &lt;code>mapping&lt;/code> takes a function and returns a function which expects a step function as argument. We can now pass &lt;code>conj&lt;/code> and produce the reduce-based &lt;code>map&lt;/code>, &lt;code>filter&lt;/code> and &lt;code>mapcat&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">mapl&lt;/span> [&lt;span style="color:#b8860b">f&lt;/span> &lt;span style="color:#b8860b">coll&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">reduce &lt;/span>((&lt;span style="color:#00a000">mapping&lt;/span> &lt;span style="color:#b8860b">f&lt;/span>) &lt;span style="color:#b8860b">conj&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [] &lt;span style="color:#b8860b">col&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">filterl&lt;/span> [&lt;span style="color:#b8860b">pred&lt;/span> &lt;span style="color:#b8860b">coll&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">reduce &lt;/span>((&lt;span style="color:#00a000">filtering&lt;/span> &lt;span style="color:#b8860b">pred&lt;/span>) &lt;span style="color:#b8860b">conj&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [] &lt;span style="color:#b8860b">coll&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">mapcatl&lt;/span> [&lt;span style="color:#b8860b">f&lt;/span> &lt;span style="color:#b8860b">coll&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f">reduce &lt;/span>((&lt;span style="color:#00a000">mapcatting&lt;/span> &lt;span style="color:#b8860b">f&lt;/span>) &lt;span style="color:#b8860b">conj&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [] &lt;span style="color:#b8860b">coll&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That was the whole point. Transducers are fully decoupled. They know nothing about the process they modify.&lt;/p>
&lt;h3 id="transducers-are-fast">Transducers are fast&lt;/h3>
&lt;p>One benefit of transducers is speed. Since they are just a stack of function calls, there&amp;rsquo;s no laziness overhead, no intermediate collections, no extra &amp;ldquo;boxes&amp;rdquo;. There&amp;rsquo;s no notion of &amp;ldquo;empty list is nothing&amp;rdquo;. Nothing is nothing, an empty list is an empty list.&lt;/p>
&lt;h3 id="transducer-types">Transducer types&lt;/h3>
&lt;p>Transducers raise interesting problems in the context of type theory.&lt;/p>
&lt;p>If you&amp;rsquo;re trying to produce the next process &lt;code>N&lt;/code>, you must supply the result of step &lt;code>N-1&lt;/code> as input. Thus, modeling the type system as &lt;code>R→R&lt;/code> would be wrong. We&amp;rsquo;d need something like this:&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/transducers/transducer_types.png" width="330px">
&lt;/figure>
&lt;p>Also, the black boxes that represent the elements are unnecessarily restrictive by type in this image. We could create a step function that when input is &lt;code>x&lt;/code> the output is &lt;code>y&lt;/code>, when input is &lt;code>y&lt;/code> the output is &lt;code>z&lt;/code>, when input is &lt;code>z&lt;/code> the output is &lt;code>x&lt;/code>. It&amp;rsquo;s a perfectly fine state machine, but it&amp;rsquo;d be tricky to figure out the type system for that. &lt;code>{x,y,z}→{x,y,z}&lt;/code> is not the answer, because for input &lt;code>x&lt;/code> the output must be only &lt;code>y&lt;/code>, not &lt;code>x&lt;/code> or &lt;code>y&lt;/code> or &lt;code>z&lt;/code>.&lt;/p>
&lt;h3 id="early-termination">Early termination&lt;/h3>
&lt;p>Ordinary reduction processes everything. Sometimes a process has a reason to terminate early, for whatever reason. It maybe the desire of the process itself, or a decision of one of the steps, or an external trigger.&lt;/p>
&lt;p>Continuing with the baggage handling analogy, let&amp;rsquo;s say if the bag is ticking — that&amp;rsquo;s it, go home.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">comp&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">mapcatting&lt;/span> &lt;span style="color:#b8860b">unbundle-pallet&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">taking-while&lt;/span> &lt;span style="color:#b8860b">non-ticking?&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">filtering&lt;/span> &lt;span style="color:#b8860b">non-food?&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">mapping&lt;/span> &lt;span style="color:#b8860b">label-heavy&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>taking-while&lt;/code> must stop the whole job. How to do that? Turns out in Clojure &lt;code>reduce&lt;/code> can already do that via &lt;code>(reduced result)&lt;/code>. It&amp;rsquo;s a wrapper with a corresponding test &lt;code>reduced?&lt;/code>. Once wrapped, we can dereference the value with &lt;code>deref&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">reduced?&lt;/span> (&lt;span style="color:#00a000">reduced&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>)) &lt;span style="color:#080;font-style:italic">;; → true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f">deref &lt;/span>(&lt;span style="color:#00a000">reduced&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>)) &lt;span style="color:#080;font-style:italic">;; → x&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Transducers support &lt;code>reduced&lt;/code>. That means that step functions can return &lt;code>(reduced value)&lt;/code>. If a transducer gets a reduced value from a nested step call, it must never call that step function again with input.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">taking-while&lt;/span> [&lt;span style="color:#b8860b">pred&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">step&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">if &lt;/span>(&lt;span style="color:#00a000">pred&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">step&lt;/span> &lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">reduced&lt;/span> &lt;span style="color:#b8860b">r&lt;/span>)))))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>All reducing processes must also support reduced. If the step function returns a reduced value, the process must not supply any more input to the step function. The dereferenced value is the final accumulated value.&lt;/p>
&lt;p>To illustrate this improvement in the context of types, we&amp;rsquo;d need to build something like this:&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/transducers/transducer_types_reduced.png" width="330px">
&lt;/figure>
&lt;p>Vertical bar represents &lt;code>OR&lt;/code>. I.e. a process returns a black box or a reduced version of it.&lt;/p>
&lt;h3 id="state">State&lt;/h3>
&lt;p>Some interesting sequence functions require state (e.g. &lt;code>take&lt;/code>, &lt;code>partition-*&lt;/code>). They might count stuff or accumulate a sum, for example. Where would that data go? In purely functional approach the stack or laziness can be used to capture state. The idea of transducers is to isolate the execution completely, so we can&amp;rsquo;t use that approach.&lt;/p>
&lt;p>State has to be explicit. It must be incorporated into the transducer object. They must create unique state every time they are called upon to transform a step function. Thus, once applied to a process, a transducer yields another (potentially stateful) process.&lt;/p>
&lt;p>You should always treat an applied transducer stack as if it returned a stateful process, because you don&amp;rsquo;t know until the end whether there&amp;rsquo;ll be state. This means you can&amp;rsquo;t alias the process returned by a transducer.&lt;/p>
&lt;p>Here&amp;rsquo;s an example of a stateful transducer:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">dropping-while&lt;/span> [&lt;span style="color:#b8860b">pred&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">step&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">let &lt;/span>[&lt;span style="color:#b8860b">dv&lt;/span> (&lt;span style="color:#00a000">volatile!&lt;/span> &lt;span style="color:#b8860b">true&lt;/span>)]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">let &lt;/span>[&lt;span style="color:#b8860b">drop?&lt;/span> &lt;span style="color:#666">@&lt;/span>&lt;span style="color:#b8860b">dv&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">if &lt;/span>(&lt;span style="color:#a2f">and &lt;/span>&lt;span style="color:#b8860b">drop?&lt;/span> (&lt;span style="color:#00a000">pred&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">r&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">do&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">vreset!&lt;/span> &lt;span style="color:#b8860b">dv&lt;/span> &lt;span style="color:#b8860b">false&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">step&lt;/span> &lt;span style="color:#b8860b">r&lt;/span> &lt;span style="color:#b8860b">x&lt;/span>))))))))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That&amp;rsquo;s not the prettiest thing&amp;hellip;&lt;/p>
&lt;h3 id="completion">Completion&lt;/h3>
&lt;p>Some jobs don&amp;rsquo;t complete, they don&amp;rsquo;t have ends, they don&amp;rsquo;t consume finite collections. They might accept things from a channel or a event source.&lt;/p>
&lt;p>But other jobs might complete (stop receiving input). A process might want to do a final transformation of the value built up. A stateful transducer might want to flush a pending value. So, all step functions &lt;em>must&lt;/em> have an arity-1 variant that does not take an input.&lt;/p>
&lt;p>If the process has finished, it must call the completion operation exactly once. Each transducer must do the same thing and call its nested completion operation. It &lt;em>may&lt;/em> flush state using the nested step function prior to calling nested complete.&lt;/p>
&lt;p>Coming back to the visual types, we now have to think of reducing function as a pair of operations. The first takes no input — that&amp;rsquo;s the completion operation. The second is the regular step operation.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/transducers/transducer_types_completion.png" width="330px">
&lt;/figure>
&lt;h3 id="init">Init&lt;/h3>
&lt;p>There&amp;rsquo;s the third type of operation that&amp;rsquo;s associated with processing in general: &lt;code>init&lt;/code>. A reducing function &lt;em>may&lt;/em> support arity-0 which returns an initial accumulation value. Transducers must support arity-0 in terms of a call to the nested init.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">+&lt;/span>) &lt;span style="color:#080;font-style:italic">;; → 0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f">+ &lt;/span>&lt;span style="color:#666">21&lt;/span>) &lt;span style="color:#080;font-style:italic">;; → 21&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">*&lt;/span>) &lt;span style="color:#080;font-style:italic">;; → 1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We now have three operations, one of which is init from nothing.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/transducers/transducer_types_completion_optional.png" width="330px">
&lt;/figure>
&lt;p>In Clojure we just define functions with arity 0, 1, 2. Transducers take and return reducing functions. Now, core sequence functions&amp;rsquo; 1-arity variant return a transducer. We decided not to call those things &lt;code>mapping&lt;/code>, &lt;code>filtering&lt;/code>, etc (as this may not carry very well into languages other than English). So, to create a transducer, call a function without the collection:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">mapping&lt;/span> &lt;span style="color:#b8860b">f&lt;/span>) &lt;span style="color:#a2f">== &lt;/span>(&lt;span style="color:#a2f">map &lt;/span>&lt;span style="color:#b8860b">f&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This applies to core sequence functions:&lt;/p>
&lt;ul>
&lt;li>&lt;code>map&lt;/code>&lt;/li>
&lt;li>&lt;code>mapcat&lt;/code>&lt;/li>
&lt;li>&lt;code>filter&lt;/code>&lt;/li>
&lt;li>&lt;code>remove&lt;/code>&lt;/li>
&lt;li>&lt;code>take&lt;/code>&lt;/li>
&lt;li>&lt;code>take-while&lt;/code>&lt;/li>
&lt;li>&lt;code>drop&lt;/code>&lt;/li>
&lt;li>&lt;code>drop-while&lt;/code>&lt;/li>
&lt;li>&lt;code>take-nth&lt;/code>&lt;/li>
&lt;li>&lt;code>replace&lt;/code>&lt;/li>
&lt;li>&lt;code>partition-by&lt;/code>&lt;/li>
&lt;li>&lt;code>partition-all&lt;/code>&lt;/li>
&lt;li>&lt;code>keep&lt;/code>&lt;/li>
&lt;li>&lt;code>keep-indexed&lt;/code>&lt;/li>
&lt;li>&lt;code>cat&lt;/code>&lt;/li>
&lt;li>&lt;code>dedupe&lt;/code>&lt;/li>
&lt;li>etc&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>Here&amp;rsquo;s the final example: filter returning a transducer. It takes a predicate and returns a step modifying function, which takes a reducing function which presumably has # arities, and defines a function with three arities:&lt;/p>
&lt;ol>
&lt;li>init&lt;/li>
&lt;li>complete, which in case of filter just returns the result&lt;/li>
&lt;li>the result of input, which is what regular filter does&lt;/li>
&lt;/ol>
&lt;!--listend-->
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-clojure" data-lang="clojure">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f;font-weight:bold">defn &lt;/span>&lt;span style="color:#b8860b">filter&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ([&lt;span style="color:#b8860b">pred&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">fn &lt;/span>[&lt;span style="color:#b8860b">rf&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">fn&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ([] (&lt;span style="color:#00a000">rf&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ([&lt;span style="color:#b8860b">result&lt;/span>] (&lt;span style="color:#00a000">rf&lt;/span> &lt;span style="color:#b8860b">result&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ([&lt;span style="color:#b8860b">result&lt;/span> &lt;span style="color:#b8860b">input&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a2f;font-weight:bold">if &lt;/span>(&lt;span style="color:#00a000">pred&lt;/span> &lt;span style="color:#b8860b">input&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">rf&lt;/span> &lt;span style="color:#b8860b">result&lt;/span> &lt;span style="color:#b8860b">input&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">result&lt;/span>)))))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ([&lt;span style="color:#b8860b">pred&lt;/span> &lt;span style="color:#b8860b">coll&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#00a000">sequence&lt;/span> (&lt;span style="color:#a2f">filter &lt;/span>&lt;span style="color:#b8860b">pred&lt;/span>) &lt;span style="color:#b8860b">coll&lt;/span>)))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>Transducers:&lt;/p>
&lt;ul>
&lt;li>support early termination and completion&lt;/li>
&lt;li>are context-independent&lt;/li>
&lt;li>reusable across a wide variety of contexts&lt;/li>
&lt;li>composable via ordinary function composition&lt;/li>
&lt;li>efficient&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>This is a complete summary of an excellent talk by Rich Hickey &lt;a href="https://www.youtube.com/watch?v=6mTbuzafcII">&amp;ldquo;Transducers&amp;rdquo;&lt;/a>. Illustrations and diagrams are recreated; source code taken verbatim from the slides, except for comments, which were extended in some places.&lt;/p>
&lt;p>&lt;em>(You can get a set of PDF/HTML/epub/Kindle versions below. Name your price, starting from $1.)&lt;/em>&lt;/p>
&lt;script src="https://gumroad.com/js/gumroad.js">&lt;/script>
&lt;p>&lt;a class="gumroad-button" href="https://gum.co/mASyV?wanted=true" target="_blank" data-gumroad-single-product="true">Get set&lt;/a>&lt;/p></description></item><item><title>Fastmail setup with Emacs, mu4e and mbsync on macOS</title><link>https://rakhim.org/fastmail-setup-with-emacs-mu4e-and-mbsync-on-macos/</link><pubDate>Thu, 30 Jan 2020 13:09:00 +0200</pubDate><guid>https://rakhim.org/fastmail-setup-with-emacs-mu4e-and-mbsync-on-macos/</guid><description>
&lt;p>I guess it was inevitable. Once you embrace Emacs, at some point you gonna want to do email in it. Honestly, I don&amp;rsquo;t think I&amp;rsquo;ll stick with it, but as an experiment, I want to try and see whether it makes sense to use Emacs as an email client.&lt;/p>
&lt;p>These are my requirements:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Fastmail as email provider.&lt;/strong> It&amp;rsquo;s a solid service with normal IMAP (unlike Gmail with their weird, non-standard implementation which makes it very hard to use anything other than their web interface). I moved from Gmail to Fastmail almost two years ago and I highly recommend the service. You can use my &lt;a href="https://ref.fm/u19462080">referral code&lt;/a> to get 10% off.&lt;/li>
&lt;li>&lt;strong>No obligations.&lt;/strong> I want to be able to use whatever third party email client or continue using Fastmail&amp;rsquo;s web interface without any caveats. This means I don&amp;rsquo;t want to setup something in Emacs that will only work there (like custom tagging which doesn&amp;rsquo;t propagate to the server).&lt;/li>
&lt;li>&lt;strong>Fast and simple.&lt;/strong> I dislike elaborate, finicky setups and slowdowns.&lt;/li>
&lt;/ol>
&lt;p>The setup consists of the following parts:&lt;/p>
&lt;ol>
&lt;li>Fastmail IMAP server. This doesn&amp;rsquo;t require any special configuration.&lt;/li>
&lt;li>&lt;strong>mbsync&lt;/strong> — a command-line utility which syncs IMAP server with a local directory in &lt;a href="https://en.wikipedia.org/wiki/Maildir">Maildir&lt;/a> format. This has nothing to do with Emacs. Even if you&amp;rsquo;re not interested in doing email in Emacs, having a full local copy of your mailbox may be a good idea. mbsync does exactly that. In can sync both ways, so if I just move a file (which corresponds to a single message) from folder to folder, and then run mbsync again, this change will propagate to the server.&lt;/li>
&lt;li>&lt;strong>&lt;a href="https://www.djcbsoftware.nl/code/mu/">mu&lt;/a>&lt;/strong> — a command line email client which works with Maildir storage. It doesn&amp;rsquo;t deal with the server directly, instead, it just read your local file system. It&amp;rsquo;s very fast!&lt;/li>
&lt;li>&lt;strong>&lt;a href="https://www.djcbsoftware.nl/code/mu/mu4e.html">mu4e&lt;/a>&lt;/strong> — Emacs package that comes with mu. Provides an interface to mu.&lt;/li>
&lt;/ol>
&lt;h2 id="installing-and-configuring-mbsync">Installing and configuring mbsync&lt;/h2>
&lt;p>Install mbsync with homebrew (the package is called &lt;code>isync&lt;/code>, but the binary is called &lt;code>mbsync&lt;/code>):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>brew install isync
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Mbsync uses &lt;code>~/.mbsyncrc&lt;/code> for configuration. &lt;a href="https://manpages.debian.org/unstable/isync/mbsync.1.en.html">This page&lt;/a> describes all options. Here&amp;rsquo;s my config, with comments:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic"># First section: remote IMAP account&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>IMAPAccount fastmail
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Host imap.fastmail.com
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Port &lt;span style="color:#666">993&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>User myemailaddress@mydomain.com
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic"># For simplicity, this is how to read the password from another file.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic"># For better security you should use GPG https://gnupg.org/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>PassCmd &lt;span style="color:#b44">&amp;#34;cat ~/.mbsync-fastmail&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>SSLType IMAPS
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>SSLVersions TLSv1.2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>IMAPStore fastmail-remote
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Account fastmail
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic"># This section describes the local storage&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>MaildirStore fastmail-local
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Path ~/Maildir/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Inbox ~/Maildir/INBOX
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic"># The SubFolders option allows to represent all&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic"># IMAP subfolders as local subfolders&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>SubFolders Verbatim
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic"># This section a &amp;#34;channel&amp;#34;, a connection between remote and local&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Channel fastmail
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Master :fastmail-remote:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Slave :fastmail-local:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Patterns *
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Expunge None
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>CopyArrivalDate yes
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Sync All
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Create Slave
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>SyncState *
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Few details about the channel options worth mentioning:&lt;/p>
&lt;ol>
&lt;li>&lt;code>Patterns *&lt;/code> — sync all folders. Alternatively, you can select only certain folders to sync.&lt;/li>
&lt;li>&lt;code>Expunge None&lt;/code> — don&amp;rsquo;t destroy messages neither locally, nor remotely. Details later.&lt;/li>
&lt;li>&lt;code>CopyArrivalDate&lt;/code> — makes sure the date of the arrival stays the same when you move messages around. Without this option, moving a message to another folder will reset the date of the message.&lt;/li>
&lt;li>&lt;code>Create Slave&lt;/code> — when new folders are added on server, create them locally.&lt;/li>
&lt;/ol>
&lt;p>Now run &lt;code>mbsync&lt;/code> and wait for it to download messages:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>mbsync -a
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For me, around 50k messages were synced in a few minutes.&lt;/p>
&lt;h2 id="installing-and-configuring-mu-and-mu4e">Installing and configuring mu and mu4e&lt;/h2>
&lt;p>I use Emacs port for macOS, which you can install like so:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>brew tap railwaycat/emacsmacport
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>brew cask install emacs-mac
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once installed, verify that &lt;code>which emacs&lt;/code> points to a valid Emacs executable, and &lt;code>emacs --version&lt;/code> shows the correct version:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>→ which emacs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/usr/local/bin/emacs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>→ ls -la /usr/local/bin/emacs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/usr/local/bin/emacs -&amp;gt; /Applications/Emacs.app/Contents/MacOS/Emacs.sh
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>→ emacs --version
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>GNU Emacs 26.3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, install mu:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>brew install mu
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And let it index the Maildir:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>mu index --maildir&lt;span style="color:#666">=&lt;/span>~/Maildir
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now you can check if everything worked by trying a command-line search:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>mu find hello
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>mu comes with mu4e by default. To verify, check for presence of elisp files in &lt;code>/usr/local/share/emacs/site-lisp/mu/mu4e&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>→ ls /usr/local/share/emacs/site-lisp/mu/mu4e
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mu4e-actions.el mu4e-contrib.elc mu4e-main.el ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now load these files in Emacs and enable mu4e. Put the following in your Emacs config:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#b8860b">add-to-list&lt;/span> &lt;span style="color:#b8860b">&amp;#39;load-path&lt;/span> &lt;span style="color:#b44">&amp;#34;/usr/local/share/emacs/site-lisp/mu/mu4e&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f">require&lt;/span> &lt;span style="color:#b8860b">&amp;#39;mu4e&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And add some configuration.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f">setq&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mue4e-headers-skip-duplicates&lt;/span> &lt;span style="color:#800">t&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-view-show-images&lt;/span> &lt;span style="color:#800">t&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-view-show-addresses&lt;/span> &lt;span style="color:#800">t&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-compose-format-flowed&lt;/span> &lt;span style="color:#800">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-date-format&lt;/span> &lt;span style="color:#b44">&amp;#34;%y/%m/%d&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-headers-date-format&lt;/span> &lt;span style="color:#b44">&amp;#34;%Y/%m/%d&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-change-filenames-when-moving&lt;/span> &lt;span style="color:#800">t&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-attachments-dir&lt;/span> &lt;span style="color:#b44">&amp;#34;~/Downloads&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-maildir&lt;/span> &lt;span style="color:#b44">&amp;#34;~/Maildir&amp;#34;&lt;/span> &lt;span style="color:#080;font-style:italic">;; top-level Maildir&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">;; note that these folders below must start with /&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">;; the paths are relative to maildir root&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-refile-folder&lt;/span> &lt;span style="color:#b44">&amp;#34;/Archive&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-sent-folder&lt;/span> &lt;span style="color:#b44">&amp;#34;/Sent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-drafts-folder&lt;/span> &lt;span style="color:#b44">&amp;#34;/Drafts&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">mu4e-trash-folder&lt;/span> &lt;span style="color:#b44">&amp;#34;/Trash&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; this setting allows to re-sync and re-index mail&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">;; by pressing U&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f">setq&lt;/span> &lt;span style="color:#b8860b">mu4e-get-mail-command&lt;/span> &lt;span style="color:#b44">&amp;#34;mbsync -a&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That&amp;rsquo;s it! Run &lt;code>M-x mu4e&lt;/code> and after a few final setup questions mu4e should be running. Check out &lt;a href="https://www.djcbsoftware.nl/code/mu/mu4e/Keybindings.html#Keybindings">keybindings&lt;/a> for it.&lt;/p>
&lt;h3 id="caveat-1-deletion-vs-dot-expunge">Caveat 1: deletion vs. expunge&lt;/h3>
&lt;p>By default, when you mark a message to be deleted, mu4e will apply the &amp;ldquo;Trashed&amp;rdquo; flag. Fastmail automatically destroys the messages flagged this way, as per IMAP standard. Unfortunately, there is no way to stop Fastmail from doing that.&lt;/p>
&lt;p>Instead of total deletion, I want to move messages to the &amp;ldquo;Trash&amp;rdquo; folder. I can simply use &amp;ldquo;move&amp;rdquo; command of mu4e, but it&amp;rsquo;d be nicer to use &lt;code>d&lt;/code> button (deletion) for that. The following piece of elisp remaps the &lt;code>d&lt;/code> button to &amp;ldquo;move to Trash folder&amp;rdquo; action. This way, neither mu4e nor Fastmail destroy the message.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">fset&lt;/span> &lt;span style="color:#b8860b">&amp;#39;my-move-to-trash&lt;/span> &lt;span style="color:#b44">&amp;#34;mTrash&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">define-key&lt;/span> &lt;span style="color:#b8860b">mu4e-headers-mode-map&lt;/span> (&lt;span style="color:#b8860b">kbd&lt;/span> &lt;span style="color:#b44">&amp;#34;d&amp;#34;&lt;/span>) &lt;span style="color:#b8860b">&amp;#39;my-move-to-trash&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#00a000">define-key&lt;/span> &lt;span style="color:#b8860b">mu4e-view-mode-map&lt;/span> (&lt;span style="color:#b8860b">kbd&lt;/span> &lt;span style="color:#b44">&amp;#34;d&amp;#34;&lt;/span>) &lt;span style="color:#b8860b">&amp;#39;my-move-to-trash&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="caveat-2-spam-reporting">Caveat 2: Spam reporting&lt;/h3>
&lt;p>I was worried about not being able to report spam messages back to Fastmail. Turns out it wasn&amp;rsquo;t an issue: in Fastmail settings, you can turn on &amp;ldquo;Spam learning&amp;rdquo; for a folder. Fastmail will scan a folder daily and learn any new messages as spam. So, I don&amp;rsquo;t need to explicitly report spam, all I need is to move spammy messages to &amp;ldquo;Spam&amp;rdquo; folder.&lt;/p>
&lt;h2 id="sending-mail">Sending mail&lt;/h2>
&lt;p>mu4e re-uses Gnus’ &lt;code>message-mode&lt;/code> for writing mail and inherits its configuration. For sending via SMTP, mu4e uses &lt;code>smtpmail&lt;/code>. Minimal configuration looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#a2f">setq&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">message-send-mail-function&lt;/span> &lt;span style="color:#b8860b">&amp;#39;smtpmail-send-it&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">smtpmail-default-smtp-server&lt;/span> &lt;span style="color:#b44">&amp;#34;smtp.fastmail.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b8860b">smtpmail-smtp-server&lt;/span> &lt;span style="color:#b44">&amp;#34;smtp.fastmail.com&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When using SMTP for the first time, Emacs prompts you for the user name and password to use, and then offers to save the information. By default, Emacs stores authentication information in a file &lt;code>~/.authinfo&lt;/code>.&lt;/p>
&lt;h2 id="links">Links&lt;/h2>
&lt;ol>
&lt;li>&lt;a href="https://www.gnu.org/software/emacs/manual/html%5Fmono/smtpmail.html">Emacs SMTP Library (smtpmail) documentation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.djcbsoftware.nl/code/mu/mu4e/index.html">Mu4e user manual&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://pragmaticemacs.com/mu4e-tutorials/">List of mu4e tutorials&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Summary of Concurrency Is Not Parallellism, a talk by Rob Pike</title><link>https://rakhim.org/summary-of-concurrency-is-not-parallellism-a-talk-by-rob-pike/</link><pubDate>Fri, 20 Dec 2019 16:25:00 +0200</pubDate><guid>https://rakhim.org/summary-of-concurrency-is-not-parallellism-a-talk-by-rob-pike/</guid><description>
&lt;p>This is a complete summary of an excellent talk by Rob Pike &lt;a href="https://www.youtube.com/watch?v=cN%5FDpYBzKso">&amp;ldquo;Concurrency is Not Parallelism&amp;rdquo;&lt;/a>. Illustrations and diagrams are recreated; source code taken verbatim from the slides, except for comments, which were extended in some places.&lt;/p>
&lt;p>&lt;em>(You can get a set of PDF (&lt;a href="https://rakhim.org/ziptalks/SAMPLE%2520Summary%2520of%2520Concurrency%2520Is%2520Not%2520Parallelism%2520(Rob%2520Pike).pdf">preview&lt;/a>)/HTML/epub/Kindle versions below. Name your price, starting from $1.)&lt;/em>&lt;/p>
&lt;script src="https://gumroad.com/js/gumroad.js">&lt;/script>
&lt;p>&lt;a class="gumroad-button" href="https://gum.co/XZBKp?wanted=true" target="_blank" data-gumroad-single-product="true">Get set&lt;/a>&lt;/p>
&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>The world is parallel: starting from the computing fundamentals, such as multi-core CPUs, and all the way to real life objects, people, planets and the Universe as a whole — everything is happening simultaneously. Yet, the computing tools that we have aren&amp;rsquo;t good at expressing this world view. We can rectify this by exploring concurrency.&lt;/p>
&lt;p>Go is a concurrent language. It makes it easy to design concurrent systems by providing the ability to:&lt;/p>
&lt;ul>
&lt;li>execute things concurrently&lt;/li>
&lt;li>communicate between concurrently running processes&lt;/li>
&lt;/ul>
&lt;p>There&amp;rsquo;s a misconception about Go and concurrency: many programmers believe concurrency and parallelism are the same thing. They are not, and this talk will try to answer why.&lt;/p>
&lt;h2 id="concurrency-vs-parallelism">Concurrency vs parallelism&lt;/h2>
&lt;p>Here&amp;rsquo;s the core of the distinction:&lt;/p>
&lt;p>&lt;strong>Concurrency is composition of independently executing things&lt;/strong> (typically, functions). We often use the word &amp;lsquo;process&amp;rsquo; to refer to such running thing, and we don&amp;rsquo;t mean &amp;lsquo;unix process&amp;rsquo;, but rather a process in the abstract, general sense.&lt;/p>
&lt;p>&lt;strong>Parallelism is simultaneous execution of multiple things&lt;/strong>. Those things might or might not be related to each other.&lt;/p>
&lt;p>Concurrency is about &lt;strong>dealing&lt;/strong> with a lot of things at once. Parallelism is about &lt;strong>doing&lt;/strong> a lot of things at once. The ideas are, obviously, related, but one is inherently associated with structure, the other is associated with execution. Concurrency is structuring things in a way that &lt;em>might&lt;/em> allow parallelism to actually execute them simultaneously. But parallelism is not the goal of concurrency. &lt;strong>The goal of concurrency is good structure.&lt;/strong>&lt;/p>
&lt;h3 id="analogy">Analogy&lt;/h3>
&lt;p>The operating system manages multiple devices at the same time (disk, screen, keyboard, etc). They are somewhat independent and completely &lt;em>concurrent&lt;/em> concerns. However, they aren&amp;rsquo;t necessarily parallel: if the computer has only one core, several things can&amp;rsquo;t possibly run simultaneously. The model here is concurrent, it is structured as a system of concurrent processes. Its reality could be parallel, depending on circumstances.&lt;/p>
&lt;p>Compare this to performing matrix multiplication on a powerful GPU which contains hundreds or thousands of cores. Both the underlying idea and the reality are &lt;em>parallel&lt;/em>, it&amp;rsquo;s all about running operations at the same physical time.&lt;/p>
&lt;p>Concurrency allows to structure the system in such a way that enables possible parallelism, but requires communication. Tony Hoare has written &amp;ldquo;Communicating sequential processes&amp;rdquo; (&lt;a href="https://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf">https://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf&lt;/a>) in 1978, where he describes problems and techniques of dealing with these issues. It is the greatest paper in computer science and we highly recommend every programmer to read it. Programming languages like Erlang and Go are largely based on ideas described in it.&lt;/p>
&lt;h2 id="concurrent-composition">Concurrent composition&lt;/h2>
&lt;p>There&amp;rsquo;s a pile of books we need to burn. We have a gopher whose job is to move books from the pile to the incinerator.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/1.png">
&lt;/figure>
&lt;p>One gopher is slow, so let&amp;rsquo;s add another gopher. But now we need to synchronize them, since they might bump into each other, or get stuck at either side. One way to solve this is to make them communicate with each other by sending messages (like, &amp;ldquo;I&amp;rsquo;m at the pile now&amp;rdquo; or &amp;ldquo;I&amp;rsquo;m on my way to the incinerator&amp;rdquo;).&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/2.png">
&lt;/figure>
&lt;p>How can we go faster? Double everything! Two piles of books, two incinerators! Consumption and burning can be twice as fast now. That&amp;rsquo;s parallel.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/3.png">
&lt;/figure>
&lt;p>But try to think about it as the composition of two gopher processes. We start with a single process, and then just introduce another instance of the same process. This is called &lt;strong>concurrent composition&lt;/strong>.&lt;/p>
&lt;p>While not immediately obvious, concurrent composition is not automatically parallel! It&amp;rsquo;s possible that only one gopher moves at a time. The design is still concurrent, but not parallel. This is similar to the OS example on a single core processor, where two concurrent things might not run in parallel due to technical limitations.&lt;/p>
&lt;p>However, concurrent composition is automatically &lt;strong>parallelizable&lt;/strong>.&lt;/p>
&lt;h3 id="different-design">Different design&lt;/h3>
&lt;p>Let&amp;rsquo;s try another approach. There will be three gophers in total:&lt;/p>
&lt;ol>
&lt;li>One only loads the cart.&lt;/li>
&lt;li>Another runs the cart to and from the incinerator.&lt;/li>
&lt;li>The third unloads the cart.&lt;/li>
&lt;/ol>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/4.png">
&lt;/figure>
&lt;p>Each gopher is an independently executing procedure.&lt;/p>
&lt;p>This approach is probably faster, although, not by much. There&amp;rsquo;ll definitely be problems like blocking, unnecessary waiting when the books are being loaded and unloaded, the time when the 2nd gopher runs back and nothing useful is happening, etc. Let&amp;rsquo;s add another gopher!&lt;/p>
&lt;h3 id="finer-grained-concurrency">Finer-grained concurrency&lt;/h3>
&lt;p>Now there&amp;rsquo;s a 4th gopher who returns the empty cart&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/5.png">
&lt;/figure>
&lt;p>This version of the problem will work better than the previous version, even though we&amp;rsquo;re doing &lt;em>more work&lt;/em>. Concurrent composition of better managed pieces can run faster. In the perfect situation, with all settings optimal (number of books, timing, distance), this approach can be 4 times faster than the original version.&lt;/p>
&lt;p>This is important! We improved the performance of this program by adding a concurrent procedure to existing design. We added more things and it got faster! The reason it &lt;em>can&lt;/em> run faster is that it &lt;em>can&lt;/em> be parallel, and the reason it can be parallel is better concurrent design.&lt;/p>
&lt;p>So, we have four distinct gopher procedures:&lt;/p>
&lt;ol>
&lt;li>Load books onto cart.&lt;/li>
&lt;li>Move cart to incinerator.&lt;/li>
&lt;li>Unload cart into incinerator.&lt;/li>
&lt;li>Return empty cart.&lt;/li>
&lt;/ol>
&lt;p>Think of them as of independent procedures, running on their own, and we compose them in parallel to construct the solution. We can make it more parallel by, well, parallellizing the whole thing:&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/6.png">
&lt;/figure>
&lt;p>Note what we&amp;rsquo;re doing here: we have a well composed system which we then parallelize on a different axis to, hopefully, achieve better throughput. We understand the composition and have control over the pieces.&lt;/p>
&lt;p>And what if gophers can&amp;rsquo;t run simultaneously (back into the single core world)? No problem, really. Only one gopher runs at a time, and 7 others are idle. The system runs as fast as a single gopher and the overall speed is the same as the first solution. But the design is concurrent, and it is correct. This means we don&amp;rsquo;t have to worry about parallelism if we do concurrency right. Parallelism is optional.&lt;/p>
&lt;h3 id="yet-another-design">Yet another design&lt;/h3>
&lt;p>Two gophers with a staging dump in the middle.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/7.png">
&lt;/figure>
&lt;p>Two similar gopher procedures running concurrently. In theory, this could be twice as fast. As before, we can parallelize it and have two piles with two staging dumps.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/8.png">
&lt;/figure>
&lt;p>Or try a different design still: 4 gopher approach with a single staging dump in the middle.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/9.png">
&lt;/figure>
&lt;p>And then double that! 16 gophers, very high throughput.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/10.png">
&lt;/figure>
&lt;p>Obviously, this is very simplistic and silly. But conceptually this is how you think about problems: don&amp;rsquo;t think about parallel execution, think about breaking down the problem into independent components, and then compose in a concurrent manner.&lt;/p>
&lt;h3 id="summary">Summary&lt;/h3>
&lt;p>There are many ways to break the process down. You can easily come up with a dozen more structures. That is &lt;strong>concurrent design&lt;/strong>. Once we have the breakdown, parallelization can fall out and correctness is easy to achieve. The design is intrinsically safe.&lt;/p>
&lt;h2 id="real-world-example">Real world example&lt;/h2>
&lt;p>This gophers example might look silly, but change books to web content, gophers to CPUs, carts to networking and incinerators to a web browser, and you have a web service architecture.&lt;/p>
&lt;p>Let&amp;rsquo;s learn a little bit of Go.&lt;/p>
&lt;h3 id="goroutines">Goroutines&lt;/h3>
&lt;p>If we run a regular function, we must wait until it ends executing. But if you put a keyword &lt;code>go&lt;/code> in front of the call, the function starts running independently and you can do other things right away, at least conceptually. Not necessarily, remember: concurrent is not the same as parallel.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#00a000">f&lt;/span>(&lt;span style="color:#b44">&amp;#34;Hello&amp;#34;&lt;/span>) &lt;span style="color:#080;font-style:italic">// f runs, we wait
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">go&lt;/span> &lt;span style="color:#00a000">f&lt;/span>(&lt;span style="color:#b44">&amp;#34;Hello&amp;#34;&lt;/span>) &lt;span style="color:#080;font-style:italic">// f starts running
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span>&lt;span style="color:#00a000">g&lt;/span>() &lt;span style="color:#080;font-style:italic">// we don&amp;#39;t wait for f to return
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>(This is similar to running a background shell process with &lt;code>&amp;amp;&lt;/code>).&lt;/p>
&lt;p>It is common to create thousands of goroutines in one Go program. There could be millions! Goroutines aren&amp;rsquo;t free, but they&amp;rsquo;re very cheap.&lt;/p>
&lt;h3 id="channels">Channels&lt;/h3>
&lt;p>Under the hood, goroutines are &lt;em>like&lt;/em> threads, but they aren&amp;rsquo;t OS threads. They are much cheaper, so feel free to create them as you need. They are multiplexed onto OS threads dynamically, and if one goroutine does stop and wait (for example, for input/output operation), no other goroutines are blocked because of that.&lt;/p>
&lt;p>To communicate between goroutines we use &lt;strong>channels&lt;/strong>. They allow goroutines exchange information and sync.&lt;/p>
&lt;p>Here&amp;rsquo;s an example. We create a &lt;code>timerChan&lt;/code> channel of &lt;code>time.Time&lt;/code> values (channels are typed). Then we define and run a function &lt;code>func&lt;/code> which sleeps for some time &lt;code>deltaT&lt;/code> and sends current time to the channel. Then, some time later, we receive a value from the channel. This receiving is blocked until there&amp;rsquo;s a value. In the end, &lt;code>completedAt&lt;/code> will store the time when &lt;code>func&lt;/code> finished.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>timerChan &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#a2f">make&lt;/span>(&lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> time.Time)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">go&lt;/span> &lt;span style="color:#a2f;font-weight:bold">func&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> time.&lt;span style="color:#00a000">Sleep&lt;/span>(deltaT)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timerChan &lt;span style="color:#666">&amp;lt;-&lt;/span> time.&lt;span style="color:#00a000">Now&lt;/span>() &lt;span style="color:#080;font-style:italic">// send time on timerChan
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span>}()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">// Do something else; when ready, receive.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">// Receive will block until timerChan delivers.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">// Value sent is other goroutine&amp;#39;s completion time.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span>completedAt &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;lt;-&lt;/span>timerChan
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="select">Select&lt;/h3>
&lt;p>Goroutines and channels are the fundamental building blocks of concurrent design in Go. The last piece is the &lt;strong>select&lt;/strong> statement. It is similar to a simple switch, but the decision is based on ability to communicate instead of equality.&lt;/p>
&lt;p>The following example produces one of three outputs:&lt;/p>
&lt;ol>
&lt;li>If channel &lt;code>ch1&lt;/code> is ready (has a value), first case executes.&lt;/li>
&lt;li>If channel &lt;code>ch2&lt;/code> is ready (has a value), second case executes.&lt;/li>
&lt;li>If neither is ready, the default case executes.&lt;/li>
&lt;/ol>
&lt;!--listend-->
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">case&lt;/span> v &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;lt;-&lt;/span>ch1:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#00a000">Println&lt;/span>(&lt;span style="color:#b44">&amp;#34;channel 1 sends&amp;#34;&lt;/span>, v)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">case&lt;/span> v &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;lt;-&lt;/span>ch2:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#00a000">Println&lt;/span>(&lt;span style="color:#b44">&amp;#34;channel 2 sends&amp;#34;&lt;/span>, v)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">default&lt;/span>: &lt;span style="color:#080;font-style:italic">// optional
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> fmt.&lt;span style="color:#00a000">Println&lt;/span>(&lt;span style="color:#b44">&amp;#34;neither channel was ready&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If the default clause is not specified in the &lt;code>select&lt;/code>, then the program waits for a channel to be ready. If both ready at the same time, the system picks one randomly.&lt;/p>
&lt;h3 id="closures">Closures&lt;/h3>
&lt;p>Go supports closures, which makes some concurrent calculations easier to express. Closures work as you&amp;rsquo;d expect. Here&amp;rsquo;s a non-concurrent example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> &lt;span style="color:#00a000">Compose&lt;/span>(f, g &lt;span style="color:#a2f;font-weight:bold">func&lt;/span>(x &lt;span style="color:#0b0;font-weight:bold">float&lt;/span>) &lt;span style="color:#0b0;font-weight:bold">float&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">func&lt;/span>(x &lt;span style="color:#0b0;font-weight:bold">float&lt;/span>) &lt;span style="color:#0b0;font-weight:bold">float&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">return&lt;/span> &lt;span style="color:#a2f;font-weight:bold">func&lt;/span>(x &lt;span style="color:#0b0;font-weight:bold">float&lt;/span>) &lt;span style="color:#0b0;font-weight:bold">float&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">return&lt;/span> &lt;span style="color:#00a000">f&lt;/span>(&lt;span style="color:#00a000">g&lt;/span>(x))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f">print&lt;/span>(&lt;span style="color:#00a000">Compose&lt;/span>(sin, cos)(&lt;span style="color:#666">0.5&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="examples">Examples&lt;/h2>
&lt;h3 id="launching-daemons">Launching daemons&lt;/h3>
&lt;p>Here we use a closure to wrap a background operation without waiting for it. The task is to deliver input to output without waiting. The following code copies items from the input channel to the output channel.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">go&lt;/span> &lt;span style="color:#a2f;font-weight:bold">func&lt;/span>() { &lt;span style="color:#080;font-style:italic">// copy input to output
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> &lt;span style="color:#a2f;font-weight:bold">for&lt;/span> val &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#a2f;font-weight:bold">range&lt;/span> input {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> output &lt;span style="color:#666">&amp;lt;-&lt;/span> val
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}()
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>for range&lt;/code> runs until the channel is drained (i.e. until there are no more values in it).&lt;/p>
&lt;h3 id="simple-load-balancer">Simple load balancer&lt;/h3>
&lt;p>You have some jobs. Let&amp;rsquo;s abstract them away with a notion of a unit of work:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">type&lt;/span> Work &lt;span style="color:#a2f;font-weight:bold">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> x, y, z &lt;span style="color:#0b0;font-weight:bold">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A worker task has to compute something based on one unit of work. It accepts two arguments: a channel to get work &lt;em>from&lt;/em> and a channel to output results &lt;em>to&lt;/em>. It then loops over all values of the &lt;code>in&lt;/code> channel, does some calculations, sleeps for some time and delivers the result to the &lt;code>out&lt;/code> channel.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> &lt;span style="color:#00a000">worker&lt;/span>(in &lt;span style="color:#666">&amp;lt;-&lt;/span>&lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> &lt;span style="color:#666">*&lt;/span>Work, out &lt;span style="color:#a2f;font-weight:bold">chan&lt;/span>&lt;span style="color:#666">&amp;lt;-&lt;/span> &lt;span style="color:#666">*&lt;/span>Work) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">for&lt;/span> w &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#a2f;font-weight:bold">range&lt;/span> in {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> w.z = w.x &lt;span style="color:#666">*&lt;/span> w.y
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#00a000">Sleep&lt;/span>(w.z)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> out &lt;span style="color:#666">&amp;lt;-&lt;/span> w
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Because of arbitrary sleeping time and blocking, a solution might feel daunting, but it is rather simple in Go. All we need to do is to create two channels (&lt;code>in&lt;/code>, &lt;code>out&lt;/code>) of jobs, call however many &lt;code>worker&lt;/code> goroutines we need, then run another goroutine (&lt;code>sendLotsOfWork&lt;/code>) which generates jobs and, finally run a regular function which receives the results in the order they arrive.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> &lt;span style="color:#00a000">Run&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> in, out &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#a2f">make&lt;/span>(&lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> &lt;span style="color:#666">*&lt;/span>Work), &lt;span style="color:#a2f">make&lt;/span>(&lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> &lt;span style="color:#666">*&lt;/span>Work)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">for&lt;/span> i &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">0&lt;/span>; i &amp;lt; NumWorkers; i&lt;span style="color:#666">++&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">go&lt;/span> &lt;span style="color:#00a000">worker&lt;/span>(in, out)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">go&lt;/span> &lt;span style="color:#00a000">sendLotsOfWork&lt;/span>(in)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#00a000">receiveLotsOfResults&lt;/span>(out)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This solutions works correctly whether there is parallization or not. It is &lt;em>implicitly&lt;/em> parallel and scalable. The tools of concurrency make it almost trivial to build a safe, working, scalable, parallel design. There are no locks, mutexes, semaphores or other &amp;ldquo;classical&amp;rdquo; tools of concurrency. No explicit synchronization!&lt;/p>
&lt;h3 id="another-load-balancer">Another load balancer&lt;/h3>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/concurrency_is_not_parallelism/balancer.png">
&lt;/figure>
&lt;p>The load balancer needs to distribute incoming work between workers in an efficient way. The requester sends Requests to the balancer:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">type&lt;/span> Request &lt;span style="color:#a2f;font-weight:bold">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fn &lt;span style="color:#a2f;font-weight:bold">func&lt;/span>() &lt;span style="color:#0b0;font-weight:bold">int&lt;/span> &lt;span style="color:#080;font-style:italic">// The operation to perform.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> c &lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> &lt;span style="color:#0b0;font-weight:bold">int&lt;/span> &lt;span style="color:#080;font-style:italic">// The channel to return the result.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that the request contains a channel. Since channels are first-class values in Go, they can be passed around, so each request provides its own channel into which the result should be returned.&lt;/p>
&lt;p>Now the requester function. It accepts a &lt;code>work&lt;/code> channel of Requests. It generates a channel &lt;code>c&lt;/code> which is going to get inside the request. It sleeps for some time. Then it sends on the &lt;code>work&lt;/code> channel a request object with some function and channel &lt;code>c&lt;/code>. It then waits for the answer, which should appear in channel &lt;code>c&lt;/code>, and does some further work.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> &lt;span style="color:#00a000">requester&lt;/span>(work &lt;span style="color:#a2f;font-weight:bold">chan&lt;/span>&lt;span style="color:#666">&amp;lt;-&lt;/span> Request) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> c &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#a2f">make&lt;/span>(&lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> &lt;span style="color:#0b0;font-weight:bold">int&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">// Kill some time (fake load).
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> &lt;span style="color:#00a000">Sleep&lt;/span>(rand.&lt;span style="color:#00a000">Int63n&lt;/span>(nWorker &lt;span style="color:#666">*&lt;/span> &lt;span style="color:#666">2&lt;/span> &lt;span style="color:#666">*&lt;/span> Second))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> work &lt;span style="color:#666">&amp;lt;-&lt;/span> Request{workFn, c} &lt;span style="color:#080;font-style:italic">// send request
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> result &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;lt;-&lt;/span>c &lt;span style="color:#080;font-style:italic">// wait for answer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> &lt;span style="color:#00a000">furtherProcess&lt;/span>(result)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, the worker which accepts Requests is defined by three things:&lt;/p>
&lt;ol>
&lt;li>The channel of Requests. This is a per-worker queue of work to do.&lt;/li>
&lt;li>Number of pending tasks (the load).&lt;/li>
&lt;li>An index.&lt;/li>
&lt;/ol>
&lt;!--listend-->
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">type&lt;/span> Worker &lt;span style="color:#a2f;font-weight:bold">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> requests &lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> Request &lt;span style="color:#080;font-style:italic">// work to do (buffered channel)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> pending &lt;span style="color:#0b0;font-weight:bold">int&lt;/span> &lt;span style="color:#080;font-style:italic">// count of pending tasks
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> index &lt;span style="color:#0b0;font-weight:bold">int&lt;/span> &lt;span style="color:#080;font-style:italic">// index in the heap
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is what the worker does:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> (w &lt;span style="color:#666">*&lt;/span>Worker) &lt;span style="color:#00a000">work&lt;/span>(done &lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> &lt;span style="color:#666">*&lt;/span>Worker) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> req &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;lt;-&lt;/span>w.requests &lt;span style="color:#080;font-style:italic">// get Request from balancer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> req.c &lt;span style="color:#666">&amp;lt;-&lt;/span> req.&lt;span style="color:#00a000">fn&lt;/span>() &lt;span style="color:#080;font-style:italic">// call fn and send result
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> done &lt;span style="color:#666">&amp;lt;-&lt;/span> w &lt;span style="color:#080;font-style:italic">// we&amp;#39;ve finished this request
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Balancer sends requests to most lightly loaded worker. The channel of requests (&lt;code>w.requests&lt;/code>) delivers requests to each worker. The balancer tracks the number of pending requests. Each response goes directly to its requester. Once that is done, the balancer is out of the picture, because each worker communicates with its request via a unique channel.&lt;/p>
&lt;p>Balancer is defined by a pool of workers and a single &lt;code>done&lt;/code> channel through which the workers are going to tell the balancer about each completed request.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">type&lt;/span> Pool []&lt;span style="color:#666">*&lt;/span>Worker
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">type&lt;/span> Balancer &lt;span style="color:#a2f;font-weight:bold">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pool Pool
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> done &lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> &lt;span style="color:#666">*&lt;/span>Worker
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is what the balancer does:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> (b &lt;span style="color:#666">*&lt;/span>Balancer) &lt;span style="color:#00a000">balance&lt;/span>(work &lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> Request) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">case&lt;/span> req &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;lt;-&lt;/span>work: &lt;span style="color:#080;font-style:italic">// received a Request...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> b.&lt;span style="color:#00a000">dispatch&lt;/span>(req) &lt;span style="color:#080;font-style:italic">// ...so send it to a Worker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> &lt;span style="color:#a2f;font-weight:bold">case&lt;/span> w &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;lt;-&lt;/span>b.done: &lt;span style="color:#080;font-style:italic">// a worker has finished ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> b.&lt;span style="color:#00a000">completed&lt;/span>(w) &lt;span style="color:#080;font-style:italic">// ...so update its info
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It runs an infinite loop, forever checking whether there&amp;rsquo;s more work to do (i.e. there&amp;rsquo;s an item on the &lt;code>work&lt;/code> channel), or there&amp;rsquo;s a finished task (i.e. there&amp;rsquo;s an item on the &lt;code>done&lt;/code> channel). If there&amp;rsquo;s work, dispatch it to a worker. If a job is done, update its info.&lt;/p>
&lt;p>To allow the balancer to find the lightest loaded worker, we construct a heap of channels and providing methods such as:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> (p Pool) &lt;span style="color:#00a000">Less&lt;/span>(i, j &lt;span style="color:#0b0;font-weight:bold">int&lt;/span>) &lt;span style="color:#0b0;font-weight:bold">bool&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">return&lt;/span> p[i].pending &amp;lt; p[j].pending
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We&amp;rsquo;re ready to implement the dispatcher:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">// Send Request to worker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> (b &lt;span style="color:#666">*&lt;/span>Balancer) &lt;span style="color:#00a000">dispatch&lt;/span>(req Request) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">// Grab the least loaded worker...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> w &lt;span style="color:#666">:=&lt;/span> heap.&lt;span style="color:#00a000">Pop&lt;/span>(&lt;span style="color:#666">&amp;amp;&lt;/span>b.pool).(&lt;span style="color:#666">*&lt;/span>Worker)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">// ...send it the task.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> w.requests &lt;span style="color:#666">&amp;lt;-&lt;/span> req
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">// One more in its work queue.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> w.pending&lt;span style="color:#666">++&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">// Put it into its place on the heap.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> heap.&lt;span style="color:#00a000">Push&lt;/span>(&lt;span style="color:#666">&amp;amp;&lt;/span>b.pool, w)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>All it needs to do is:&lt;/p>
&lt;ol>
&lt;li>Grab the least loaded worker off the heap.&lt;/li>
&lt;li>Send it the task by writing into its &lt;code>requests&lt;/code> channel.&lt;/li>
&lt;li>Increment its load counter.&lt;/li>
&lt;li>Push it back into the heap.&lt;/li>
&lt;/ol>
&lt;p>That&amp;rsquo;s it!&lt;/p>
&lt;p>The final piece is the completed function which is called every time a worker finishes processing a request. It&amp;rsquo;s essentially the inverse of dispatch:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">// Job is complete; update heap
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> (b &lt;span style="color:#666">*&lt;/span>Balancer) &lt;span style="color:#00a000">completed&lt;/span>(w &lt;span style="color:#666">*&lt;/span>Worker) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">// One fewer in the queue.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> w.pending&lt;span style="color:#666">--&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">// Remove it from heap.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> heap.&lt;span style="color:#00a000">Remove&lt;/span>(&lt;span style="color:#666">&amp;amp;&lt;/span>b.pool, w.index)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#080;font-style:italic">// Put it into its place on the heap.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> heap.&lt;span style="color:#00a000">Push&lt;/span>(&lt;span style="color:#666">&amp;amp;&lt;/span>b.pool, w)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="lessons">Lessons&lt;/h3>
&lt;ul>
&lt;li>A complex problem can be broken down into easy-to-understand components.&lt;/li>
&lt;li>The pieces can be composed concurrently.&lt;/li>
&lt;li>The result is easy to understand, efficient, scalable, and correct.&lt;/li>
&lt;li>The result is maybe even parallel.&lt;/li>
&lt;/ul>
&lt;h3 id="one-more-example-query-a-replicated-db">One more example: query a replicated DB&lt;/h3>
&lt;p>Imagine you have a replicated database (multiple shards). You send the request to all instances, but pick the one response that&amp;rsquo;s first to arrive.&lt;/p>
&lt;p>The function accepts an array of connections and the query to execute. It creates a buffered channel of &lt;code>Result&lt;/code>, limited to the number of connections. Then it runs over all connections and starts a goroutine for each channel. Goroutine delivers the query, waits for response and delivers the answer to &lt;code>ch&lt;/code>. After they all are launched, the function just returns the first value on the channel as soon as it appears.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a2f;font-weight:bold">func&lt;/span> &lt;span style="color:#00a000">Query&lt;/span>(conns []Conn, query &lt;span style="color:#0b0;font-weight:bold">string&lt;/span>) Result {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ch &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#a2f">make&lt;/span>(&lt;span style="color:#a2f;font-weight:bold">chan&lt;/span> Result, &lt;span style="color:#a2f">len&lt;/span>(conns)) &lt;span style="color:#080;font-style:italic">// buffered
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#080;font-style:italic">&lt;/span> &lt;span style="color:#a2f;font-weight:bold">for&lt;/span> _, conn &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#a2f;font-weight:bold">range&lt;/span> conns {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">go&lt;/span> &lt;span style="color:#a2f;font-weight:bold">func&lt;/span>(c Conn) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ch &lt;span style="color:#666">&amp;lt;-&lt;/span> c.&lt;span style="color:#00a000">DoQuery&lt;/span>(query):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }(conn)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a2f;font-weight:bold">return&lt;/span> &lt;span style="color:#666">&amp;lt;-&lt;/span>ch
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>(Note that &lt;code>_&lt;/code> on line 3 stands for an unused, unnamed variable).&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;ul>
&lt;li>Concurrency is powerful.&lt;/li>
&lt;li>Concurrency is not parallelism.&lt;/li>
&lt;li>Concurrency enables parallelism.&lt;/li>
&lt;li>Concurrency makes parallelism (and scaling and everything else) easy.&lt;/li>
&lt;/ul>
&lt;h3 id="more-information">More information&lt;/h3>
&lt;ul>
&lt;li>Go official page: &lt;a href="https://golang.org">golang.org&lt;/a>&lt;/li>
&lt;li>Some history: &lt;a href="https://swtch.com/~rsc/thread/">Bell Labs and CSP Threads&lt;/a>&lt;/li>
&lt;li>A previous talk by Rob Pike: &lt;a href="https://www.youtube.com/watch?v=hB05UFqOtFA">Advanced Topics in Programming Languages: Concurrency/message passing Newsqueak&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://existentialtype.wordpress.com/2011/03/17/parallelism-is-not-concurrency/">Parallelism Is Not Concurrency&lt;/a> (article by Robert Harper)&lt;/li>
&lt;li>&lt;a href="https://swtch.com/~rsc/thread/cws.pdf">A Concurrent Window System&lt;/a> (paper by Rob Pike)&lt;/li>
&lt;li>&lt;a href="https://swtch.com/~rsc/thread/squint.pdf">Squinting at Power Series&lt;/a> (paper by M. Douglas McIlroy)&lt;/li>
&lt;li>&lt;a href="https://ai.google/research/pubs/pub61">Interpreting the Data: Parallel Analysis with Sawzall&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>If you liked this illustrated summary, consider supporting me by purchasing a set of PDF (&lt;a href="https://rakhim.org/ziptalks/SAMPLE%2520Summary%2520of%2520Concurrency%2520Is%2520Not%2520Parallelism%2520(Rob%2520Pike).pdf">preview&lt;/a>), HTML epub and Kindle versions in one nice package. Name your price, starting from $1.&lt;/p>
&lt;p>&lt;a class="gumroad-button" href="https://gum.co/XZBKp?wanted=true" target="_blank" data-gumroad-single-product="true">Get set&lt;/a>&lt;/p></description></item><item><title>The price of complexity</title><link>https://rakhim.org/the-price-of-complexity/</link><pubDate>Wed, 20 Nov 2019 12:48:00 +0200</pubDate><guid>https://rakhim.org/the-price-of-complexity/</guid><description>
&lt;p>Computer programmers often talk about tackling complexity, yet they thrive on complexity. I believe tech people experience a constant dilemma: on one hand, we want things to be simple and straightforward; on the other hand we love complex structures and engineering marvels.&lt;/p>
&lt;p>I think about this today as I&amp;rsquo;m performing some cleanup work on my blog. It runs on Hugo, content is written in Org mode, code is published on Github and the final website is deployed to Netlify. That&amp;rsquo;s a lot of moving parts, and, honestly, it feels excessive. Yet I love this setup.&lt;/p>
&lt;p>Lately I&amp;rsquo;ve been trying to be conscious and mindful about the price my mind pays for all this complexity. I&amp;rsquo;m not a good programmer by any means, so your mileage may vary, but it takes an enormous amount of mental energy for me to re-understand something I already figured out before. Take Hugo for example, a flexible and powerful static website generator. Jekyll, which I used before Hugo, is complex, too, but Hugo drives me crazy sometimes. It&amp;rsquo;s a multi-layered system of interconnected logic and it took me a whole day to &lt;a href="https://rakhim.org/2018/09/moved-from-jekyll-to-hugo-and-ox-hugo/">move from Jekyll&lt;/a>.&lt;/p>
&lt;p>&lt;em>&lt;strong>Intermission&lt;/strong>: I just spent 5 minutes trying to create a relative hyperlink to that other blog post, and couldn&amp;rsquo;t. It took me a while to realize I&amp;rsquo;m in Org mode now, not in Markdown, my syntax was wrong. You know what a Wordpress or Ghost user would&amp;rsquo;ve done? Clicked a link button in their rich WYSIWYG editor and had finished the blog post by this time already.&lt;/em>&lt;/p>
&lt;p>Every time I need to make some changes to the setup — fix layouts, add pages, refactor templates — I feel completely lost. It happens rare enough for my brain to forget the structure and conventions. And this is the case for a dozen of software projects I touch throughout the year.&lt;/p>
&lt;p>This feeling of being lost is similar to un-pausing a video game that was on pause for 6 months. I know I&amp;rsquo;ve been into this, but right now I don&amp;rsquo;t even know what buttons to press.&lt;/p>
&lt;p>There are ways to fight this. One is to dramatically reduce complexity in the first place, maybe even sacrifice some of the features. My setup can be technically replaced by a bunch of HTML files, for example. Or switch to a &amp;ldquo;normal&amp;rdquo; thing like Ghost or Wordpress. Oh, and don&amp;rsquo;t host them yourself, but pay someone to take care of it.&lt;/p>
&lt;p>Another way is to somehow capture the knowledge for easy retrieval. My problem with Hugo is that I rarely touch it, so I forget. I should at least add a README file for myself, explaining the current setup and structure. Keeping documentation in sync with code is another problem, sigh&amp;hellip;&lt;/p>
&lt;p>So far, I only know one good way of solving this: teach. I should just make a course about Hugo on Codexpanse, that&amp;rsquo;ll force me to really understand it and devise a good mental model.&lt;/p></description></item><item><title>User Is Dead</title><link>https://rakhim.org/user-is-dead/</link><pubDate>Tue, 22 Oct 2019 10:51:00 +0300</pubDate><guid>https://rakhim.org/user-is-dead/</guid><description>
&lt;p>&lt;em>User is dead. User remains dead. And we have killed him. How shall we comfort ourselves, the developers, the designers, the growth hackers? What was holiest and the final judge of all that the world has yet owned has bled to death under our a/b-tests and new features. Who will wipe this blood off us? What garbage collector is there for us to clean ourselves? What conference of atonement, what disruptive technology, what sacred meeting shall we have to invent? Is not the greatness of this deed too great for us? Must we ourselves not become users simply to appear worthy of it?&lt;/em>&lt;/p>
&lt;p>By saying &amp;ldquo;God is dead&amp;rdquo; Friedrich Nietzsche tried to express the fear that the decline of religion and the rise of nihilism would plunge the world into chaos.&lt;/p>
&lt;p>Whether you believe that people &lt;em>require&lt;/em> an external source of morality, or you accept the numerous philosophical and scientific arguments for intrinsic morality in behavior of complex animals, the fear remains relevant. The fear is not necessarily about God or religion, but about a moral compass, or lack thereof.&lt;/p>
&lt;p>My arguably tasteless rewrite of Nietzsche&amp;rsquo;s passage expresses another fear, which, I sincerely hope many software developers share. For a long time, we had an external source of morality in software development. &lt;strong>User.&lt;/strong> Simultaneously ephemeral stick figure of UML diagrams and a very real human, a friend, a colleague. The goal, the point, the final judge of success. Your product either satisfied the user or failed. Your software may occasionally have charmed User, but don&amp;rsquo;t be fooled, you are but a servant.&lt;/p>
&lt;p>&amp;ldquo;Move fast and break things&amp;rdquo;, &amp;ldquo;disruption&amp;rdquo; and &amp;ldquo;growth&amp;rdquo; have killed User. No more do we try to offer invisible quality. Instead of software that blends into background by virtue of its non-obtrusive robustness and simplicity, we aspire to create The Product Experience. The first pleases User. The latter pleases Shareholder.&lt;/p>
&lt;p>The Holy Texts are forgotten or, worse, perverted. Apple&amp;rsquo;s famous Human Interface Guidelines ironically describe the many ways in which modern Apple products &lt;em>do not behave&lt;/em>. The Agile manifesto became The Certified Agile Coach Training Program and a cargo cult. The very first principle of the Agile Manifesto states:&lt;/p>
&lt;blockquote>
&lt;p>Our highest priority is to satisfy the customer through early and continuous delivery of valuable software.&lt;/p>
&lt;/blockquote>
&lt;p>How many of us feel this during another a/b-test-driven, metric-based sprint?&lt;/p>
&lt;p>The 7th principle is:&lt;/p>
&lt;blockquote>
&lt;p>Working software is the primary measure of progress.&lt;/p>
&lt;/blockquote>
&lt;p>An honest, but naive assumption that we all share a similar criterion for &amp;ldquo;working&amp;rdquo;. Turns out, the height of the bar is inversely proportional to the proximity of another funding round. By the 90s standards, a lot of today&amp;rsquo;s software is just defunct.&lt;/p>
&lt;p>When User dies, only Beta Tester remains. Beta Tester is not human. It&amp;rsquo;s an expendable tool, a lab rat, a database record.&lt;/p>
&lt;p>What can we do as developers? Honestly, I don&amp;rsquo;t know. Coming up with a manifesto, a set of principles, another guideline — it all seems futile now. The problem isn&amp;rsquo;t that we forgot about User. We just collectively adapted to the new definition of normal. Breaking changes are normal. Updates for the sake of updates are normal. The house is on fire, but this is normal.&lt;/p>
&lt;p>What can we do?.. &lt;em>&amp;ldquo;Must we ourselves not become users?&amp;rdquo;&lt;/em>&lt;/p></description></item><item><title>Be Wary of Self Described Benefits</title><link>https://rakhim.org/be-wary-of-self-described-benefits/</link><pubDate>Tue, 03 Sep 2019 18:06:00 +0300</pubDate><guid>https://rakhim.org/be-wary-of-self-described-benefits/</guid><description>
&lt;p>Picking a university was one of the main tasks in the last year of high school. That and exams. I wasn&amp;rsquo;t sure what to study and which place to pick. I had no idea &lt;em>how&lt;/em> one can make these choices. There weren&amp;rsquo;t too many resources available at the time. So, a lot of us relied on promotional info provided by universities themselves.&lt;/p>
&lt;p>The one I ended up in was called SDU. Its official description says: &amp;ldquo;SDU is a secular higher educational institution located in Kaskelen, near Almaty.&amp;rdquo;&lt;/p>
&lt;p>It was the only university that actually claimed to be secular. That&amp;rsquo;s good, right? I had no interest in studying at a religious institution. Secular is good.&lt;/p>
&lt;p>Turned out, it wasn&amp;rsquo;t very secular. No, we didn&amp;rsquo;t study Holy Texts. Officially, nothing religious was going on. But the Islamic values did indeed feel affecting the policies and decisions everywhere. The dormitories for males and females were separate buildings in different neighborhoods. I had actually never seen the women&amp;rsquo;s dormitory, nor had any male student around. The girls just went to a mysterious place every evening. A large proportion of students held a religious fast (Ramadan). Several instructors gave bonuses to fasting students. So I had to work harder to get the same grade because I was being secular at a secular university.&lt;/p>
&lt;p>I dropped out after 4 months. Thank &lt;em>God&lt;/em>.&lt;/p>
&lt;p>Later, I started to notice this pattern. Often, people, communities, companies and even countries seem to blindly put positive descriptions out. As if they &lt;em>wanted&lt;/em> others to notice.&lt;/p>
&lt;p>It becomes very clear in case of countries. Here are some famous authoritarian regimes:&lt;/p>
&lt;ul>
&lt;li>Democratic People&amp;rsquo;s Republic of Korea&lt;/li>
&lt;li>Democratic Republic of the Congo&lt;/li>
&lt;li>People&amp;rsquo;s Republic of China&lt;/li>
&lt;li>People’s Democratic Republic of Algeria&lt;/li>
&lt;li>Federal Democratic Republic of Ethiopia&lt;/li>
&lt;/ul>
&lt;p>Right.&lt;/p>
&lt;p>So I learned to be wary of self described benefits. It&amp;rsquo;s kind of obvious and silly once you think about it.&lt;/p>
&lt;p>Yours truly,&lt;br>
not passive aggressive at all, Rakhim.&lt;/p></description></item><item><title>Process of Learning</title><link>https://rakhim.org/process-of-learning/</link><pubDate>Mon, 29 Jul 2019 17:24:00 +0300</pubDate><guid>https://rakhim.org/process-of-learning/</guid><description>
&lt;p>A process of learning is analogous to an attempt of building a three-dimensional model from two-dimensional photos.&lt;/p>
&lt;p>You approach a new area of knowledge. You know nothing at all. You stumble upon a first piece:&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Process_of_Learning/lm_1.jpg">
&lt;/figure>
&lt;p>That&amp;rsquo;s more than nothing, but still very little. You don&amp;rsquo;t understand it. At best, you&amp;rsquo;re able to make a few uncertain assumptions.&lt;/p>
&lt;p>Beginners often seek good book recommendations. They would google a top-10 list and then ask &amp;ldquo;which one should I start with?&amp;rdquo;. The answer is almost always &amp;ldquo;it doesn&amp;rsquo;t matter&amp;rdquo;. Start with &lt;em>any&lt;/em> non-shitty book. One book alone would not provide enough data to build a good model anyway. If you want to really understand something, you&amp;rsquo;ll have to read several books, listen to different people, try various approaches. Nobody knows what&amp;rsquo;s going to work best for a particular person. Each model building machine is unique. The starting order of feeding it data is not very important, unless that information is truly harmful.&lt;/p>
&lt;p>(&amp;ldquo;What is harmful&amp;rdquo; is a topic for another discussion, and I by no means argue that finding non-harmful books is an easy task. In fact, I&amp;rsquo;d call many popular programming books harmful, especially when it comes to teaching the basics of programming with Java. So, at least minimize the potential harm by not focusing on a single book.)&lt;/p>
&lt;p>Another basic book will provide a different view:&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Process_of_Learning/lm_2.jpg">
&lt;/figure>
&lt;p>You still can&amp;rsquo;t understand it. But keep going, and at some point you&amp;rsquo;ll arrive at a single complete picture.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Process_of_Learning/lm_3.jpg">
&lt;/figure>
&lt;p>This tells you more than before, you can even make some conclusions or reason about certain aspects of the object. But you don&amp;rsquo;t have a complete model yet, one 2D picture is not enough. Your brain haven&amp;rsquo;t met such objects in the past, there&amp;rsquo;s nothing to cling on to. This is why it&amp;rsquo;s essential to have various sources of information: other books, people, lessons, different mediums.&lt;/p>
&lt;p>New pieces keep emerging, and it&amp;rsquo;s disconcerting. They don&amp;rsquo;t seem to make any sense, this puzzle feels broken.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Process_of_Learning/lm_4.jpg">
&lt;/figure>
&lt;p>This is the toughest stage of the learning journey. Often, any hope is lost. Disconnected pieces provoke the feeling of meaninglessness. You can&amp;rsquo;t see the big picture.&lt;/p>
&lt;blockquote>
&lt;p>I think I understand each individual topic, but have no idea how they are connected. And why did I learn all that. Nothing makes sense&amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;p>But if you keep going, soon you&amp;rsquo;ll get to another full picture:&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Process_of_Learning/lm_5.jpg">
&lt;/figure>
&lt;p>Interesting! A completely different point of view. Same object, new aspects. A complete 3D model is still impossible to deduce, but there&amp;rsquo;s more space for assumptions. Having multiple pictures increases the chances of seeing &lt;em>something&lt;/em> familiar. It&amp;rsquo;s a new topic alright, but topics are rarely completely isolated from the universe.&lt;/p>
&lt;p>A few more pictures and you get a pretty accurate model.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Process_of_Learning/lm_6.jpg">
&lt;/figure>
&lt;p>The first picture was extremely valuable, but each new picture brings less and less valuable data. At some point you have a decent model in your head, so that new pictures don&amp;rsquo;t give you anything new.&lt;/p>
&lt;p>This analogy helps me learn new things. I try to remember the following:&lt;/p>
&lt;ol>
&lt;li>At first, everything is interesting and easy. The first picture gives a lot of data at once. This is the pleasant stage.&lt;/li>
&lt;li>In the middle motivation will decrease. That&amp;rsquo;s okay. Keep getting data and trust the system.&lt;/li>
&lt;li>Do not focus on a single picture. Maybe, in order to understand it you need another picture first.&lt;/li>
&lt;li>At some point, notice the diminishing returns of new data. Consider increasing the area of study.&lt;/li>
&lt;/ol></description></item><item><title>Bicycles and Love</title><link>https://rakhim.org/bicycles-and-love/</link><pubDate>Sat, 27 Jul 2019 11:47:00 +0300</pubDate><guid>https://rakhim.org/bicycles-and-love/</guid><description>
&lt;p>I love bicycles.&lt;/p>
&lt;p>The first memories I have are bike-related. The best ones are, too.&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/Bicycles_and_Love/my_first_bicycle.jpg" alt="">&lt;br>
&lt;em>My first bicycle was a tricycle&lt;/em>&lt;/p>
&lt;p>I don&amp;rsquo;t remember a time I didn&amp;rsquo;t bike. Bicycle means freedom.&lt;/p>
&lt;p>My first serious bicycles, the ones that let me explore the city, were old, steel soviet tanks. They were simultaneously indestructible and always broken.&lt;/p>
&lt;p>My friend Eugene and I spent summers biking around town, forests and river valleys. It was awesome.&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/Bicycles_and_Love/bikes_river.jpg" alt="">&lt;br>
&lt;em>Eugene riding away, my bike in front&lt;/em>&lt;/p>
&lt;p>Those bikes were all-purpose vehicles. Well, at least for us they were. Pavement, gravel, sand, water, whatever. They go where we go. They were simple and stupid, and I loved that. No speeds, no hand brakes.&lt;/p>
&lt;p>I used to live with my grandma until I was 13. Her apartment had a narrow corridor connecting the entrance to the room, and I can&amp;rsquo;t imagine it without a bicycle. It was always there, and when it wasn&amp;rsquo;t, I wasn&amp;rsquo;t home. Out biking god knows where.&lt;/p>
&lt;p>I had a dream of driving a city bus, so quite often I spent hours biking along the bus routes, stopping at bus stops, emulating a bus. An old pen was diligently placed inside the handlebar, sticking about 2 inches outwards: it was my fake turn signal lever. I had to make the turn signal clicking sound myself, of course.&lt;/p>
&lt;p>Then I moved to Canada. My next bike was a pretty cool steel Schwinn I got as a gift from my host family in Ottawa. Bicycle freedom had suddenly expanded. Now I could ride to another province where people speak a different language and traffic lights are horizontal. I could bring my bike on the train and get further than ever. I could disappear into the city and nobody could find me.&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/Bicycles_and_Love/schwinn.jpg" alt="">&lt;br>
&lt;em>I didn&amp;rsquo;t appreciate this bike enough. I wish I could get that frame back&amp;hellip;&lt;/em>&lt;/p>
&lt;p>Several random used bikes later I decided I&amp;rsquo;m ready to spend serious bucks and try road biking. With 1000 Canadian dollars in hand, I walked into a bike shop in western Ottawa and got myself a beautiful aluminium Trek 1.1.&lt;/p>
&lt;p>Freedom was reinvented once more. With this light, fast bike I could go further than ever before. First 100 km ride was a revelation. Then 350km in 2-day group event. Then 500km in three days. Then more than a 1000 km in a week across multiple provinces to see the ocean.&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/Bicycles_and_Love/trek.jpg" alt="">&lt;br>
&lt;em>At the end of my long trip to see the Atlantic Ocean, New Brunswick, Canada&lt;/em>&lt;/p>
&lt;p>I was in love again.&lt;/p>
&lt;p>I was about to finish my computer science degree and was feeling somewhat &lt;a href="https://rakhim.org/2018/10/dazed-depressed-defunct/">down&lt;/a> at times. That bike was the best thing in my life.&lt;/p>
&lt;p>Biking and computer science made perfect sense to me. Both are about efficiency. Both are tools, but at the same time fun in and of themselves. Both let you be alone.&lt;/p>
&lt;p>They let you disappear into a vast domain and get lost.&lt;/p>
&lt;p>After moving from Canada to Kazakhstan, I stopped biking. It took me almost 5 years to re-ignite the passion.&lt;/p>
&lt;p>After moving to Finland, I decided to try a single-speed and got a pretty Swedish Stålhästen Sport fixed gear bike with a flip-flop hub. Fixed gear is not my cup of tea, so I flipped the wheel and started discovering the Helsinki area.&lt;/p>
&lt;p>It was pretty sexy.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Bicycles_and_Love/fixie.jpeg">
&lt;/figure>
&lt;p>But alas, we weren&amp;rsquo;t for each other. We just didn&amp;rsquo;t click. A bicycle is an intimate object for me, along with backpacks and computers. I can&amp;rsquo;t just have one, I must love it. So I sold it to someone who, hopefully, loved it.&lt;/p>
&lt;p>And got myself a cyclocross Kona Rove Al 2015.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Bicycles_and_Love/kona_rove_al.jpeg">
&lt;/figure>
&lt;p>The relationship with this one was complicated. I loved it and sometimes hated it.&lt;/p>
&lt;p>I loved my Kona because it was very comfortable, fast and looked very cool. Its brownish color was a perfect fit for the forests and fields I took it to. Disc brakes — first for me — made me more confident on narrow descents and uneven terrain.&lt;/p>
&lt;p>But I didn&amp;rsquo;t love the maintenance. It was equipped with the cheapest components. Decent, but not awesome. Shimano Claris groupset and mechanic disc brakes were never perfect. The bike was never perfectly tuned, never perfectly silent. And I&amp;rsquo;m ashamed to admit, but I&amp;rsquo;m a crappy handyman when it comes to bikes. Actually, I&amp;rsquo;m a crappy handyman in all areas where &amp;ldquo;undo&amp;rdquo; is not an option, but I&amp;rsquo;m particularly bad with bicycles. Derailleur adjustment and disc brake calibration are black magic to me. Hours of sweat and profanity and I end up with a subpar configuration. It works and it&amp;rsquo;s fine, but it&amp;rsquo;s just not very good. Then I say &amp;ldquo;screw this!&amp;rdquo; and bring the bike to a mechanic.&lt;/p>
&lt;p>By the way, good bike mechanics are pretty expensive in Finland.&lt;/p>
&lt;p>I&amp;rsquo;ve later learned that better groupsets and hydraulic brakes (or at least the hybrid dual-piston ones) are so much better, and I honestly almost convinced myself to spend two grand on a &lt;em>very good bicycle&lt;/em>. Because then I will ride more, right?&lt;/p>
&lt;p>Kona Rove Al was my bike for three good seasons, but the furthest I took it was a 120 km one day ride. It never saw other regions of the country even. We weren&amp;rsquo;t too adventurous together.&lt;/p>
&lt;p>This summer, after another failed attempt to eliminate 100% of the disc brakes noise, I decided to say goodbye to Kona and look for something new.&lt;/p>
&lt;p>A romanticized and, perhaps, irrational desire to simplify struck me again. What is the simplest bicycle possible? Single speed with coaster brake. I love handbrakes, so, rim brakes then. But I wasn&amp;rsquo;t sure I could do single-speed again. I love my knees.&lt;/p>
&lt;p>Then I discovered internal hubs. They look and feel like single-speed from the outside: no derailleur, no cable slack. My girlfriend and I got ourselves small folding bikes with Shimano Nexus 3-speed hubs and they are very nice. Simple, reliable, solid.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Bicycles_and_Love/nexus.jpg">
&lt;/figure>
&lt;p>There also exist 7-speed Nexus hubs, but I haven&amp;rsquo;t tried one. They are pretty heavy and it&amp;rsquo;s a pain to install them with dropbars.&lt;/p>
&lt;p>Turns out, there are also automatic hubs! Woah! Extremely curious, I got myself an old Fixie Inc. Floater with Sram Automatix 2-speed automatic hub.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Bicycles_and_Love/fixie_floater_sram.jpg">
&lt;/figure>
&lt;p>The experience is&amp;hellip; interesting. You just start pedaling and at about 15 km/h the gear changes automatically. It&amp;rsquo;s pretty solid, no wobble or anything. It&amp;rsquo;s like you&amp;rsquo;re suddenly teleported into higher gear. This shift point it ridiculously low though. It was pretty easy to disassemble the hub and change the shift point by unwinding a tiny metallic spring (&lt;a href="https://bikesfornoreason.blogspot.com/2014/01/sram-automatix-2-speed-hack.html?m=1">here&amp;rsquo;s a good description&lt;/a>).&lt;/p>
&lt;p>At first, it seemed like a good compromise. Almost as simple as a single-speed, but the second gear allows to keep a sane cadence at higher speeds. Unfortunately, the hub is not as isolated from the outside world as other internal gear hubs, so it will require some maintenance.&lt;/p>
&lt;p>I took it on multiple rides and just didn&amp;rsquo;t love it. What a picky, delicate flower I am.&lt;/p>
&lt;p>And then, completely by chance, I had a chance to ride Bombtrack Arise. A single-speed, steel gravel bike.&lt;/p>
&lt;p>Yes! It made me smile!&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/Bicycles_and_Love/bombtrack_arise.jpg">
&lt;/figure>
&lt;p>I&amp;rsquo;m not a fast rider, so when it comes to gearing, comfortable climbing is more important to me than going fast. Finding the perfect ratio for a single-speed takes time and lots of cogs, but this bike came with what seems to be the perfect ratio for me: 42 teeth chain ring and 17 teeth rear sprocket. With 28&amp;quot; tires, it yields 69 gear inches, which works very well for the steepest hills in my area and is good enough for descents.&lt;/p>
&lt;p>This &lt;a href="https://www.bikecalc.com/fixed">handy bike calculator&lt;/a> helps to figure out the good ratio for a given cadence and speed. For my new bike, 90 rpm produces 28.4 km/h, and the high cadence of 130 rpm yields 41 km/h.&lt;/p>
&lt;p>Single speed forces me to be more disciplined and think ahead. I can&amp;rsquo;t just attack a hill on a granny gear anymore, I have to save as much momentum as possible. At the same time, this creates ultimate freedom. I don&amp;rsquo;t think about optimal gearing, I am always in the &lt;del>wrong&lt;/del> right gear.&lt;/p>
&lt;p>Steel frame is lovely and comfortable, it feels softer, yet more confident. The simplicity of not having so many extra things inspires almost a zen-like feeling. Oh, and it&amp;rsquo;s quiet. No chain slap.&lt;/p>
&lt;p>I am yet to take the Bombtrack on a truly long ride, and it might not be as peachy as I describe. But so far, with about 250km behind, I remain in love.&lt;/p>
&lt;p>Find a bike you enjoy, it will make you happier.&lt;/p></description></item><item><title>80-characters limit for text is wrong</title><link>https://rakhim.org/80-char-is-wrong/</link><pubDate>Thu, 30 May 2019 12:27:00 +0300</pubDate><guid>https://rakhim.org/80-char-is-wrong/</guid><description>
&lt;p>I believe the 80-characters (or any other number) line limit for text to be wrong. Not archaic or irrelevant, but wrong. It violates a fundamental idea of computer science: separating layers of abstraction.&lt;/p>
&lt;p>Not talking about code today, although, I don&amp;rsquo;t think a strict limit is a good thing there either, for other reasons. I&amp;rsquo;m talking about human text.&lt;/p>
&lt;p>Many programmers stick to the 80-characters line length limit while writing documentation, emails, etc. Emacs and other editors even have special modes or plugins for automatic hard wrapping.&lt;/p>
&lt;p>Often, results look like so:&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/email.png">
&lt;/figure>
&lt;p>Without hard-wrapping, this email had a chance to look normal everywhere. Any app can interpret and present it in any way. With hard-wrapping though, this email can only look normal in certain conditions. Namely, a certain lower limit for window width.&lt;/p>
&lt;p>Imagine joining a web project and seeing a &lt;code>users&lt;/code> database table with values like &lt;code>&amp;lt;strong&amp;gt;Jason Norwig&amp;lt;/strong&amp;gt;&lt;/code>. Your reaction might include profanity, because mixing data and presentation is wrong. Person&amp;rsquo;s name (data) and its presentation (HTML) are different layers of abstraction.&lt;/p>
&lt;p>Hard-wrapping lines by inserting &lt;code>\n&lt;/code> symbols where they don&amp;rsquo;t have any semantic meaning is the same sin. The whole character return thing was relevant in a context where data was inseparable from presentation: typewriters and paper.&lt;/p>
&lt;p>A key argument for hard-wrapping goes something like this: &amp;ldquo;modern screens are too wide, it&amp;rsquo;s uncomfortable to read long lines&amp;rdquo;. But modern screens come with modern apps, which can handle presentation to your preference. By modern I mean &amp;ldquo;developed after the &amp;rsquo;70s&amp;rdquo;. Text editors (including vim and Emacs) have been able to soft-wrap lines at arbitrary comfortable column for decades.&lt;/p>
&lt;p>If your text presentation tool can&amp;rsquo;t present text to your preference, consider replacing it. Moving this responsibility into data itself is not a solution.&lt;/p>
&lt;p>So can we please&lt;br>&lt;br>
stop doing this&lt;br>&lt;br>
to each other.&lt;br>&lt;/p></description></item><item><title>Rethinking Twitter</title><link>https://rakhim.org/rethinking-twitter/</link><pubDate>Wed, 15 May 2019 17:51:00 +0300</pubDate><guid>https://rakhim.org/rethinking-twitter/</guid><description>
&lt;h2 id="good-old-days">Good old days&lt;/h2>
&lt;p>Twitter is the only social media I&amp;rsquo;ve ever used. Sure, I had accounts on other sites, but was never really engaged elsewhere. I couldn&amp;rsquo;t understand all those &amp;ldquo;facebook addiction&amp;rdquo; stories, honestly. Facebook was always extremely boring, while Twitter was always exciting.&lt;/p>
&lt;p>From the beginning, it was a perfect, geeky, simple thing that is kind of hard to explain. Just a stream of short thoughts. Cool links. One liners. A public chat room.&lt;/p>
&lt;p>I joined Twitter in 2007. Woah, 12 years ago! I was studying in a university, and smartphones weren&amp;rsquo;t really a thing yet. This is totally a &amp;ldquo;back in my days&amp;rdquo; kind of rhetoric, but hell, that Twitter was nice.&lt;/p>
&lt;p>Most people didn&amp;rsquo;t have a Twitter account. Because it&amp;rsquo;s weird, what&amp;rsquo;s the point? So, like other internet communities, Twitter started as a selected group of weirdos, acting constantly amazed about the fact that they&amp;rsquo;re all here. Tweeting.&lt;/p>
&lt;p>The timeline was chronological, and the world seemed ordered. It was more like RSS than like Facebook or TV: you could only see the people you explicitly subscribed to. Never an unknown face in your timeline. Retweets weren&amp;rsquo;t a feature, it was just a thing people did: copying a tweet and putting &amp;ldquo;RT&amp;rdquo; in front of it. I think it&amp;rsquo;s a better strategy overall: if you retweet something this way, you put your face on the message, you own it now. So, the value of retweets was higher, nobody wanted to copy every mildly interesting thing.&lt;/p>
&lt;p>There were no likes, this added sugar of engagement and interaction. You like something? Well, say it. Tell it to the author, do a retweet with an encouraging comment. Do something you&amp;rsquo;d do in real life.&lt;/p>
&lt;p>Twitter was allowing people to communicate via a digital medium. Not communicate in a new digital way.&lt;/p>
&lt;p>And Twitter wasn&amp;rsquo;t like TV.&lt;/p>
&lt;h2 id="outsourced-schizophrenia">Outsourced schizophrenia&lt;/h2>
&lt;p>A HackerNews user had &lt;a href="https://news.ycombinator.com/item?id=16292024">put&lt;/a> it nicely:&lt;/p>
&lt;blockquote>
&lt;p>I think the problem is that Twitter is a platform for evolutionary selection of slogan-based-dialog. I kind of imagine two armies standing across a battlefield from one another carefully deciding which volley of pithy digs to throw at one another.&lt;/p>
&lt;/blockquote>
&lt;p>Yeah. It&amp;rsquo;s like switching TV channels every 5 seconds.&lt;/p>
&lt;p>&lt;a href="https://adambrault.com/blog/i-quit-twitter-for-a-month">This article&lt;/a> had hit home hard for me. I&amp;rsquo;ll just include a few quotes:&lt;/p>
&lt;blockquote>
&lt;p>There are people you know whose voice you can hear in your head [&amp;hellip;], or people who you even consult with in your head for wisdom (&amp;ldquo;What advice would my dad give here?&amp;rdquo;) [&amp;hellip;]&lt;/p>
&lt;p>&amp;hellip;it can be a huge mental lease we&amp;rsquo;re signing when we invite a few hundred people into our Twitter life. To some degree, it is choosing to subject ourselves to thousands of ads throughout the day, but ones that come from trusted sources we care about, so they&amp;rsquo;re actually impactful.&lt;/p>
&lt;/blockquote>
&lt;p>Yes! And especially today, thanks to both natural and artificial (algorithmic) selection, these short ads are hyper-optimized for stickiness and virality.&lt;/p>
&lt;blockquote>
&lt;p>We&amp;rsquo;ve surrendered a massive amount of mental and emotional energy without making the explicit choice to do so&amp;ndash;it&amp;rsquo;s simply imposed on us by subscribing to the channel and checking it.&lt;/p>
&lt;/blockquote>
&lt;p>The worst part is that nowadays you see a chaotic stream from people you &lt;em>haven&amp;rsquo;t&lt;/em> explicitly subscribed to: retweets, liked and promoted tweets, ads, etc.&lt;/p>
&lt;blockquote>
&lt;p>Mentally, we just aren&amp;rsquo;t capable of simultaneously empathizing with hundreds of people&amp;ndash;let alone thousands or millions. The result is we either build up a calloused, jaded, or cynical defense against empathy or find a way to block out more.&lt;/p>
&lt;/blockquote>
&lt;p>I can hardly manage interacting with a single person for more than a few hours, let alone with hundreds, even via virtual channels.&lt;/p>
&lt;p>This one describes me very well too:&lt;/p>
&lt;blockquote>
&lt;p>I&amp;rsquo;ll admit: I&amp;rsquo;m an annoyingly oversensitive person. I do believe this is both a strength and a weakeness. [&amp;hellip;] I also have a tendency to listen carefully to any criticism or disagreement I hear, internalize it, reflect on it, and evaluate it, then conclude some thought on it. Until I do that, it just sort of hangs there in my head. The degree to which it dominates my headspace is largely a question of how much it impacts me.&lt;/p>
&lt;/blockquote>
&lt;p>If nothing else, I want you to consider this quote:&lt;/p>
&lt;blockquote>
&lt;p>[&amp;hellip;] Twitter is outsourced schizophrenia. I have a couple hundred voices I have consensually agreed to allow residence inside my brain.&lt;/p>
&lt;/blockquote>
&lt;p>I assume not everyone is like that, and not everyone experiences social media this way. But Adam, the author of that post, does. I do too.&lt;/p>
&lt;p>His essay is fantastic, really:&lt;/p>
&lt;blockquote>
&lt;p>I&amp;rsquo;ve realized how Twitter has made me break up my thoughts into tiny, incomplete, pieces&amp;ndash;lots of hanging ideas, lots of incomplete relationships, punctuated by all manner of hanging threads and half-forked paths.&lt;/p>
&lt;/blockquote>
&lt;h2 id="change">Change&lt;/h2>
&lt;p>But Twitter has been very beneficial to my career. With a few thousand followers, links to my projects had consistently brought me hundreds of hits, readers, clients. This is my strategic failure: I haven&amp;rsquo;t been working on maintaining an independent outlet for my audience, a newsletter or a consistent blog. For more than a decade, Twitter has been the main channel of distributing my work.&lt;/p>
&lt;p>So, I decided to make a few changes and conduct an experiment.&lt;/p>
&lt;ol>
&lt;li>Unfollow everybody. No more timeline.&lt;/li>
&lt;li>Tweet via an app like Buffer, without opening Twitter itself.&lt;/li>
&lt;li>Open Twitter and reply to mentions only on weekends.&lt;/li>
&lt;li>Invest energy into maintaining a consistent blog and a personal newsletter. You can subscribe to the newsletter &lt;a href="https://buttondown.email/rakhim">here&lt;/a> or with a form below:&lt;/li>
&lt;/ol>
&lt;form
action="https://buttondown.email/api/emails/embed-subscribe/rakhim"
method="post"
target="popupwindow"
onsubmit="window.open('https://buttondown.email/rakhim', 'popupwindow')"
class="embeddable-buttondown-form"
>
&lt;label for="bd-email">Enter your email&lt;/label>
&lt;input type="email" name="email" id="bd-email">
&lt;input type="hidden" value="1" name="embed"/>
&lt;input type="submit" value="Subscribe" />
&lt;/form>
&lt;p>I will maintain this system at least until the end of 2019 and we&amp;rsquo;ll see how it goes.&lt;/p>
&lt;p>TWEET THIS NOW!&lt;/p></description></item><item><title>Easy slides (for me)</title><link>https://rakhim.org/easy-slides-for-me/</link><pubDate>Sun, 21 Apr 2019 14:16:00 +0300</pubDate><guid>https://rakhim.org/easy-slides-for-me/</guid><description>
&lt;p>As a software developer, at some point you discover simple slides and presentation generators: Markdown-to-PDF/HTML converters, Emacs extensions, LaTeX exporters, VIM plugins, etc. The idea makes perfect sense, because:&lt;/p>
&lt;ol>
&lt;li>I don&amp;rsquo;t want to use PowerPoint or Keynote.&lt;/li>
&lt;li>I don&amp;rsquo;t want to use the mouse.&lt;/li>
&lt;li>I want plain text.&lt;/li>
&lt;li>I want simplicity.&lt;/li>
&lt;/ol>
&lt;p>You can create presentations without leaving your favourite editor or command line. But unfortunately, the majority of the results are just text with an occasional poorly positioned funny GIF (that didn&amp;rsquo;t load because wifi is down).&lt;/p>
&lt;p>I completely understand the desire to make things as simple as possible and forget about clunky GUI-based presentation software. I don&amp;rsquo;t like them either, and yeah, I&amp;rsquo;d love to be able to do things from the comfort of my text editor. You might say that text is mostly enough, animations and other flashy effects don&amp;rsquo;t contribute to the value.&lt;/p>
&lt;p>But I argue that animations, visualizations and transitions are tools, and like any other tool, they add value when used correctly. By sticking to text-only slide generators, you disregard a whole set of tools and potentially a whole set of problems they might help solve.&lt;/p>
&lt;p>A title flying out from the corner probably doesn&amp;rsquo;t do any good, the effect has no meaning. But if you want to explain something non-trivial (not to you, but to your audience), consider using &lt;em>something&lt;/em> to illustrate your point or even just to focus viewer&amp;rsquo;s attention. It&amp;rsquo;s not about animation or burning flames effect, it&amp;rsquo;s about &lt;em>anything&lt;/em> above the typewriter in the pyramid of technology.&lt;/p>
&lt;p>Dimming. Colors. Shapes. Transparency.&lt;/p>
&lt;p>Computer science is full of complex ideas, multiple levels of abstraction, non-obvious connections and relations. It pains me to see whole presentations, thick books and long manuals with essentially zero visuals, zero attempt to convey an idea with something other than text.&lt;/p>
&lt;p>Your slim Markdown-to-PDF converter serves one purpose: make &lt;em>your&lt;/em> life easier. Nothing wrong with that. But there are also viewers who might benefit from a more detailed visual presentation. Of course, not all viewers would. For many, text and your speech are more than enough, after all, many of us became programmers because of the ability to understand complex, abstract, non-visual ideas to begin with. This is where lack of diversity starts from, I believe. We filter out people by their adaptability to certain styles and formats of explanations. We filter out people by their learning medium.&lt;/p>
&lt;p>&lt;em>You need a cartoon to understand closures? Good luck. Maybe, programming isn&amp;rsquo;t for you?..&lt;/em>&lt;/p>
&lt;p>Now, I understand that it takes time, and you might just not have enough of it. I am not bashing these wonderful tools and not saying you &lt;em>must&lt;/em> produce visuals and animations. I just wanted to remind you that plain-text presentations are compromises. It&amp;rsquo;s absolutely fine to mindfully and intentionally make compromises.&lt;/p></description></item><item><title>How to podcast</title><link>https://rakhim.org/how-to-podcast/</link><pubDate>Mon, 15 Apr 2019 16:23:00 +0300</pubDate><guid>https://rakhim.org/how-to-podcast/</guid><description>
&lt;p>Here is a TL;DR version:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Record using whatever. Even smartphones produce decent sound nowadays. Just make sure to have a consistent volume, i.e. maintain constant distance between your mouth and the phone.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Compress to 96kbps mp3 constant bitrate. Or use an online encoder like &lt;a href="https://pinecoder.pinecast.com/">Pinecoder&lt;/a>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Pick a podcast hosting provider:&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://pinecast.com">pinecast.com&lt;/a>: starts at $5/month with unlimited storage, unlimited shows, basic analytics and website generator.&lt;/li>
&lt;li>&lt;a href="https://simplecast.com/">simplecast.com&lt;/a>: starts at $15/month per show. Cool player, website generator.&lt;/li>
&lt;li>&lt;a href="https://transistor.fm/">transistor.fm&lt;/a>: starts at $19/month with unlimited storage, unlimited shows. Limited at 5k downloads per month.&lt;/li>
&lt;li>Or one of many others, including &lt;a href="https://www.podbean.com/">podbean&lt;/a>, &lt;a href="https://www.libsyn.com/">libsyn&lt;/a>, &lt;a href="https://fireside.fm/">fireside&lt;/a> or even &lt;a href="https://soundcloud.com/for/podcasting">soundcloud&lt;/a> (probably not the best idea, since the company&amp;rsquo;s finances aren&amp;rsquo;t great)&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>Add your podcast to public catalogs:&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://podcastsconnect.apple.com/">Apple Podcasts (iTunes)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://podcasters.spotify.com/">Spotify&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://pocketcasts.com/submit">PocketCasts&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://play.google.com/music/podcasts/portal/">Google Podcasts&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://player.fm/importer/feed">Player FM&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://help.tunein.com/contact/add-podcast-S19TR3Sdf">TuneIn&lt;/a>&lt;/li>
&lt;/ol>
&lt;p>Some podcast hosting providers can submit your feed to those catalogs on your behalf, but I suggest you do everything manually to keep 100% control over your content.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="preparation">Preparation&lt;/h2>
&lt;p>This part is very subjective, so I&amp;rsquo;ll just describe my own process.&lt;/p>
&lt;p>Since each of my episodes follows a specific topic, I start thinking about it weeks before, just allowing ideas, thoughts and just random pieces of info simmer in my head for hours. I do my best to write down these things, but often just forget them. It&amp;rsquo;s okay. I have learned to let go of &amp;ldquo;obviously genius&amp;rdquo; ideas.&lt;/p>
&lt;p>I never hesitate to write down whatever comes to mind though. It could be an analogy, a funny phrase, a weird comparison.&lt;/p>
&lt;p>At some point I feel ready to jot down the structure. I&amp;rsquo;ve been using a large notepad and iPad pro with Apple Pencil, but lately have been enjoying &lt;a href="https://mindnode.com/">MindNode&lt;/a> on the desktop. Having a large, tree-like structure fits my way of thinking very well. I just &amp;ldquo;walk&amp;rdquo; the tree during recording, trying to visit all the nodes.&lt;/p>
&lt;h2 id="recording">Recording&lt;/h2>
&lt;p>There are different levels here.&lt;/p>
&lt;h3 id="level-1-just-use-whatever-dot">Level 1: Just use whatever.&lt;/h3>
&lt;p>Your phone or internal microphone of your laptop are &lt;em>okay&lt;/em>. Not great by any means, but keep these in mind and you&amp;rsquo;ll be fine:&lt;/p>
&lt;ul>
&lt;li>Pick a quiet spot. Humming noises are the worst (loud fridge, AC, etc.)&lt;/li>
&lt;li>Maintain constant distance between your mouth and the mic&lt;/li>
&lt;li>If possible, record in a smaller room with lots of soft objects to minimize echo. Books, blankets, pillows, curtains reduce echo significantly.&lt;/li>
&lt;/ul>
&lt;h3 id="level-2-usb-headset">Level 2: USB headset&lt;/h3>
&lt;p>A good and cheap way to improve your sound significantly is using almost any good USB headset. But it &lt;em>must&lt;/em> be USB. Those headsets that connect via jack cables are simply using your computer&amp;rsquo;s audio card and the sound might not be much better than with an internal mic.&lt;/p>
&lt;h3 id="level-3-usb-microphone">Level 3: USB microphone&lt;/h3>
&lt;p>USB mics are the combination of &amp;ldquo;real&amp;rdquo; microphones and the convenience of USB. You usually don&amp;rsquo;t need anything else, just plug and play.&lt;/p>
&lt;p>There are several good choices in this category:&lt;/p>
&lt;ol>
&lt;li>&lt;a href="http://www.rode.com/microphones/podcaster">Rode Podcaster&lt;/a> (my only mic for 10+ years, produced hundreds of podcasts and videos with it)&lt;/li>
&lt;li>&lt;a href="https://www.bluedesigns.com/products/yeti/">Blue Yeti&lt;/a>&lt;/li>
&lt;li>Audio-Technica ATR2100-USB&lt;/li>
&lt;li>Shure PG42-USB&lt;/li>
&lt;li>Audio-Technica AT2020USB+&lt;/li>
&lt;/ol>
&lt;h3 id="level-4-xlr-microphone">Level 4: XLR microphone&lt;/h3>
&lt;p>When you&amp;rsquo;re ready to go &amp;ldquo;full podcaster&amp;rdquo;, go read &lt;a href="https://marco.org/podcasting-microphones">Marco Arment&amp;rsquo;s Podcasting Microphones Mega-Review&lt;/a>. There are audio samples, too.&lt;/p>
&lt;h2 id="editing">Editing&lt;/h2>
&lt;p>I edit my podcasts heavily. 60 minutes of recording usually result in about 35-45 minutes of end result. Marco Arment wrote about this in &lt;a href="https://marco.org/2014/11/29/easy-listening">Easy listening&lt;/a> and I agree: &amp;ldquo;you just need to care&amp;rdquo;.&lt;/p>
&lt;p>Free and open source &lt;a href="https://www.audacityteam.org/download/">Audacity&lt;/a> is more than capable for both recording and editing, but, to be honest, I find it extremely cumbersome and ugly. Once you&amp;rsquo;re serious, I think it&amp;rsquo;s worth to invest into buying and learning a tool like Adobe Audition (my choice) or Apple Logic Pro X.&lt;/p>
&lt;h2 id="publishing">Publishing&lt;/h2>
&lt;p>Podcast is basically an RSS feed with media files. You can generate it yourself and host mp3 files wherever. If you already have a blog running on Wordpress, it makes sense to just continue using it. On wordpress.com they have a &lt;a href="https://en.support.wordpress.com/audio/podcasting/">special feature for podcasting&lt;/a>, and if you run your own wordpress instance, &lt;a href="https://wordpress.org/plugins/seriously-simple-podcasting/">this plugin&lt;/a> will help you.&lt;/p>
&lt;p>It is much easier to use one of the specialized hosting providers:&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://pinecast.com">pinecast.com&lt;/a>: starts at $5/month with unlimited storage, unlimited shows, basic analytics and website generator.&lt;/li>
&lt;li>&lt;a href="https://simplecast.com/">simplecast.com&lt;/a>: starts at $15/month per show. Cool player, website generator.&lt;/li>
&lt;li>&lt;a href="https://transistor.fm/">transistor.fm&lt;/a>: starts at $19/month with unlimited storage, unlimited shows. Limited at 5k downloads per month.&lt;/li>
&lt;li>Or one of many others, including &lt;a href="https://www.podbean.com/">podbean&lt;/a>, &lt;a href="https://www.libsyn.com/">libsyn&lt;/a>, &lt;a href="https://fireside.fm/">fireside&lt;/a> or even &lt;a href="https://soundcloud.com/for/podcasting">soundcloud&lt;/a> (probably not the best idea, since the company&amp;rsquo;s finances aren&amp;rsquo;t great)&lt;/li>
&lt;/ol>
&lt;p>Most of them can even generate websites for your shows, although, their design choices are questionable at times.&lt;/p>
&lt;p>All of my shows are hosted by pinecast: it&amp;rsquo;s a fantastic value for the money and everything works perfectly fine. Here is my referral coupon code for 40% off for 4 months: &lt;code>r-a6562b&lt;/code>. Use it at checkout.&lt;/p>
&lt;h2 id="questions">Questions?&lt;/h2>
&lt;p>Feel free to email me at &lt;a href="mailto:contact@rakhim.org">contact@rakhim.org&lt;/a>, and I&amp;rsquo;ll try to add more info to this guide.&lt;/p></description></item><item><title>Frontend vs. Functional Programming Conferences</title><link>https://rakhim.org/honestly-undefined/18/</link><pubDate>Wed, 27 Feb 2019 14:25:00 +0200</pubDate><guid>https://rakhim.org/honestly-undefined/18/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/frontend_vs_fp_conferences.jpg"></description></item><item><title>Christmas Wish</title><link>https://rakhim.org/honestly-undefined/17/</link><pubDate>Mon, 17 Dec 2018 17:43:00 +0200</pubDate><guid>https://rakhim.org/honestly-undefined/17/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/christmas_wish.jpg"></description></item><item><title>If real life were as buggy as software</title><link>https://rakhim.org/honestly-undefined/16/</link><pubDate>Fri, 30 Nov 2018 22:58:00 +0200</pubDate><guid>https://rakhim.org/honestly-undefined/16/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/buggy_ketchup.jpg"></description></item><item><title>Evolution of Frontend vs. Backend</title><link>https://rakhim.org/honestly-undefined/15/</link><pubDate>Fri, 16 Nov 2018 16:20:00 +0200</pubDate><guid>https://rakhim.org/honestly-undefined/15/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/frontend_vs_backend.jpg"></description></item><item><title>The Ultimate AI answers the Ultimate Question</title><link>https://rakhim.org/honestly-undefined/14/</link><pubDate>Wed, 07 Nov 2018 13:27:00 +0200</pubDate><guid>https://rakhim.org/honestly-undefined/14/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/ultimate_ai.jpg"></description></item><item><title>Important Git Realization</title><link>https://rakhim.org/honestly-undefined/13/</link><pubDate>Mon, 29 Oct 2018 10:51:00 +0200</pubDate><guid>https://rakhim.org/honestly-undefined/13/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/git.jpg"></description></item><item><title>Every movie that involves computers</title><link>https://rakhim.org/honestly-undefined/12/</link><pubDate>Tue, 23 Oct 2018 12:13:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/12/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/hackers-in-movies.jpg"></description></item><item><title>Kids nowadays are LIKE stupid</title><link>https://rakhim.org/honestly-undefined/11/</link><pubDate>Mon, 22 Oct 2018 11:39:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/11/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/sqlkids.jpg"></description></item><item><title>A case against LISP</title><link>https://rakhim.org/honestly-undefined/10/</link><pubDate>Mon, 15 Oct 2018 14:30:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/10/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/lisp_is_ugly.jpg"></description></item><item><title>Software updates throughout history</title><link>https://rakhim.org/honestly-undefined/9/</link><pubDate>Thu, 11 Oct 2018 09:26:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/9/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/software_updates.jpg"></description></item><item><title>People don't change</title><link>https://rakhim.org/honestly-undefined/8/</link><pubDate>Mon, 08 Oct 2018 10:29:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/8/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/immutable_gf.jpg"></description></item><item><title>React Expectation vs Reality</title><link>https://rakhim.org/honestly-undefined/7/</link><pubDate>Fri, 05 Oct 2018 14:01:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/7/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/react_expectation_reality.jpg"></description></item><item><title>Make Firefox faster and nicer on macOS</title><link>https://rakhim.org/improve-performance-of-firefox-on-macos/</link><pubDate>Mon, 01 Oct 2018 13:53:00 +0300</pubDate><guid>https://rakhim.org/improve-performance-of-firefox-on-macos/</guid><description>
&lt;p>I&amp;rsquo;m trying Firefox as my primary browser on all devices. It has some great features like &lt;a href="https://support.mozilla.org/en-US/kb/containers">Multi-Account containers&lt;/a> in addition to being a non-Google product, which is an increasingly rare feature on the web nowadays.&lt;/p>
&lt;p>Firefox on macOS is somewhat sluggish at the moment. If you try the current stable version 62 or current beta version 63, you&amp;rsquo;ll notice some lags and general slow response time for even the simplest tasks like changing tabs.&lt;/p>
&lt;p>Mozilla will probably fix these issues in the upcoming releases. Meanwhile, I found the following steps improve the performance significantly.&lt;/p>
&lt;h2 id="download-firefox-63-or-higher">Download Firefox 63 or higher&lt;/h2>
&lt;p>As of today (October 1, 2018), stable release version is 62. Version 63 is currently in beta, and I recommend using it today. It&amp;rsquo;s very robust, I haven&amp;rsquo;t had any problems with it. There are some important &lt;a href="https://www.mozilla.org/en-US/firefox/63.0beta/releasenotes/">performance improvements in it&lt;/a>.&lt;/p>
&lt;p>You can also try Firefox Nightly, it is currently version 64 on the dark side. Nightly is an unstable testing and development platform. By default, Nightly sends data to Mozilla — and sometimes their partners. There are some rough edges, I wouldn&amp;rsquo;t recommend it for daily browsing.&lt;/p>
&lt;h2 id="disable-animations">Disable animations&lt;/h2>
&lt;p>By default Firefox has lots of animations. I find them unnecessary and distracting, but more importantly, they contribute to the general sluggishness.&lt;/p>
&lt;p>Go to &lt;code>about:config&lt;/code> in the address bar. Search for &lt;code>animate&lt;/code> and set at least &lt;code>cosmeticAnimations&lt;/code> to &lt;code>false&lt;/code>.&lt;/p>
&lt;p>Fullscreen transition takes 0.2 seconds both ways. Make them instant by setting the following to &lt;code>0 0&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>full-screen-api.transition-duration.enter&lt;/code>&lt;/li>
&lt;li>&lt;code>full-screen-api.transition-duration.leave&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="disable-pocket">Disable Pocket&lt;/h2>
&lt;p>Firefox embedded Pocket into the browser. A questionable move, but it&amp;rsquo;s easy to disable (unless you use it, of course). Set &lt;code>extensions.pocket.enabled&lt;/code> to &lt;code>false&lt;/code>.&lt;/p>
&lt;h2 id="other-stuff">Other stuff&lt;/h2>
&lt;p>These are not related to performance, but can make your Firefox experience a bit nicer.&lt;/p>
&lt;p>Set to true:&lt;/p>
&lt;ul>
&lt;li>&lt;code>modalHighlight&lt;/code> highlight all the search results.&lt;/li>
&lt;li>&lt;code>browser.tabs.closeTabByDblclick&lt;/code> close tab by double-clicking on it.&lt;/li>
&lt;li>&lt;code>abs.multiselect&lt;/code> shift-click on tabs to select a group of tabs and do something with them (for example, detach from window).&lt;/li>
&lt;li>&lt;code>insecure_connection_text.enabled&lt;/code> write &amp;ldquo;Not Secure&amp;rdquo; in the address bar of non-https pages (like Chrome does). Additionally, enable a broken padlock icon with &lt;code>security.insecure_connection_icon.enabled&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Minor things:&lt;/p>
&lt;ul>
&lt;li>&lt;code>general.smoothScroll.mouseWheel.durationMaxMS&lt;/code> set &lt;code>200&lt;/code> to make scrolling speed similar to Chrome.&lt;/li>
&lt;li>&lt;code>geo.enabled&lt;/code> set to &lt;code>false&lt;/code> to disable geolocation.&lt;/li>
&lt;li>&lt;code>extensions.screenshots.disabled&lt;/code> set to &lt;code>true&lt;/code> to disable the screenshot extension. It&amp;rsquo;s actually pretty handy, check it out before disabling.&lt;/li>
&lt;/ul></description></item><item><title>I can't believe this happened again</title><link>https://rakhim.org/honestly-undefined/6/</link><pubDate>Fri, 28 Sep 2018 23:16:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/6/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/google_shutdowns_product.jpg"></description></item><item><title>A company sells your data</title><link>https://rakhim.org/honestly-undefined/5/</link><pubDate>Thu, 27 Sep 2018 23:16:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/5/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/company_sells_data.jpg"></description></item><item><title>Product manager's bedtime story</title><link>https://rakhim.org/honestly-undefined/4/</link><pubDate>Wed, 26 Sep 2018 23:16:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/4/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/night_user_story_time.jpg"></description></item><item><title>For Google, you're neither the consumer nor the product. You're a data point.</title><link>https://rakhim.org/you-are-a-data-point/</link><pubDate>Wed, 26 Sep 2018 14:24:00 +0300</pubDate><guid>https://rakhim.org/you-are-a-data-point/</guid><description>
&lt;p>In light of recent changes to Google Chrome, many forums have filled with bitter discussions. Here are just a few:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/ctavan/status/1044282084020441088">Chrome 69 will keep Google Cookies when you tell it to delete all cookies&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=18052923">Why I’m done with Chrome (HN discussion)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=17942252">Using Gmail? You will be force logged into Chrome&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>At some point someone inevitably says:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>For Google, you are not the consumer. You are the product.&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>But companies usually care about their products, protect them, try to improve their state.&lt;/p>
&lt;p>If I were a product, Google would do its best not to destroy me. They have invested a lot of resources into this product, so why risk it by making baffling changes to both privacy and user experience? If I stay happy with Google&amp;rsquo;s offerings, I keep being the perfect product: I can be mined for data and &amp;ldquo;sold&amp;rdquo; perpetually.&lt;/p>
&lt;p>Clearly, Google doesn&amp;rsquo;t care about me personally. And how could it? There are billions of people just like me who use their services every day.&lt;/p>
&lt;p>Maybe we should stop thinking we&amp;rsquo;re &amp;ldquo;Google&amp;rsquo;s product&amp;rdquo; and start thinking we are &lt;strong>data points in endless experiments&lt;/strong>.&lt;/p>
&lt;h2 id="you-re-data-point">You&amp;rsquo;re Data Point&lt;/h2>
&lt;p>What does it mean to be a data point?&lt;/p>
&lt;ol>
&lt;li>Your personal issues don&amp;rsquo;t matter.&lt;/li>
&lt;li>You, alone, are not valuable.&lt;/li>
&lt;li>You can be easily replaced.&lt;/li>
&lt;/ol>
&lt;p>While this is a gloomy oversimplification, I believe it&amp;rsquo;s important to recognize the general idea.&lt;/p>
&lt;h2 id="you-re-playing-a-role-in-some-user-story">You&amp;rsquo;re playing a role in some &amp;ldquo;user story&amp;rdquo;&lt;/h2>
&lt;p>When you pretend you&amp;rsquo;re the consumer, you think in terms of goods and services, and a simple two-way relationship. This leads to disappointment every time a breaking change comes in or a service shuts down altogether.&lt;/p>
&lt;p>In reality, you are playing roles in many &amp;ldquo;user stories&amp;rdquo; and experiments, without knowing it and without knowing what those roles actually constitute.&lt;/p>
&lt;figure class="wide">&lt;img src="https://rakhim.org/images/posts/google_data_point/user_story_time.jpg"
alt="Figure 1: Product manager&amp;rsquo;s bedtime user story.">&lt;figcaption>
&lt;p>Figure 1: Product manager&amp;rsquo;s bedtime user story.&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>Few weeks ago Google announced that Inbox will be closed next Spring. Many ask the heavens &amp;ldquo;Why would Google kill Inbox?! I&amp;rsquo;ve been using it every day and all my friends have!&amp;rdquo;&lt;/p>
&lt;p>I bet it was a successful experiment for Google: by using the app, you&amp;rsquo;ve generated a lot of important information, helped them learn useful patterns and god knows what else. They are not killing it because it failed as a consumer product, it was never intended to be one. They are not killing it because not enough people used it, or used not extensively enough. Quite the opposite.&lt;/p>
&lt;p>They are not really &amp;ldquo;killing&amp;rdquo; a product, they are finishing up an experiment.&lt;/p>
&lt;p>Just like scientists don&amp;rsquo;t &amp;ldquo;kill&amp;rdquo; good experiments, even though the mice might ask the heavens &amp;ldquo;why would they kill this maze?! I&amp;rsquo;ve been running in it every day and all my friends have!&amp;rdquo;&lt;/p>
&lt;figure class="wide">&lt;img src="https://rakhim.org/images/posts/google_data_point/mouse_maze.jpg"
alt="Figure 2: Google shuts down another product.">&lt;figcaption>
&lt;p>Figure 2: Google shuts down another product.&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>Of course, not all Google&amp;rsquo;s products are experiments. This isn&amp;rsquo;t a binary thing though: some products are clearly (in hindsight) temporary experiments, some are core things that rarely change, but the majority of apps and services are somewhere between. And you never know what games are you in.&lt;/p>
&lt;h2 id="you-are-punished-for-being-a-good-data-point">You are punished for being a good data point&lt;/h2>
&lt;p>If you truly use Google&amp;rsquo;s products extensively, you are statistically more likely to be punished for that. For example, if you haven&amp;rsquo;t willingly participated in the experiment called &amp;ldquo;Google Inbox&amp;rdquo; and just ignored their promotion, its shutdown won&amp;rsquo;t affect you negatively. Actually, it will affect you positively: the results of this experiment will help improve the feature set of Gmail, the service you&amp;rsquo;re probably using.&lt;/p>
&lt;p>But if you were a good data point&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>, and invested a lot into Inbox&amp;rsquo; features, you&amp;rsquo;ll receive a punishment. Your workflow — and an important one! — will just be disregarded.&lt;/p>
&lt;p>&amp;ldquo;We will incorporate many features of Inbox into Gmail&amp;rdquo; is a weak consolation when in one day your whole email experience just shuts down.&lt;/p>
&lt;p>&lt;strong>We have to understand this fundamental change of the relationship between companies and users&lt;/strong>. The main focus for tech giants is growth, which requires data, which requires experiments. The more we participate in data points generation, the more likely we&amp;rsquo;ll be burned.&lt;/p>
&lt;p>Compare this to the old and straightforward concept of &amp;ldquo;product X is popular, therefore product X will be kept on the market&amp;rdquo;.&lt;/p>
&lt;p>But why? Don&amp;rsquo;t they &lt;em>want&lt;/em> to have popular products? Well, yes, but&amp;hellip;&lt;/p>
&lt;h2 id="your-personal-metrics-aren-t-aligned-with-theirs">Your personal metrics aren&amp;rsquo;t aligned with theirs&lt;/h2>
&lt;p>We might think that &amp;ldquo;popular is good&amp;rdquo;, but for Google often &amp;ldquo;more data points is good&amp;rdquo;, because that&amp;rsquo;s &lt;strong>the real resource&lt;/strong> that allows them to grow. Ten different products, launched and shut down sequentially, is a better source of data points than one, long-lived and stable service.&lt;/p>
&lt;p>We do not and cannot know what is important in any given experiment. Heck, we can&amp;rsquo;t even know the scopes and the limits of them. That&amp;rsquo;s the point of having experiments!&lt;/p>
&lt;p>But one thing is certain: our values and metrics rarely align with theirs. Because, as a user, I don&amp;rsquo;t really care about company&amp;rsquo;s growth.&lt;/p>
&lt;p>You might say &amp;ldquo;Yeah, obviously! Their goal is profit, nothing new here!&amp;rdquo;. But it&amp;rsquo;s different this time. All businesses&amp;rsquo; end goal is profit, that&amp;rsquo;s capitalism, nothing wrong with that. The problem is in the hidden, implicit nature of the relationship. We don&amp;rsquo;t really know the deal! What can we do? What can we be sure of? What are our rights? What are their responsibilities?&lt;/p>
&lt;p>&lt;strong>What exactly is the deal?!&lt;/strong>&lt;/p>
&lt;h2 id="you-re-a-renewable-source">You&amp;rsquo;re a renewable source&lt;/h2>
&lt;p>Google itself uses your data to grow, but it also uses it for to make money by targeting ads. That&amp;rsquo;s what people mean when they say &amp;ldquo;Google sells your data!&amp;rdquo; and that&amp;rsquo;s where &amp;ldquo;you&amp;rsquo;re the product&amp;rdquo; rhetoric comes from.&lt;/p>
&lt;p>But it&amp;rsquo;s not like Adidas wants &lt;em>my data&lt;/em>. Or yours. They want a large group that satisfies certain parameters.&lt;/p>
&lt;figure class="wide">&lt;img src="https://rakhim.org/images/posts/google_data_point/selling_data.jpg"
alt="Figure 3: A company sells your data.">&lt;figcaption>
&lt;p>Figure 3: A company sells your data.&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>Of course, companies don&amp;rsquo;t get a .zip-file with names and addresses. They receive the ability to show certain ads to certain people. Google doesn&amp;rsquo;t &lt;em>sell&lt;/em> your data, they &lt;em>sell access&lt;/em> to your eyes and ears. So, you&amp;rsquo;re not just a product, you&amp;rsquo;re renewable source of products. Your tastes and needs change over time, you can be targeted over and over.&lt;/p>
&lt;p>This denies any sort of hope one might have about companies caring about their products. They do, just not about individual items or individual producers. Apple doesn&amp;rsquo;t care about any particular iPhone device or any particular worker at their Chinese factory.&lt;/p>
&lt;h2 id="but-i-pay-them-money">But I pay them money!&lt;/h2>
&lt;p>The third aspect is &amp;ldquo;Google and paid services&amp;rdquo;. Google sells a lot of things directly, and this &lt;em>must&lt;/em> be a &amp;ldquo;consumer-producer&amp;rdquo; scenario, right?&lt;/p>
&lt;p>Yeah, no.&lt;/p>
&lt;p>Google recently increased the fees for Google Maps API about 1400%. This kind of increase means one of the two:&lt;/p>
&lt;ol>
&lt;li>Previous pricing model was inaccurate.&lt;/li>
&lt;li>New pricing model is inaccurate.&lt;/li>
&lt;/ol>
&lt;p>Were they losing money before to conquer the market? Or did they just decide to make a buttload of cash using the conquered market? Either way, the problem is the same: &lt;strong>we had no idea what the deal is.&lt;/strong>&lt;/p>
&lt;p>Another example is Google Cloud, a platform used by many large businesses. You can &lt;a href="https://medium.com/@serverpunch/why-you-should-not-use-google-cloud-75ea2aec00de">lose everything&lt;/a> in 3 days and deal with a pretty bad support even though you&amp;rsquo;re paying client.&lt;/p>
&lt;blockquote>
&lt;p>Not to mention the lack of visibility in changes - it seems like everything is constantly running at multiple versions that can change suddenly with no notice, and if that breaks your use case they don&amp;rsquo;t really seem to know or care. It feels like there&amp;rsquo;s miles of difference between the SRE book and how their cloud teams operate in practice. (&lt;a href="https://news.ycombinator.com/item?id=17431813">comment in a relevant discussion&lt;/a>)&lt;/p>
&lt;/blockquote>
&lt;p>Are they underfunded? Is their goal to make a reliable platform or is it something else? What do they take into account when they make changes? We have no idea.&lt;/p>
&lt;h2 id="this-is-not-just-google">This is not just Google&lt;/h2>
&lt;p>It&amp;rsquo;s easier to talk about Google because they seem to be the biggest company in this area (or ever). But this is the reality for a lot of businesses, not necessarily in the advertisement industry.&lt;/p>
&lt;p>Once the company is large enough, all customers become data points. This is okay in principle. I can live with that, as long as I understand it. This is a question of honesty.&lt;/p>
&lt;p>If Google said upfront:&lt;/p>
&lt;blockquote>
&lt;p>We&amp;rsquo;re launching this new product X, but it&amp;rsquo;s an experiment. We&amp;rsquo;ll work on it for at least 5 years, but can&amp;rsquo;t guarantee anything after that. We might shut it down with short notice. Would you like to participate?&lt;/p>
&lt;/blockquote>
&lt;p>Then there would be no point in complaining. That&amp;rsquo;s a fair deal. Of course, this kind of frankness wouldn&amp;rsquo;t help Google. It&amp;rsquo;s like telling the participants of a sociological experiment about all details of said experiment. It poisons the data. Scientists want unsullied results.&lt;/p>
&lt;figure>&lt;img src="https://rakhim.org/images/posts/google_data_point/google_inbox.jpg"
alt="Figure 4: I hope I&amp;rsquo;m not sued for this&amp;hellip;">&lt;figcaption>
&lt;p>Figure 4: I hope I&amp;rsquo;m not sued for this&amp;hellip;&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>If Google said upfront:&lt;/p>
&lt;blockquote>
&lt;p>We&amp;rsquo;re giving you a lot of awesome products free of charge. But we&amp;rsquo;ll collect as much information on you as possible, and if we&amp;rsquo;ll keep changing the services and terms to collect more data. We will use this information to target ads and maybe do something else. Would you like to participate?&lt;/p>
&lt;/blockquote>
&lt;p>That&amp;rsquo;s a fair deal too. You are &lt;em>free&lt;/em> to give up anything, as long as you understand what&amp;rsquo;s going on. Of course, this kind of message doesn&amp;rsquo;t survive the path from Terms of Service to The Marketing Department.&lt;/p>
&lt;p>But you always read ToS, right?&lt;/p>
&lt;p>All that is obvious in hindsight (in those forums, there&amp;rsquo;s always at least one guy who says &amp;ldquo;what did you expect?&amp;rdquo;), but we have to learn to infer these things from the business models. This is not our jobs, but that&amp;rsquo;s the reality. &lt;strong>We have to understand all the implications of these new, weird businesses&lt;/strong>.&lt;/p>
&lt;p>Every time you see a new startup, new app, new service with some interesting features, and it&amp;rsquo;s clearly not a simple &amp;ldquo;I pay, you provide&amp;rdquo; kind of deal, &lt;strong>beware&lt;/strong>. What are the implications?&lt;/p>
&lt;h2 id="conclusions">Conclusions&lt;/h2>
&lt;p>Let&amp;rsquo;s summarize:&lt;/p>
&lt;ul>
&lt;li>Growth, not simple profit generation is the main focus.&lt;/li>
&lt;li>Growth requires data. Experiments, changes and seemingly weird decisions generate data.&lt;/li>
&lt;li>For Google and many tech giants, you are a data point.&lt;/li>
&lt;/ul>
&lt;p>And as the result:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>We no longer interact with businesses.&lt;/strong> We interact with the top layer interface of a multi-layered, non-obvious system built with implicit, vague rules.&lt;/li>
&lt;li>Never before were users&amp;rsquo; and companies&amp;rsquo; goals so irrelated to each other.&lt;/li>
&lt;li>We&amp;rsquo;re constantly &lt;strong>playing games&lt;/strong> we&amp;rsquo;re not aware of.&lt;/li>
&lt;li>We have to learn to understand the implications of this.&lt;/li>
&lt;/ul>
&lt;h2 id="final-words">Final words&lt;/h2>
&lt;p>Not all is bad. This symbiosis can be very benificial for all parties. We can explicitly play roles of consumers, products and data points at the same time, knowing what&amp;rsquo;s happening and being in control. Companies can play whatever games they need to play with us.&lt;/p>
&lt;p>Extremely relevant ads and extremely personalized user experiences sound pretty good to me, all the creepiness aside.&lt;/p>
&lt;p>Legislation will never catch up in time, so we have to take things into our own hands and learn to live in this brave new world.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Rather, a generator of myriads of data points, but &amp;ldquo;data point&amp;rdquo; sounds more inhumane and humiliating, so I&amp;rsquo;ll stick to this term for dramatic purposes.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>A Simple Introduction to Proof by Induction</title><link>https://rakhim.org/a-simple-introduction-to-proof-by-induction/</link><pubDate>Thu, 20 Sep 2018 17:18:00 +0300</pubDate><guid>https://rakhim.org/a-simple-introduction-to-proof-by-induction/</guid><description>
&lt;script src="{{ "js/mathjax-config.js" | absURL }}">&lt;/script>
&lt;script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS_HTML">&lt;/script>
&lt;p>Now that you&amp;rsquo;re familiar with &lt;a href="https://rakhim.org/2018/09/a-simple-introduction-to-proof-by-contradiction/">direct proof and proof by contradiction&lt;/a>, it&amp;rsquo;s time to discover a powerful technique of proof by induction.&lt;/p>
&lt;p>&lt;em>Aside: do not confuse mathematical induction with inductive or deductive reasoning. Despite the name, mathematical induction is actually a form of deductive reasoning.&lt;/em>&lt;/p>
&lt;p>Let&amp;rsquo;s say, we want to prove that some statement \(P\) is true for all positive integers. In other words:&lt;/p>
&lt;p>\(P(1)\) is true, \(P(2)\) is true, \(P(3)\) is true&amp;hellip; etc.&lt;/p>
&lt;p>We could try and prove each one directly or by contradiction, but the infinite number of positive integers makes this task rather grueling. Proof by induction is a sort of generalization that starts with the basis:&lt;/p>
&lt;p>&lt;strong>Basis:&lt;/strong> Prove that \(P(1)\) is true.&lt;/p>
&lt;p>Then makes one generic step that can be applied indefinitely:&lt;/p>
&lt;p>&lt;strong>Induction step:&lt;/strong> Prove that for all \(n\geq1\), the following statement holds: If \(P(n)\) is true, then \(P(n+1)\) is also true.&lt;/p>
&lt;p>See what we did there? We&amp;rsquo;ve devised another problem to solve, and it&amp;rsquo;s seemingly the same. But if the basis is true, then proving this &lt;em>inductive step&lt;/em> will prove the theorem.&lt;/p>
&lt;p>To do this, we chose an arbitrary \(n\geq1\) and assume that \(P(n)\) is true. This assumption is called the &lt;em>inductive hypothesis&lt;/em>. The tricky part is this: we don&amp;rsquo;t prove the hypothesis directly, but prove the \(n+1\) version of it.&lt;/p>
&lt;p>This is all rather amorphous, so let&amp;rsquo;s prove a real theorem.&lt;/p>
&lt;p>&lt;strong>Theorem 1.&lt;/strong> For all positive integers \(n\), the following is true:&lt;/p>
&lt;p>\begin{equation}&lt;br>
\label{eq:1}&lt;br>
1 + 2 + 3 + &amp;hellip; + n = \frac{n(n+1)}{2}&lt;br>
\end{equation}&lt;/p>
&lt;p>&lt;strong>Proof&lt;/strong>. Start with the basis when \(n\) is \(1\). Just calculate it:&lt;/p>
&lt;p>\[ 1 = \frac{1(1+1)}{2}. \]&lt;/p>
&lt;p>This is correct, so, the basis is proven. Now, assume that the theorem is true for any \(n\geq1\):&lt;/p>
&lt;p>\begin{equation}&lt;br>
\label{eq:2}&lt;br>
1 + 2 + 3 + &amp;hellip; + n = \frac{n(n+1)}{2}&lt;br>
\end{equation}&lt;/p>
&lt;p>In the induction step we have to prove that it&amp;rsquo;s true for \(n+1\):&lt;/p>
&lt;p>\begin{equation}&lt;br>
\label{eq:3}&lt;br>
1 + 2 + 3 + &amp;hellip; + (n+1) = \frac{(n+1)(n+2)}{2}&lt;br>
\end{equation}&lt;/p>
&lt;p>Having this equation, we should just try to expand it and prove directly. Since the last member on the left side is \(n+1\), the second last must be \(n\), so:&lt;/p>
&lt;p>\[ 1 + 2 + 3 + &amp;hellip; + (n + 1) = 1 + 2 + 3 + &amp;hellip; + n + (n+1) \]&lt;/p>
&lt;p>From our assumption, we know, that&lt;/p>
&lt;p>\[ 1 + 2 + 3 + &amp;hellip; + n = \frac{n(n+1)}{2}. \]&lt;/p>
&lt;p>So, let&amp;rsquo;s replace it on the right hand side:&lt;/p>
&lt;p>\[ 1 + 2 + 3 + &amp;hellip; + (n + 1) = \frac{n(n+1)}{2} + (n+1) \]&lt;/p>
&lt;p>And then make that addition so that the right hand side is a single fraction:&lt;/p>
&lt;p>\[ 1 + 2 + 3 + &amp;hellip; + (n + 1) = \frac{n(n+1)}{2} + \frac{2(n+1)}{2} \]&lt;/p>
&lt;p>\[ = \frac{n(n+1) + 2(n+1)}{2} \]&lt;/p>
&lt;p>\[ = \frac{(n+1)(n+2)}{2}. \]&lt;/p>
&lt;p>Done, we have proven that the inductive step (\ref{eq:3}) is true.&lt;/p>
&lt;p>There are two results:&lt;/p>
&lt;ol>
&lt;li>The theorem is true for \(n=1\).&lt;/li>
&lt;li>If the theorem is true for any \(n\), then it&amp;rsquo;s also true for \(n+1\).&lt;/li>
&lt;/ol>
&lt;p>Combining these two results we can conclude that the theorem is true for all positive integers \(n\).&lt;/p>
&lt;hr>
&lt;p>I had troubles with this technique because for a long time I couldn&amp;rsquo;t for the life of me understand why is this &lt;em>enough&lt;/em> and how is the basis &lt;em>helping&lt;/em>?! The basis seemed redundant. We assume \(P(n)\) is true, then prove that \(P(n+1)\) is true given that \(P(n)\) is true, but so what? We didn&amp;rsquo;t prove the thing we assumed!&lt;/p>
&lt;p>It clicked after I understood that we don&amp;rsquo;t have to prove \(P(n)\), we just take the concrete value from the basis and use it as \(n\). Since we have a proof of \(P(n+1)\) being true &lt;strong>if&lt;/strong> \(P(n)\) is true, we conclude that if \(P(1)\) is true, then \(P(1+1)\) is true.&lt;/p>
&lt;p>Well, if \(P(1+1)\) is true, then, using the same idea, \(P(1+1+1)\) is true, and so forth.&lt;/p>
&lt;p>The basis was the cheat-code to kick-start the process by avoiding the need to prove the assumption \ref{eq:2}.&lt;/p></description></item><item><title>RE: Software disenchantment</title><link>https://rakhim.org/re-software-disenchantment/</link><pubDate>Mon, 17 Sep 2018 23:57:00 +0300</pubDate><guid>https://rakhim.org/re-software-disenchantment/</guid><description>
&lt;p>Nikita just published &lt;a href="http://tonsky.me/blog/disenchantment/">Software disenchantment&lt;/a>, and here is my rant-y reply. Please, read his post first.&lt;/p>
&lt;p>TL;DR: I agree with Nikita and I am equally frustrated with the current state of the industry. We started &lt;a href="https://grumpy.website/">grumpy.website&lt;/a> together, after all. But I don&amp;rsquo;t believe that situation will significantly improve until the general public&amp;rsquo;s standards increase. Along with the responsibility to make better software, we have a responsibility to educate the public, so that they don&amp;rsquo;t get used to the idea that computers suck.&lt;/p>
&lt;hr>
&lt;blockquote>
&lt;p>Would you buy a car if it eats 100 liters per 100 kilometers? How about 1000 liters? With computers, we do that all the time.&lt;/p>
&lt;/blockquote>
&lt;p>The thing is — yes, people would. It all comes to the micro economic level. If a car eats 1000 liters per 100 km and fuel is cheap (and cars hold enough liters to drive around everyday), people will buy them, use them and rarely think about it. Just like they do it today with 6 liters per 100km cars and don&amp;rsquo;t think about how the combustion engine system is inefficient in terms of pure energy output. A lot of that energy goes into useless heat and noise. 6 liters of matter has the potential to fuel the planet for weeks if we were to extract it efficiently.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter if something is efficient or optimal when it comes to general consumer market. And, whether you like it or not, technology and software is now in the general consumer market, in the same area with clothing, cars and such.&lt;/p>
&lt;p>Have you been noticing how annoying the clothing industry is? Home items? Furniture? I&amp;rsquo;ve never seen a laundry detergent or a hand cream bottle that wasn&amp;rsquo;t downright obnoxious. I&amp;rsquo;ve used a potato chips package that was &lt;em>easy to use&lt;/em>. It&amp;rsquo;s everywhere: unusable, badly designed, over bloated with unnecessary solutions and optimized for marketing and fast development, not for good use. The things you&amp;rsquo;re describing are not specific to software, it&amp;rsquo;s about design in general.&lt;/p>
&lt;p>The only places where things are truly not too damn awful are some parts of military and super high level industrial solutions, where stakes are wa-a-a-a-y higher and general consumer is a non-existent agent altogether.&lt;/p>
&lt;blockquote>
&lt;p>Yet half of webpages struggle to maintain smooth 60fps scroll on the latest top-of-the-line MacBook Pro.&lt;/p>
&lt;/blockquote>
&lt;p>You care, I care, but most people don&amp;rsquo;t. Not because they&amp;rsquo;re stupid, they just don&amp;rsquo;t notice these things. And we can dream all day long that engineers finally wake up and realize the scale of the crap-monster we&amp;rsquo;ve been building for years, but I don&amp;rsquo;t think any significant change will happen until the general population starts to care.&lt;/p>
&lt;p>I&amp;rsquo;ve seen this just a few weeks ago: a professional marketing specialist is using a high end laptop with some bloatware in the browser. Forget 60fps, his pages were doing 10-12 fps at best, &lt;em>and&lt;/em> every time he moved the cursor to close a tab, an antivirus popup appeared on top of the button. He says &amp;ldquo;damn!&amp;rdquo;, moves the cursor away, then slowly moves it back again from a specific angle, carefully trying not to invoke that popup. He succeeds and carries on with his task. I asked him, it&amp;rsquo;s been months like that. Hundreds of times. Every day. He just doesn&amp;rsquo;t know better, this is &lt;em>what computers are&lt;/em>.&lt;/p>
&lt;p>When a car makes barely works, we think it needs to be fixed asap. When a computer barely works, we think &amp;ldquo;those damn computers!&amp;rdquo;.&lt;/p>
&lt;p>The things that contributed to your depression are often minor annoyances to the people. Sorry. This whole statement is a good catalyst for a whole another mental issue, fuck.&lt;/p>
&lt;p>This is why I stopped pleading to the developers and started pleading to the users. I want users to demand more and be angrier with the promise of computing.&lt;/p>
&lt;p>But this is a wrong battle anyway, I think. You, me and our peers are in the 0.1% of the world population when it comes to opportunity, wealth and availability of technology. We care about browsers&amp;rsquo; fps because we don&amp;rsquo;t think about whether or not our children will eat tomorrow. Not to dwell on the &amp;ldquo;first world problems&amp;rdquo;, this is how global economy works.&lt;/p>
&lt;p>Today when we say &amp;ldquo;the population is growing&amp;rdquo; and &amp;ldquo;the internet is growing&amp;rdquo;, we actually mean &amp;ldquo;China, Asia and Africa are growing and connecting to the global economy and the internet&amp;rdquo;. Software industry is as global as globalization goes, and, like any other industry, it often adapts to the lowest denominator with the best margins.&lt;/p>
&lt;p>Millions of people move from powerty to middle class, and they are &amp;ldquo;the big wave&amp;rdquo; of new users for technology. If your father died of hunger, but you suddenly found yourself having a job and buying a smartphone, I bet it&amp;rsquo;ll be a long time until you start caring about Android core size and Chrome&amp;rsquo;s render speed. Even if you know a thing or two about technology.&lt;/p>
&lt;p>What I&amp;rsquo;m trying to say is:&lt;/p>
&lt;ol>
&lt;li>Most users in the developed countries are used to bad software.&lt;/li>
&lt;li>Most users in the developing countries are conditioned to bad software from the beginning.&lt;/li>
&lt;/ol>
&lt;p>This is weird! An average Western European family has very different notions of &amp;ldquo;enough food&amp;rdquo; and &amp;ldquo;a good job&amp;rdquo; and &amp;ldquo;nice life&amp;rdquo; to an average rural family from a developing country. But when it comes to, say, Android apps, both families have pretty similar experience and expectations, I guess.&lt;/p>
&lt;p>Insert equality rhetoric.&lt;/p>
&lt;p>Why software 20-40 years ago was actually faster, more stable and nicer to use (as long as you invest some time to RTFM)? Because it wasn&amp;rsquo;t built for the general public &lt;strong>&lt;strong>and&lt;/strong>&lt;/strong> wasn&amp;rsquo;t built by the general public, it was very limited in both audience and developers.&lt;/p>
&lt;p>Today there is no good incentive to make good software unless it&amp;rsquo;s some highly specific professional product.&lt;/p>
&lt;p>One can easily interpret this in a wrong way, I believe. Software becoming a mass product is ultimately good, but comes with some harsh transition periods.&lt;/p>
&lt;blockquote>
&lt;p>Google Inbox, a web app written by Google, running in Chrome browser also by Google, takes 13 seconds to open moderately-sized emails.&lt;/p>
&lt;/blockquote>
&lt;p>Well, Google inbox is discontinued next Spring, so, that&amp;rsquo;s not a problem anymore. Especially for Google.&lt;/p>
&lt;p>But, here comes my second point: even if you care about this stuff, you still use it, and as far as Google is concerned, that&amp;rsquo;s a success. I asked you recently why did you switch away from Firefox back to Chrome, even though so many Google&amp;rsquo;s design decisions are appalling for you and me. And I knew the answer: we don&amp;rsquo;t have much choice. There are just a handful of alternatives, and everything is bad.&lt;/p>
&lt;p>Individual developers at Google will probably agree with you, they probably care a lot about all that stuff. But Google-the-company is not the collection of those developers, it&amp;rsquo;s another organism altogether. That organism, just like any other evolutionary being in a competitive global economy, tries to do the least to get the most. It&amp;rsquo;s a corporation, it wants to make&amp;hellip; no, scratch that, it wants to &lt;span class="underline">have&lt;/span> money, but not necessarily &lt;span class="underline">make&lt;/span> it.&lt;/p>
&lt;blockquote>
&lt;p>Windows 10 takes 30 minutes to update. What could it possibly be doing for that long?&lt;/p>
&lt;/blockquote>
&lt;p>Yeah, so? Is Microsoft having any difficulties because of that? Maybe, but I don&amp;rsquo;t think they believe that. What can you do? Switch to Linux, ha?&lt;/p>
&lt;p>Yeah, if that update takes 24 hours, I bet not much changes, people will still use it, because for the majority of people Windows = Computers. It&amp;rsquo;s not &amp;ldquo;windows is slow&amp;rdquo;, it&amp;rsquo;s &amp;ldquo;this is what computers are&amp;rdquo;.&lt;/p>
&lt;p>And organizations will develop special routines and systems to deal with that &amp;ldquo;intrinsic nature of computation&amp;rdquo;: have &amp;ldquo;windows update&amp;rdquo; weeks. As long as everybody in the world suffers, there is no competitive advantage of having faster updates. Or better software.&lt;/p>
&lt;blockquote>
&lt;p>Android system with no apps takes almost 6 Gb. Just think for a second how obscenely HUGE that number is.&lt;/p>
&lt;/blockquote>
&lt;p>Look at plastic and garbage in general. The amount of packaging is staggering, but only a handful of activists care. It&amp;rsquo;s hidden and doesn&amp;rsquo;t really affect us. Yeah, you need larger garbage bins, whatever. Just buy them.&lt;/p>
&lt;p>Just like you need a larger SD card or a new phone. Just buy them.&lt;/p>
&lt;hr>
&lt;p>My third point is that software industry consists of amateurs, mostly (see &lt;a href="https://rakhim.org/2018/07/software-shouldnt-fail/">https://rakhim.org/2018/07/software-shouldnt-fail/&lt;/a>).&lt;/p>
&lt;p>&lt;em>&amp;ldquo;The Web was the first global technological phenomena that was built and maintained by the amateurs. Computer hardware, software, and the internet itself were built by mathematicians and engineers. The Web was built by people like me.&amp;rdquo;&lt;/em>&lt;/p>
&lt;p>And that middle class thing comes into play again. Being an amateur web developer is a way to bring your family out of poverty for many people. See success stories of many boot camps and such. If I couldn&amp;rsquo;t provide for my family, but then I learned how to combine 10000 node modules into an electron app, and some company pays me money for that, I will happily make a lot of electron apps long before I start worrying about problems that most of the users don&amp;rsquo;t have.&lt;/p>
&lt;p>Bad sofware design and bad UX are ethically acceptable.&lt;/p>
&lt;p>The machine is self-supporting and recursive: the more amateurs build software, the more developers we need to support it, thus creating more demand for new people becoming developers ASAP. More amateurs building software creates more amateurs building software.&lt;/p>
&lt;p>Keep in mind that this is a net positive result for the individual lives of people and communities in the short term, even though it &lt;em>might&lt;/em> be a net negative for the civilization at large. Considering this, talking about this is very difficult.&lt;/p>
&lt;blockquote>
&lt;p>A 3D game can fill whole screen with hundreds of thousands (!!!) of polygons in the same 16ms and also process input, recalculate the world and dynamically load/unload resources. How come?&lt;/p>
&lt;/blockquote>
&lt;p>Several things:&lt;/p>
&lt;ol>
&lt;li>Gamers &lt;strong>care&lt;/strong>. See millions of views and comments to videos about minor differences in gaming performance.&lt;/li>
&lt;li>A game costs 60€. People can return games if they&amp;rsquo;re slow or bad (because they care). An iPhone/Android app costs cents. You can&amp;rsquo;t return them. Free apps cost nothing and have miniscule margins of profit for devs (ads).&lt;/li>
&lt;li>Slow games are actully unusable. You can&amp;rsquo;t play at 15fps, it&amp;rsquo;s just physically uncomfortable. But reading web at 10fps is, well, like Kindle. It&amp;rsquo;s fine.&lt;/li>
&lt;/ol>
&lt;p>Another big idea about games I wanted to refer to is console games. This is the industry we can learn a lot from! Unlike PC games, console games seem to be much more stable. Because when Sony unveils a new PS, it says &amp;ldquo;this thing is THE console for the next 8 years. Have fun!&amp;rdquo;&lt;/p>
&lt;p>Game devs know the schedule, and can take their time to tailor code to that particular, immutable and stable system. This is why every year new console games look better and better, even though the underlying hardware doesn&amp;rsquo;t change at all. Devs squeeze the shit out of the resources.&lt;/p>
&lt;p>Compare first PS3 games and last PS3 games. It&amp;rsquo;s crazy. Same hardware!&lt;/p>
&lt;p>Web developers don&amp;rsquo;t care that much, next year their product will probably work faster (given it doesn&amp;rsquo;t break due to browser update or API deprecation), because next year the average smartphone CPU will be faster.&lt;/p>
&lt;blockquote>
&lt;p>A simple text chat is notorious for its load speed and memory consumption. Yes, you really have to count Slack in as a resource-heavy application. I mean, chatroom and barebones text editor, those are supposed to be two of the less demanding apps in the whole world. Welcome to 2018.&lt;/p>
&lt;/blockquote>
&lt;p>Notice how the biggest and probably one of the most complex software projects in the history is being developed without Slack. Those Linux kernel devs, how can they work like that?! Without real time chat! Without Slack integration with CI and github?! Without notifications?! They still communicate via email, those weirdos!!!&lt;/p>
&lt;p>I often hear this: &amp;ldquo;Slack is great because of integrations, we see errors and status updates and CI live in our chat!&amp;rdquo;.&lt;/p>
&lt;p>When the whole world is updating like crazy because everyone else is doing this, not necessarily because it&amp;rsquo;s intrinsically good for the users, and the teams have to grow big to cope with the speed, and the technology has to be fragile and complex because Lean and Agile&amp;hellip; you have no choice but to monitor and react to the system updates like a team of military doctors.&lt;/p>
&lt;p>This doesn&amp;rsquo;t make Slack an intrinsically good product. But it&amp;rsquo;s &lt;strong>&lt;strong>necessery&lt;/strong>&lt;/strong> given the state of things.&lt;/p>
&lt;p>&lt;strong>&amp;ldquo;This complex portable surgeon robot is great, it allows us to move fast every time we shoot ourselves in the foot!&amp;rdquo;&lt;/strong>&lt;/p>
&lt;p>So, you can&amp;rsquo;t make truly good apps because you&amp;rsquo;re a team of amateurs in the world full of similar competition, and to be able to move you NEED slack. If slack is 2x slower tomorrow, you take it, you NEED it.&lt;/p>
&lt;blockquote>
&lt;p>Nobody understands anything at this point. Neither they want to. We just throw barely baked shit out there, hope for the best and call it “startup wisdom”.&lt;/p>
&lt;/blockquote>
&lt;p>Yup. 100% this.&lt;/p>
&lt;p>It seems, individual users don&amp;rsquo;t matter anymore. As long as the final majority of users end up in the &amp;ldquo;okay, I guess it works&amp;rdquo; state, we&amp;rsquo;re golden.&lt;/p>
&lt;p>I&amp;rsquo;m all for your &amp;ldquo;Better world manifesto&amp;rdquo;, sign me up. But I think that developers are not the bottleneck, the users are. We do have the responsibility, but this is an industry change we&amp;rsquo;re talking about, and only markets seem to be able to effectively change industries.&lt;/p>
&lt;p>Until we all live in some perfect society, there will be huge markets full of users with &amp;ldquo;other problems than your stupid app&amp;rdquo;. And as long as it makes economical sense to produce cheap crap, it will be produced.&lt;/p></description></item><item><title>Dumb Down the Context Until the Problem Goes Away</title><link>https://rakhim.org/dumb-down-the-context-until-the-problem-goes-away/</link><pubDate>Fri, 14 Sep 2018 21:10:00 +0300</pubDate><guid>https://rakhim.org/dumb-down-the-context-until-the-problem-goes-away/</guid><description>
&lt;p>At work we use SCSS and HAML, so I rarely write pure HTML and CSS there. But for small side projects and my personal blog I tend to stick with the simplest (and dumbest) possible tools. This week I was working on a refreshed look for this blog. Being a good modern man, I tried to stick with &lt;code>em&lt;/code> or &lt;code>rem&lt;/code> for sizing and typography.&lt;/p>
&lt;p>Using &lt;code>em&lt;/code> means adding state to your specs, and I don&amp;rsquo;t like this. Looking at a particular element, it can be impossible to understand what &lt;code>em&lt;/code> means. So &lt;code>rem&lt;/code> it is.&lt;/p>
&lt;p>The value &lt;code>rem&lt;/code> is &amp;ldquo;equal to the computed value of font-size on the root element&amp;rdquo;, so starting with this:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-nil" data-lang="nil">html {
font-size: 21px;
}
&lt;/code>&lt;/pre>&lt;p>we suppose to get a universal and stable variable. &lt;code>10rem&lt;/code> now means &lt;code>210px&lt;/code>. Cool? Not so much.&lt;/p>
&lt;p>I wrote a simple media query to make headers smaller on narrow screens:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-nil" data-lang="nil">@media (max-width: 34rem) {
h1 {
font-size: 2.369rem;
}
}
&lt;/code>&lt;/pre>&lt;p>But it doesn&amp;rsquo;t work at the specified break point of &lt;code>34rem = 714px&lt;/code>. Turns out that in media queries &lt;code>rem&lt;/code> means &amp;ldquo;initial value of font-size&amp;rdquo;, as per spec&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>. It&amp;rsquo;s &lt;code>16px&lt;/code> in most browsers.&lt;/p>
&lt;p>You have two lines of code near one another, and the same symbol means different things. Check out this &lt;a href="https://fvsch.com/browser-bugs/rem-mediaquery/">demonstration&lt;/a>. And you dare to complain about mutations in your imperative programming language!&lt;/p>
&lt;p>&lt;a href="https://adamwathan.me/dont-use-em-for-media-queries/">Using &lt;code>em&lt;/code>&amp;rsquo;s in media queries brings problems&lt;/a> as well. So, in the end, pixels are the only units that behave consistently across all browsers and don&amp;rsquo;t add hidden qualities to your styles.&lt;/p>
&lt;p>I then thought okay, I can get around this problem by using &lt;code>calc&lt;/code>, which seems to be supported in all browsers nowadays. Nope, it doesn&amp;rsquo;t work in media queries.&lt;/p>
&lt;p>The first thought that came after that is almost a reflex for many web developers alike: just use some tools on top of this ugly and inconsistent language!&lt;/p>
&lt;p>A pre-compiler like SCSS provides variables and calculations and other sweet features. It can seamlessly generate final CSS if you enable a watcher, or even better, set up something like Gulp or Webpack (oh, god). But then it&amp;rsquo;ll be kind of difficult to use the web inspector in the browser, since it shows the final CSS, but I never work with it directly.&lt;/p>
&lt;p>Oh, no worries, you can generate source maps for SASS/SCSS. Magic&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>!&lt;/p>
&lt;p>But wait&amp;hellip; While this solves my problem, it adds a tremendous amount of complexity. Is it worth it? Clearly, not in my case, but for a huges project like Hexlet at my main job it clearly does. Where is the threshold? How does one know when it&amp;rsquo;s worth to invest into a set of new abstractions that comes with their own quirks and problems?&lt;/p>
&lt;p>It&amp;rsquo;s a difficult question, but for me and my small projects I found it important to remind myself: resist complexity at all costs, resist adding new things into the system. If my problem asks for a solution that involves additional tools or systems, first and foremost consider dumbing the whole thing down so that the problem goes away. By regressing to pixels, which are so &amp;ldquo;not modern&amp;rdquo;, I managed to avoid a whole bag of cruft being put on top of this primitive project. The system became dumber. It&amp;rsquo;s a win for me.&lt;/p>
&lt;p>This is a weak example, I agree, so let me provide another one. Few years ago I needed to launch a small wiki site. Many popular wiki engines (like MediaWiki) are way too complex and feature-rich, so I looked for simpler alternatives. I found a nice Ruby library&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> and spend few hours setting it up, providing custom templates and styles. I was happy with the result, but then I found myself daunted by the worst part: deploy and maintenance.&lt;/p>
&lt;p>Of course, setting up a server by hand is a no-no, so I had to write an Ansible recipe for Ubuntu Rails environment. Accidental complexity involved in this problem became so large I started forgetting what I was trying to achieve.&lt;/p>
&lt;p>It took me some time to realize that the primary audience for this wiki will actually be much more comfortable editing text directly via Git rather than fiddle with a web interface. And if it&amp;rsquo;s hosted on GitHub, I don&amp;rsquo;t have to worry about authorization and accounts. I still needed it to run on my domain with some specific HTML, so I just made a simple Jekyll site and provided links to quickly edit and send pull requests via GitHub.&lt;/p>
&lt;p>I had problems associated with deployment and maintenance, and instead of adding tools as solutions, I dumbed the whole context so that the problems went away.&lt;/p>
&lt;p>Note that these problems are often of accidental complexity type. Intrinsic, real problems don&amp;rsquo;t surrender this easily.&lt;/p>
&lt;p>If playing with lots of inter operating tools is fun, by all means go for it. As long as you &lt;em>remember&lt;/em> and &lt;em>realize&lt;/em> what is going on. Complexity is not inherently bad, it&amp;rsquo;s just sort of cunning when you&amp;rsquo;re not mindful.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://www.w3.org/TR/css3-mediaqueries/">https://www.w3.org/TR/css3-mediaqueries/&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a href="https://robots.thoughtbot.com/sass-source-maps-chrome-magic">https://robots.thoughtbot.com/sass-source-maps-chrome-magic&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>&lt;a href="https://github.com/goll">https://github.com/goll&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>A Simple Introduction to Proof by Contradiction</title><link>https://rakhim.org/a-simple-introduction-to-proof-by-contradiction/</link><pubDate>Wed, 12 Sep 2018 17:49:00 +0300</pubDate><guid>https://rakhim.org/a-simple-introduction-to-proof-by-contradiction/</guid><description>
&lt;script src="{{ "js/mathjax-config.js" | absURL }}">&lt;/script>
&lt;script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS_HTML">&lt;/script>
&lt;p>In mathematics, a theorem is a true statement, but the mathematician is expected to be able to prove it rather than take it on faith. The proof is a sequence of mathematical statements, a path from some basic truth to the desired outcome. An impeccable argument, if you will.&lt;/p>
&lt;p>One of the basic techniques is proof by contradiction. Here is the idea:&lt;/p>
&lt;ol>
&lt;li>Assume the statement is false.&lt;/li>
&lt;li>Derive a contradiction, a paradox, something that doesn&amp;rsquo;t make sense. This will mean that the statement cannot possibly be false, therefore it&amp;rsquo;s true.&lt;/li>
&lt;/ol>
&lt;p>When I first saw this formal technique, it puzzled me. It didn&amp;rsquo;t seem to be valid: alright, assuming something is false leads to a paradox, so what? We haven&amp;rsquo;t proven that assuming it&amp;rsquo;s true doesn&amp;rsquo;t lead to another paradox! Or even the same paradox, for that matter. What I failed to understand conceptually is that a statement is a binary thing: it&amp;rsquo;s either true or untrue. Nothing in between. So, if one can definitely declare &amp;ldquo;X is not false&amp;rdquo;, then no other options are left: &amp;ldquo;X must be true&amp;rdquo;.&lt;/p>
&lt;h2 id="direct-proof">Direct proof&lt;/h2>
&lt;p>To demonstrate this, let&amp;rsquo;s first use another technique of a &lt;em>direct proof&lt;/em> so that we have something to work with.&lt;/p>
&lt;p>&lt;strong>Theorem 1.&lt;/strong> If \(n\) is an odd positive integer, then \(n^2\) is odd.&lt;/p>
&lt;p>A &lt;em>direct proof&lt;/em> just goes head in, trying to see what the statement means if we kinda play with it.&lt;/p>
&lt;p>&lt;strong>Proof.&lt;/strong> An odd positive integer can be written as \( n = 2k + 1 \), since something like \( 2k \) is even and adding 1 makes it definitely odd. We&amp;rsquo;re interested in what odd squared looks like, so let&amp;rsquo;s square this definition:&lt;/p>
&lt;p>\[ n^2 = (2k + 1)^2 = \]&lt;br>
\[4k^2 + 4k + 1 = \]&lt;br>
\[ 2(2k^2 + 2k) + 1 \]&lt;/p>
&lt;p>So, we have this final formula \( 2(2k^2 + 2k) + 1 \) and it follows the pattern of \( 2k + 1 \). This means it&amp;rsquo;s odd! We have a proof. ■&lt;/p>
&lt;p>This theorem is based on an idea that numbers described as \( 2k + 1 \) are definitely odd. This might be another theorem that requires another proof, and that proof might be based on some other theorems. The general idea of mathematics is that if you follow any theorem to the very beginning, you&amp;rsquo;ll meet the fundamental axioms, the basis of everything.&lt;/p>
&lt;p>Now that we have this proven theorem in our arsenal, let&amp;rsquo;s take a look at another theorem and prove it by contradiction.&lt;/p>
&lt;h2 id="proof-by-contradiction">Proof by contradiction&lt;/h2>
&lt;p>&lt;strong>Theorem 2.&lt;/strong> \(n\) is a positive integer. If \( n^2 \) is even, then \(n\) is even.&lt;/p>
&lt;p>We may try to construct another direct proof, but creating paradoxes is much more fun!&lt;/p>
&lt;p>&lt;strong>Proof.&lt;/strong> Let&amp;rsquo;s assume that \(n^2\) is even, &lt;strong>but \(n\) is odd&lt;/strong>. This is the opposite of what we want, and we will show that this scenario is impossible.&lt;/p>
&lt;p>\(n\) is odd, and from Theorem 1 we know that \(n^2\) must be odd. This doesn&amp;rsquo;t make sense! Our assumption and our conclusion are the opposite. This is a paradox, so the assumption was wrong. Meaning, the idea &amp;ldquo;\(n^2\) is even, but \(n\) is odd&amp;rdquo; is false. Therefore, the idea &amp;ldquo;\(n^2\) is even, \(n\) is even&amp;rdquo; is true.■&lt;/p>
&lt;h2 id="famous-irrational--sqrt-2">Famous irrational \( \sqrt{2} \)&lt;/h2>
&lt;p>&lt;strong>Theorem 3.&lt;/strong> \( \sqrt{2} \) is irrational.&lt;/p>
&lt;p>Woah, this is&amp;hellip; different. In the first two theorems we had formulas, something to play with, something physical. This now is just an idea, so how would we even start?&lt;/p>
&lt;p>Let&amp;rsquo;s start with a definition.&lt;/p>
&lt;blockquote>
&lt;p>In mathematics, the irrational numbers are all the real numbers which are not rational numbers.&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/p>
&lt;/blockquote>
&lt;p>Doesn&amp;rsquo;t seem helpful, but let&amp;rsquo;s continue. What are rational numbers then? Are they some reasonable beings who make optimal decisions all the time?&lt;/p>
&lt;blockquote>
&lt;p>A rational number is any number that can be expressed as the fraction \(\frac{p}{q}\) of two integers.&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>&lt;/p>
&lt;/blockquote>
&lt;p>Oh! They are rational because they are &lt;em>ratios&lt;/em>!&lt;/p>
&lt;p>Just to make things super clear, let&amp;rsquo;s dig one more step and make sure we understand integers.&lt;/p>
&lt;blockquote>
&lt;p>An integer (from the Latin &lt;em>integer&lt;/em> meaning &amp;ldquo;whole&amp;rdquo;) is a number that can be written without a fractional component. For example, 21, 4, 0, and −2048 are integers, while \(9.75\), \( 5\frac{1}{2} \) and \( \sqrt{2} \) are not.&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>&lt;/p>
&lt;/blockquote>
&lt;p>Combining these things, we can construct a comprehensive definition of an irrational number: it&amp;rsquo;s a number that cannot be expressed as the fraction of two whole numbers.&lt;/p>
&lt;p>Now, let&amp;rsquo;s apply this to Theorem 3 so that it has some meat:&lt;/p>
&lt;p>&lt;strong>Theorem 3.&lt;/strong> \( \sqrt{2} \) cannot be expressed as \( \frac{p}{q} \), where \(p\) and \(q\) are integers.&lt;/p>
&lt;p>Alright, now there is something to play with!&lt;/p>
&lt;p>&lt;strong>Proof.&lt;/strong> Start by assuming the opposite &amp;ndash; \( \sqrt{2} \) is rational. This means it can be written as a fraction of two integers:&lt;/p>
&lt;p>\[ \sqrt{2} = \frac{p}{q}\ \]&lt;/p>
&lt;p>We can assume that \(p\) and \(q\) are not &lt;strong>both&lt;/strong> even, because if they are, we can reduce them by dividing both by a common factor (like, for example, \( \frac{8}{10}\ \) should be reduced to \( \frac{4}{5}\ \)). In other words, if they are both even, reduce them until at least one is odd and no further reductions are possible.&lt;/p>
&lt;p>Now, let&amp;rsquo;s square the square root:&lt;/p>
&lt;p>\[ (\sqrt{2})^2 = \frac{p^2}{q^2}\ \]&lt;/p>
&lt;p>\[ 2 = \frac{p^2}{q^2}\ \]&lt;/p>
&lt;p>\[ p^2 = 2q^2 \]&lt;/p>
&lt;p>Remember, something like \(2k + 1\) is odd, and \(2k\) is even. Here we see this pattern: \(p^2 = 2q^2\), which means that \(p^2\) is even (it consists of &lt;em>two&lt;/em> things).&lt;/p>
&lt;p>Then, using Theorem 2, we can say that \(p\) is even as well, which means we can write \(p\) as \(p = 2k\). So:&lt;/p>
&lt;p>\[ 2q^2 = p^2 = (2k)^2 \]&lt;/p>
&lt;p>\[ 2q^2 = 4k^2 \]&lt;/p>
&lt;p>Divide both by two:&lt;/p>
&lt;p>\[ q^2 = 2k^2 \]&lt;/p>
&lt;p>So, \(q^2\) is even. By the same Theorem 2 it follows that \(q\) is even.&lt;/p>
&lt;p>Let&amp;rsquo;s summarize the two conclusions:&lt;/p>
&lt;ol>
&lt;li>\(p\) is even.&lt;/li>
&lt;li>\(q\) is even.&lt;/li>
&lt;/ol>
&lt;p>Wait&amp;hellip; We made sure that not both \(p\) and \(q\) are even before starting this whole thing! We made sure to reduce them until at least one is odd, but then, by applying Theorem 2, we ended up with two even numbers. This is impossible, so the idea that &amp;ldquo;\(\sqrt{2}\) is rational&amp;rdquo; is not true.&lt;/p>
&lt;p>Therefore, \(\sqrt{2}\) is irrational.■&lt;/p>
&lt;p>&lt;em>P.S. I often use proof by contradiction in real life by arguing that, for example, not eating the whole bucket of ice cream at once will lead to a paradox that endangers the whole fabric of space-time. It works for me, but your mileage my vary.&lt;/em>&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Irrational%5Fnumber">https://en.wikipedia.org/wiki/Irrational%5Fnumber&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Rational%5Fnumber">https://en.wikipedia.org/wiki/Rational%5Fnumber&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Integer">https://en.wikipedia.org/wiki/Integer&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Keyboard fanaticism</title><link>https://rakhim.org/keyboard-fanaticism/</link><pubDate>Mon, 10 Sep 2018 16:54:00 +0300</pubDate><guid>https://rakhim.org/keyboard-fanaticism/</guid><description>
&lt;p>I&amp;rsquo;ve been reading &lt;a href="https://sites.google.com/site/steveyegge2/effective-emacs">an article about Emacs&lt;/a>, and this paragraph had nailed me right into the soul:&lt;/p>
&lt;blockquote>
&lt;p>IDE users spend most of their time fumbling around with the mouse. They wouldn&amp;rsquo;t dream of doing it any other way, but they don&amp;rsquo;t realize how inefficient their motions are.&lt;/p>
&lt;p>&amp;hellip;&lt;/p>
&lt;p>Whenever you need to jump the cursor backward or forward more than about 5 lines, and you can see the target location, you should be using i-search.&lt;br>
&amp;hellip;&lt;/p>
&lt;p>Let your eye defocus slightly and take in the whole paragraph or region around the target point, and choose a word that looks reasonably unique or easy to type. Then i-search for it to navigate to it. You may need to hit Ctrl-r or Ctrl-s repeatedly if your anchor word turns out not to be unique.&lt;/p>
&lt;/blockquote>
&lt;p>This is a common rhetoric: use keyboard only, don&amp;rsquo;t you dare to use the mouse — it&amp;rsquo;s so inefficient!&lt;/p>
&lt;p>The scenario in question is simple: you have to move the cursor to some position you see on the screen. Instead of moving your hand to the mouse to move the pointer, the author suggests the following algorithm:&lt;/p>
&lt;ol>
&lt;li>Determine if the place you need to go to is before or after current position. This is non-zero mental work.&lt;/li>
&lt;li>Take a look around that point and &amp;ldquo;choose a word that looks reasonably unique&amp;rdquo;. Perform more mental work of determining which word is unique enough.&lt;/li>
&lt;li>If the target is before the current position, use &lt;code>Ctrl+s&lt;/code>. If it&amp;rsquo;s after, use &lt;code>Ctrl+r&lt;/code>. This is more or less automatic, but still required mental work of maintaining the mapping between direction and binding.&lt;/li>
&lt;li>If your judgement of the uniqueness wasn&amp;rsquo;t good enough, you&amp;rsquo;ll end up somewhere else. Possibly, in a completely different section of the document. Additional mental work — you have to realize what happened, disoriented. Keep hitting &lt;code>Ctrl+s&lt;/code> or &lt;code>Ctrl+r&lt;/code>. And you have to keep scanning the surroundings every time you jump until you get where you want.&lt;/li>
&lt;li>Okay, you&amp;rsquo;re there! But remember, you&amp;rsquo;ve been jumping to a place &lt;strong>near&lt;/strong> the target, so now you have to move a bit more — by word or by character.&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>Mastering it simply requires that you do it repeatedly until your fingers do it &amp;ldquo;automatically&amp;rdquo;. Emacs eventually becomes like an extension of your body, and you&amp;rsquo;ll be performing hundreds of different keystrokes and mini-techniques like this one without thinking about them.&lt;/p>
&lt;/blockquote>
&lt;p>While I understand the premise completely, and I occasionally use the same technique, I can’t help but think an advice like that rarely takes into account the trade-off. Yes, moving your hand to the mouse takes time, but it’s not uncommon that the time required is actually &lt;strong>less&lt;/strong> than multi key multi step keybinding. Instead of spending a second, two motions and a single click the user is advised to analyze text, make several decisions and hit multiple keys, which might or might not be enough. But hey, you didn&amp;rsquo;t leave the home row, so, win, I guess?..&lt;/p>
&lt;p>I&amp;rsquo;m not defending the mouse here, but I do think there are occasions where using the mouse is just better &lt;strong>for me&lt;/strong>. Too often these articles are trying to make you feel like an unintelligent cave man for daring to use the &amp;ldquo;device of IDE users&amp;rdquo;.&lt;/p>
&lt;p>Also, Emacs packages like &lt;a href="https://github.com/abo-abo/avy">avy&lt;/a> or &lt;a href="https://github.com/hlissner/evil-snipe">evil-snipe&lt;/a> make jumping to visible text much simpler and cost less mentally.&lt;/p>
&lt;p>The vast sea of discussions and advice about programming tools and especially text editing is full of opinions, approaches and cult-like repeated revelations. Often, the loud sounds of the echo chamber make it difficult to stop for a moment and evaluate something yourself. But please do try.&lt;/p>
&lt;p>It&amp;rsquo;s easy to be indoctrinated.&lt;/p></description></item><item><title>Personal finances and controlled anarchy</title><link>https://rakhim.org/personal-finances-and-controlled-anarchy/</link><pubDate>Fri, 07 Sep 2018 12:31:00 +0300</pubDate><guid>https://rakhim.org/personal-finances-and-controlled-anarchy/</guid><description>
&lt;p>Most of my life, I didn&amp;rsquo;t have much money. I was born in the 90s in a tiny Kazakh town, and nobody had a job there, it seemed. USSR just collapsed and my parents were trying to make ends meet in a constant hustle.&lt;/p>
&lt;p>That life defined my relationship with money and wealth for years to come. Every time I had to buy coffee, I was thinking &amp;ldquo;is it worth it?.. Maybe I shouldn&amp;rsquo;t&amp;rdquo;. In restaurants and cafes I was looking at prices first, then at meals. &amp;ldquo;Hmm, this cheap pasta looks so attractive! Ooh, I bet this expensive steak is not that good&amp;rdquo;.&lt;/p>
&lt;p>&amp;ldquo;You know what? I&amp;rsquo;ll just buy this coffee and not buy that iPhone game I wanted to buy. Yeah, perfect! Now I&amp;rsquo;m calm and safe!&amp;rdquo;&lt;/p>
&lt;p>After graduating and starting working full time I decided to follow a popular advice: budget everything. I started using an excellent app called &lt;a href="https://www.youneedabudget.com/">YNAB&lt;/a> — You Need a Budget. Not only it allows you to track all your expenses and plan ahead, it also comes with a philosophy, a set of rules and ideas to help you navigate your personal finance world.&lt;/p>
&lt;p>First things first: save one month worth of expenses and never let your account get dry. The idea is to be spending money that is at least 30 days old. So, if you got your salary on February 1st, you will spend this money in March or later, but not in February. This way you never get into «I need some money until my next salary».&lt;/p>
&lt;p>Next, give every dollar a job. This means that each dollar you get — you decide what it&amp;rsquo;s for. I have regular expenses like rent, phone fees, groceries etc. Some amount of money MUST go there. But I also have other categories, like &amp;ldquo;Books&amp;rdquo; or &amp;ldquo;Electronics&amp;rdquo; or &amp;ldquo;Travel&amp;rdquo;. And if I want to buy a book or go for a vacation, I have to have enough money saved in that category.&lt;/p>
&lt;p>Putting money into savings account is another type of a job.&lt;/p>
&lt;p>YNAB allows you to assign every dollar a particular job. It actually encourages you to keep exactly ZERO cents unassigned! You feel like a finance director of a small enterprise. Serious business!&lt;/p>
&lt;p>This way you know exactly whether you can afford something. And you never have to guess &amp;ldquo;hmm, if I buy this laptop now, will I be okay with the rent?..&amp;rdquo;.&lt;/p>
&lt;p>&lt;strong>&lt;img src="https://rakhim.org/images/posts/ynab.jpg" alt="">&lt;/strong>&lt;br>
&lt;em>YNAB classic app screenshot (not mine)&lt;/em>&lt;/p>
&lt;p>Another YNAB rule is to budget in detail and ahead. &amp;ldquo;Make your money boring&amp;rdquo; is their slogan for it. For every bill to come or an unexpected expense to surprise you, you&amp;rsquo;ll have money waiting.&lt;/p>
&lt;p>For example, I was putting some money into &amp;ldquo;Car repair&amp;rdquo; category each month, even though for the most part my car didn&amp;rsquo;t require any repair. But when the AC compressor suddenly died in the middle of the hot German summer vacation, I knew I don&amp;rsquo;t have to worry.&lt;/p>
&lt;p>Basically, save money for Christmas all year long, not just in December.&lt;/p>
&lt;p>One more YNAB rule is to &amp;ldquo;Roll With The Punches&amp;rdquo;. When you overspend in a budget category, just adjust. No guilt necessary! It was easy for me to justify another gadget when I under-spent in some other categories.&lt;/p>
&lt;p>I was an everyday user of YNAB for 7 years. The app itself is 14 years old and it has a great following and a nice community around it. It helped me &lt;strong>tremendously&lt;/strong>! A huge amount of stress just went away, I was on top of my finances, I knew exactly what&amp;rsquo;s happening and how much money I&amp;rsquo;m getting and spending. When my girlfriend moved in with me and we started sharing our budgets, YNAB was able to accommodate it. I just added another bank account in the settings. In total, I was controlling multiple bank accounts (including &amp;ldquo;cash&amp;rdquo; account) and cards, several sources of income and tens of budgeting categories.&lt;/p>
&lt;p>It was great.&lt;/p>
&lt;p>So, why did I stop?&lt;/p>
&lt;p>Don&amp;rsquo;t get me wrong: an app and a method like that makes a HUGE difference. I will never go back to having no control and no knowledge over my finances, but I still had lots of stress points.&lt;/p>
&lt;p>First, it took a lot of time and energy to maintain the system. I had to put all the expenses precisely, every purchase, every fee, including cash purchases. The system makes sense only if you&amp;rsquo;re precise and 100% accurate.&lt;/p>
&lt;p>Card purchases overseas were especially painful. They often change over time, like, you buy something off Amazon, and they charge you with currency conversion, and after a week or so an &amp;ldquo;adjustment&amp;rdquo; charge is made silently (since the exchange rate changed a bit). You have to track it all and &amp;ldquo;consolidate&amp;rdquo; your accounts every month.&lt;/p>
&lt;p>Or you just forget what that $0.99 supermarket purchase was three weeks ago. Was it chocolate, so, groceries category? Or a LAN cable, so, electronics category? Does it really matter? It&amp;rsquo;s just 99 cents, so… whatever, let it be groceries.&lt;/p>
&lt;p>Another problem was — I still had some stress over money. Less than before, but still. This &amp;ldquo;roll with the punches&amp;rdquo; rule is nice and liberating, but sometimes it seemed like I was just abusing the system. I want a new gadget, so, I&amp;rsquo;ll just transfer $100 from &amp;ldquo;car repair&amp;rdquo; and compensate next month by spending less in every category. It&amp;rsquo;ll be just fine!&lt;/p>
&lt;p>It takes lots of energy not only to maintain the system, but to keep disciplined. I&amp;rsquo;m not that good at it.&lt;/p>
&lt;p>A year ago I decided to deliberately simplify my life. Automate everything I can, ignore more stuff, eliminate pain points and minimize the mental energy requirements on everything except first-order things.&lt;/p>
&lt;p>First-order things are the actual things I want to spend time on, the things that are intrinsically important for me. Money is a tool, so, it&amp;rsquo;s at most second-order. It allows for the first-order things, but it doesn&amp;rsquo;t have intrinsic value itself.&lt;/p>
&lt;p>So I ditched YNAB and budgeting in general.&lt;/p>
&lt;p>This was the most liberating moment I had in some time!&lt;/p>
&lt;p>I call my new system &amp;ldquo;controlled anarchy&amp;rdquo;, and it&amp;rsquo;s pretty simple.&lt;/p>
&lt;p>Every time our family gets salary payment or other income, I distribute it between three bank accounts:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Monthly bills.&lt;/strong> This account pays all the bills, from rent to Netflix. It has its own debit card, so I don&amp;rsquo;t really see the purchases very often. I know exactly how much money is spent, though, since all the expenses here are static. Like with YNAB philosophy, this account has 2 months of expenses all the time, so it never gets dry. (I am actually increasing this account to 6 months of expenses so it will act as the emergency fund. Bad things happen — we have 6 months to figure things out).&lt;/li>
&lt;li>&lt;strong>Savings.&lt;/strong>. Yup, just savings. At least 33% of all the income is saved. A portion of it is invested in mutual funds for the long term.&lt;/li>
&lt;li>&lt;strong>Everyday spending.&lt;/strong> The rest is free! This is the key — I don&amp;rsquo;t have to plan or to calculate or track anything. This account is the free money we can spend however we want! (Some of it goes to groceries, but the rest is truly free).&lt;/li>
&lt;/ol>
&lt;p>The Everyday spending account rarely gets to zero, and we never move money away from it. So, it actually grows gradually, and if we don&amp;rsquo;t spend it all one month, we get even more free money next month!&lt;/p>
&lt;p>The goal is to eliminate guilt and uncertainty about purchases. You want that new thing? Just buy it if there&amp;rsquo;s enough money. Not enough in Everyday spending? Well, sorry, you can&amp;rsquo;t buy it. But hey — feel free to buy whatever — spend it all away!&lt;/p>
&lt;p>Oh, man, this made our lives so much easier.&lt;/p>
&lt;p>The &amp;ldquo;controlled anarchy&amp;rdquo; system lacks the precision of the previous one, but requires no time and energy to maintain. All the payments and transfers are automatic, it&amp;rsquo;s like we&amp;rsquo;re kids and a wise parent manages our spending money :)&lt;/p>
&lt;p>This is what I call simplification: less decision-making means more energy for the truly important things.&lt;/p>
&lt;p>Now, if you don&amp;rsquo;t do any sort of budgeting and don&amp;rsquo;t really control your money, I&amp;rsquo;m not sure going into &amp;ldquo;controlled anarchy&amp;rdquo; right away is a good idea. It seems like it&amp;rsquo;s alright, but maybe you should try real detailed budgeting first, maybe for a year or so, just so that you understand what&amp;rsquo;s going on, where money goes to.&lt;/p></description></item><item><title>Agile Food Truck</title><link>https://rakhim.org/honestly-undefined/3/</link><pubDate>Fri, 24 Aug 2018 23:16:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/3/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/agile_food_truck.jpg"></description></item><item><title>Reactive solution</title><link>https://rakhim.org/honestly-undefined/2/</link><pubDate>Fri, 24 Aug 2018 21:16:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/2/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/react_js.jpg"></description></item><item><title>Spaceship Problems</title><link>https://rakhim.org/honestly-undefined/1/</link><pubDate>Sun, 19 Aug 2018 23:19:00 +0300</pubDate><guid>https://rakhim.org/honestly-undefined/1/</guid><description>
&lt;img src="https://rakhim.org/images/honestly-undefined/spaceship_money.jpg"></description></item><item><title>Podcasting is not walled (yet)</title><link>https://rakhim.org/podcasting-is-not-walled/</link><pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate><guid>https://rakhim.org/podcasting-is-not-walled/</guid><description>
&lt;p>I&amp;rsquo;ve launched a podcast recently (&lt;a href="http://emacscast.rakhim.org/">EmacsCast&lt;/a>) and received lots of feedback. One of the perplexing things people said was &amp;ldquo;&lt;em>that&amp;rsquo;s great, but how do I subscribe, it&amp;rsquo;s not on iTunes/Google Podcasts?&lt;/em>&amp;rdquo;&lt;/p>
&lt;p>I had a similar experience 10+ years ago when I started podcasting, but today it&amp;rsquo;s much worse. It worries me a lot.&lt;/p>
&lt;p>Podcasts are simply RSS feeds with links to media files (usually mp3s). A podcast is basically a URL. And podcast clients are special browsers. They check that URL regularly and download new episodes if the content of the URL changes (new link added). That&amp;rsquo;s it, no magic, no special membership or anything else required. The technology is pretty &amp;ldquo;stupid&amp;rdquo; in a good way.&lt;/p>
&lt;p>Video podcasts were a thing! It was basically distributed, un-censorable YouTube where both viewers and creators had full control over their things.&lt;/p>
&lt;p>Ever since tech companies started waging war against RSS, podcast distribution became visually RSS-free. What do you do to subscribe? Easy, just search in the app! For the majority of iOS users that app is Apple Podcasts, and recently Google made their own &amp;ldquo;default client&amp;rdquo; for Android — Google Podcasts.&lt;/p>
&lt;p>It looks like podcast clients are similar to web browsers and just provide a way to consume content, but the underlying listings make them very different. Corresponding services are actually isolated catalogs. When you perform a search on Apple Podcasts, you aren&amp;rsquo;t searching for podcasts. &lt;strong>You are searching for Apple-approved podcasts.&lt;/strong> And if the thing you&amp;rsquo;re looking for is not there, then&amp;hellip; well, you get nothing.&lt;/p>
&lt;p>Imagine web browsers worked that way. You want to visit my blog, but you don&amp;rsquo;t know what URLs are, you&amp;rsquo;ve never seen or heard of things like &lt;code>https://rakhim.org&lt;/code>. There&amp;rsquo;s no address bar in your browser, just a &amp;ldquo;search catalog&amp;rdquo; field.&lt;/p>
&lt;p>I tell you my name and you type it into Safari. If my blog was already added to Apple Websites Catalogue, then great, you can visit my site.&lt;/p>
&lt;p>But if I haven&amp;rsquo;t added my blog to their catalog, or, even worse, I&amp;rsquo;ve tried, but it wasn&amp;rsquo;t approved for some reason, then I&amp;rsquo;ll be just sitting here, shouting &amp;ldquo;dude, just go to &lt;code>https://rakhim.org&lt;/code>, it&amp;rsquo;s &lt;strong>there!&lt;/strong>&amp;rdquo;, but you&amp;rsquo;d have no idea what to do with that information. For you, &lt;em>my site doesn&amp;rsquo;t exist&lt;/em>.&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/future_of_the_web.jpg" alt="future_of_the_web.png">&lt;/p>
&lt;p>Your browser is capable of reaching my blog, but the feature is hidden or about to disappear. I can teach you to find some obscure menu item, but I can&amp;rsquo;t do it for all potential visitors.&lt;/p>
&lt;p>Most Podcast clients still accept RSS. Apple Podcasts, iTunes, PocketCasts, OverCast, PodcastAddict.&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/itunespodcast.png" alt="itunespodcast.png">&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/iospodcasts.jpeg" alt="iospodcasts.jpeg">&lt;/p>
&lt;p>Google Play Music doesn&amp;rsquo;t say anything explicitly, but you can just put RSS URL into the search field and it works. For now. I won&amp;rsquo;t be surprised if these apps gradually and silently remove this feature.&lt;/p>
&lt;p>Lately, there are lots of discussions about Apple, Facebook, YouTube, and Spotify banning Alex Jones&amp;rsquo;s Infowars podcast. And most of the debate comes down to either &amp;ldquo;they have a right&amp;rdquo; or &amp;ldquo;they shouldn&amp;rsquo;t censor&amp;rdquo;.&lt;/p>
&lt;p>The thing is, &lt;strong>this shouldn&amp;rsquo;t matter&lt;/strong>. If some company decides not to include an URL in their catalog, this shouldn&amp;rsquo;t really matter. An URL is an URL, the content is there. Both Apple and Google are pretty much hiding the feature that makes podcasting as free and un-censorable as websites.&lt;/p>
&lt;p>And it works! For most people, RSS for podcasts don&amp;rsquo;t exist, and a corporation is in charge of what they can listen to.&lt;/p>
&lt;p>&lt;a href="https://rakhim.org/2018/07/software-shouldnt-fail">Last time&lt;/a> I was talking about programmers&amp;rsquo; responsibilities when it comes to software reliability. Today I want to add: we, developers and software engineers, also have the moral responsibility to educate the public about the way the internet was supposed to be. Open, un-censorable, with underlying protocols put in place to ensure it&amp;rsquo;s a forest, not a walled garden.&lt;/p></description></item><item><title>We shouldn't let people get used to the idea that software fails</title><link>https://rakhim.org/software-shouldnt-fail/</link><pubDate>Wed, 11 Jul 2018 00:00:00 +0000</pubDate><guid>https://rakhim.org/software-shouldnt-fail/</guid><description>
&lt;p>Society has certain expectations when it comes to engineering and technology. We expect buildings and bridges to basically never fail. We expect cars to be extremely safe and fool-proof, as much as explosions-based metal boxes moving at hundred miles per hour can be safe and fool-proof. Home appliances are supposed to work for years without any serious maintenance. Electricity is just there, always. And when it goes out, we are incredibly frustrated and surprised.&lt;/p>
&lt;p>The high expectation of engineering runs deep into relevant industries and educational institutions. Engineers take oaths!&lt;/p>
&lt;hr>
&lt;p>&lt;strong>The Obligation of The Engineer&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>I am an Engineer.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>In my profession I take deep pride. To it I owe solemn obligations.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>As an engineer, I, (full name), pledge to practice Integrity and Fair Dealing, Tolerance, and Respect, and to uphold devotion to the standards and dignity of my profession, conscious always that my skill carries with it the obligation to serve humanity by making best use of the Earth&amp;rsquo;s precious wealth.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>As an engineer, I shall participate in none but honest enterprises. When needed, my skill and knowledge shall be given without reservation for the public good. In the performance of duty, and in fidelity to my profession, I shall give the utmost.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;p>It might seem like I&amp;rsquo;m about to start a rant about buggy OS updates and how software engineering must be held accountable as much as industrial engineering. I&amp;rsquo;m not. Just to be clear: yes, I think the state of modern software development is ridiculous. Yes, seems like it&amp;rsquo;ll take quite some time and quite a few technological catastrophes until we take software more seriously.&lt;/p>
&lt;p>What worries me more than the current state of the things is how it affects the mindset of society. These high expectations of engineering are reversely mirrored in software. We have learned to expect software to &lt;em>fail&lt;/em>.&lt;/p>
&lt;p>Just a few decades ago it was different. Software developers were the only ones who sometimes expected software to fail. The general public had high hopes, and journalists helped build it.&lt;/p>
&lt;p>The rise of information technology was akin to the rise of mechanization of the early XX century. We could see the analogy clearly: in the 20s and 30s everyone in the developed world started having and using cars, washing machines, air conditioners and other wonderful devices. They were new, yet incredibly reliable. Every year they became better and cheaper.&lt;/p>
&lt;p>In the 80s and 90s, everyone in the developed world started having and using computers and mobile phones. And at first, the process looked very similar. The first personal computers were clunky and weird, but pretty reliable. I&amp;rsquo;d say, incredibly stable and relatively fast compared to modern computers.&lt;/p>
&lt;p>Of course, they were million times less complex and had fewer features. But hey, here is something to consider: I&amp;rsquo;m typing this in a macOS app on a very powerful 5K iMac computer. It&amp;rsquo;s just text, and I&amp;rsquo;m going to publish it on the web. The whole experience is mediocre at best. The software is not blazing fast, even though it&amp;rsquo;s a native app for text editing (it has very high app store rating and it&amp;rsquo;s one of the apps selected by the app store editors). The typing experience is laggy on a Bluetooth keyboard. To publish this, I&amp;rsquo;m going to interact with a browser, which is at this point the whole other operating system with another layer of delays. The only thing that really feels &amp;ldquo;20 years better&amp;rdquo; is the connection speed itself.&lt;/p>
&lt;p>Almost slipped into a rant there.&lt;/p>
&lt;p>Personal computers felt like the new &amp;ldquo;cars for everyone&amp;rdquo;. We all get computers! They will get better, faster and cheaper every year! They are becoming the backbone of the world, as much as motor vehicles did.&lt;/p>
&lt;p>It all started to change with the Web. The Web was the first global technological phenomena that was built and maintained by the amateurs. Computer hardware, software, and the internet itself were built by mathematicians and engineers. The Web was built by people like me.&lt;/p>
&lt;p>Mathematicians and engineers are tightly connected to the academia, and they operate with ideas like &amp;ldquo;proof&amp;rdquo;, &amp;ldquo;peer review&amp;rdquo; and &amp;ldquo;oath&amp;rdquo;. While many software developers operate with ideas like &amp;ldquo;ship early, ship often&amp;rdquo;, &amp;ldquo;move fast and break things&amp;rdquo; and &amp;ldquo;If you are not embarrassed by the first version of your product, you&amp;rsquo;ve launched too late&amp;rdquo;.&lt;/p>
&lt;p>This isn&amp;rsquo;t bad in the long run. I think the society will go through The Age of the Amateur and get better in the end.&lt;/p>
&lt;p>Amateurs were making things all the time. Up until the recent years, we just couldn&amp;rsquo;t make our amateur inventions ship to millions of people that easily. When amateurs invented something potentially useful, it took quite a few iterations and stages to get it to the public. By that time it became more refined and tested and stable, thanks to regulations, selection and just time.&lt;/p>
&lt;p>Today I can make a bad piece of software that does something interesting, and potentially millions of people will get frustrated or harmed.&lt;/p>
&lt;p>If you asked a member of the general population in the 90s or early 2000s about computers and the internet, I bet most of them would sound optimistic. That&amp;rsquo;s what everyone is talking about, right? Soon, we will do everything with computers! Computers are super smart!&lt;/p>
&lt;p>While this is anecdotal evidence, today most of the people I know are frustrated with technology. Apps are buggy, the web is filled with ads and intrusive useless notifications (would you like some cookies?), touch screens everywhere suddenly made simple things like washing machines and car control panels barely usable.&lt;/p>
&lt;p>We just got used to that. Electronics is something that&amp;rsquo;s wonky and buggy. That&amp;rsquo;s what we expect.&lt;/p>
&lt;p>And this is scary.&lt;/p>
&lt;p>When a society expects certain industries or institutions to fail or do harmful things due to lack of quality control or unprofessionalism, we think that society is in bad shape. When we expect politicians to fail, we revolt. When we expect medicine to fail, we protest. When we expect infrastructure to fail, we at least write angry letters to our representatives and try to make some difference through political means.&lt;/p>
&lt;p>We can&amp;rsquo;t do much when software fails. Unlike science, there&amp;rsquo;s no public accountability. Even in those shrinking areas where there&amp;rsquo;s still competition, there isn&amp;rsquo;t much choice: all software fails. I, for one, can only name a couple of software products that are rock solid. But they don&amp;rsquo;t exist on their own, they operate on top of multiple other layers (hardware, OS, frameworks, web, etc). And it&amp;rsquo;s unlikely that all the layers are as good.&lt;/p>
&lt;p>I&amp;rsquo;m in no position to propose a better way for the world to evolve. But I believe that computer scientists and software engineers have the moral responsibility to educate the public about the way &lt;strong>software should be&lt;/strong>. We shouldn&amp;rsquo;t let people get used to the idea that software inevitably fails.&lt;/p>
&lt;p>(Discussion on &lt;a href="https://lobste.rs/s/abf7r8/we_shouldn_t_let_people_get_used_idea">Lobste.rs&lt;/a>)&lt;/p></description></item><item><title>What is binary?</title><link>https://rakhim.org/binary/</link><pubDate>Fri, 02 Dec 2016 00:00:00 +0000</pubDate><guid>https://rakhim.org/binary/</guid><description>
&lt;p>My attempt to explain binary numbers.&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/binary.png" alt="">&lt;/p></description></item><item><title>Backups</title><link>https://rakhim.org/backup/</link><pubDate>Tue, 29 Nov 2016 00:00:00 +0000</pubDate><guid>https://rakhim.org/backup/</guid><description>
&lt;p>I&amp;rsquo;m not going to try to convince you to backup data. I didn&amp;rsquo;t do backups for the most of my life, except for some photos and videos here and there. And those weren&amp;rsquo;t really backups, more like archives on external HDD&amp;rsquo;s.&lt;/p>
&lt;p>I use computers every day since, gosh, I guess 5th grade, and never ever did any hard drive fail on me. This is remarkable. Screens, keyboards, mice, even fans have died over the years, but not hard drives. Good old magnetic, noisy, spinning monsters.&lt;/p>
&lt;p>Backup evangelists love to say how &amp;ldquo;your HDD will fail, it&amp;rsquo;s inevitable&amp;rdquo;. Well, yes, it will fail inevitably as you use it for years, but the truth is — you&amp;rsquo;ll more likely switch computers before your drive fails.&lt;/p>
&lt;p>But the truth is, it&amp;rsquo;s extremely unlikely that you&amp;rsquo;ll ever need a backup. You&amp;rsquo;re probably good. I prefer to think about this stuff as an insurance. This is how you buy peace of mind. There are so few aspects of life where you can actually do that — pay some money (not much, too, which is great), and get some peace of mind. Medical insurance, for example, is sort of like that, but not really that good. With data backup, I can be pretty sure I will get everything back as it used to be, effectively travel back in time. Recovered data is precisely the same as lost data, so it&amp;rsquo;s not really lost anymore. While my body after medical treatment is not the same anymore.&lt;/p>
&lt;p>I&amp;rsquo;m not gonna go all &amp;ldquo;3-2-1 backup&amp;rdquo; on you. That&amp;rsquo;s the idea that in the perfect world you need at least 3 total copies of your data, 2 of which are local but on different devices, and at least 1 copy offsite. So, if all of your files are on your computer, then you need two external hard drives and a remote hard drive (maybe one at work, in another house or in the cloud). I don&amp;rsquo;t do that yet. For now my backup strategy looks like this:&lt;/p>
&lt;ol>
&lt;li>Offsite backups with Arq (Dropbox and Amazon Cloud Drive)&lt;/li>
&lt;li>Local backups with Time Machine and Carbon Copy Cloner&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://rakhim.org/images/posts/backup_strategy.jpg" alt="">&lt;/p>
&lt;p>Most of the things I work on daily are on Github (personal and work), Dropbox (personal) and Google Drive (work).&lt;/p>
&lt;p>Let me first explain why I said no to Backblaze and Crashplan. Long story short: I don&amp;rsquo;t trust them.&lt;/p>
&lt;h3 id="backblaze">Backblaze&lt;/h3>
&lt;p>&lt;a href="https://www.backblaze.com/">Backblaze&lt;/a> is a beautiful, sleek guy who says &amp;ldquo;don&amp;rsquo;t worry about it bro&amp;rdquo;. Mac client is minimal and cool, and it &amp;ldquo;just works&amp;rdquo;.&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/backblaze.jpg" alt="">&lt;/p>
&lt;p>There are few issues with Backblaze:&lt;/p>
&lt;ul>
&lt;li>It&amp;rsquo;s not &lt;em>really&lt;/em> a backup solution. If you delete a file from your computer, then in one month it will also be deleted from Backblaze’s servers. It syncs stuff just like Dropbox does. This is why Dropbox in and of itself is not a proper backup solution.&lt;/li>
&lt;li>If you backup an external drive and disconnect it from your computer, then Backblaze will delete that backup from their servers.&lt;/li>
&lt;li>Backblaze doesn&amp;rsquo;t backup 100% of files. You can remove some exceptions manually, but some of them are built in. So, I can&amp;rsquo;t really have a complete copy of my boot disk, for example.&lt;/li>
&lt;li>If you need to restore files from Backblaze, &lt;strong>you&amp;rsquo;re gonna have a bad time&lt;/strong>. Your options are: download a Zip-archive (if your file is 10 levels deep, then you&amp;rsquo;ll get all the upper level folder structure in the archive) or get a flash drive or a HDD via mail. You can&amp;rsquo;t restore files in place.&lt;/li>
&lt;li>Some users say it&amp;rsquo;s very fast, some say it&amp;rsquo;s very slow. I can&amp;rsquo;t figure out the reasons, but for me it was dead slow. It took almost 120 hours (5 days) to upload less than 200G of data.&lt;/li>
&lt;li>Android app seems to be made by a very intelligent puppy.&lt;/li>
&lt;/ul>
&lt;h3 id="crashplan">Crashplan&lt;/h3>
&lt;p>&lt;a href="https://www.crashplan.com/">Crashplan&lt;/a> is a douchey-looking guy in an expensive suit who says &amp;ldquo;the synergy is just overwhelming in this merger&amp;rdquo;. It took me a while to understand how it really works. It&amp;rsquo;s called CrashPlan Online Backup and it can backup, among other places, to your external hard drive. You know, because online.&lt;/p>
&lt;p>But once you get it, it&amp;rsquo;s pretty great in theory. With Crashplan you can backup to any local hard drive and offsite machine (like friend&amp;rsquo;s computer or any other machine in your network) for free, and with additional fees you can also backup to Crashplan&amp;rsquo;s cloud.&lt;/p>
&lt;ul>
&lt;li>If you use their cloud, then it&amp;rsquo;s &lt;em>really&lt;/em> a backup solution. All your files are stored in the cloud forever&lt;sup>*&lt;/sup>. Unlike Backblaze.&lt;/li>
&lt;li>You can restore any file to its original location or any other location. No need to download Zip-files from a cumbersome web interface.&lt;/li>
&lt;/ul>
&lt;p>&lt;em>(* not forever)&lt;/em>&lt;/p>
&lt;p>Crashplan Mac app is&amp;hellip; well&amp;hellip; ugly like hell.&lt;/p>
&lt;div style="text-align: center; margin-bottom: 1em;">
&lt;img src="https://rakhim.org/images/posts/crashplan.png"/>
&lt;/div>
&lt;p>Look at this Java shit.&lt;/p>
&lt;p>I was happy with Crashplan for the first few days, and possibilities of adding more backup destinations if I decide to go all &amp;ldquo;3-2-1&amp;rdquo; was reassuring. But it turned out I can&amp;rsquo;t trust it.&lt;/p>
&lt;p>Crashplan, like any other software of that type, is supposed to work in the background, doing its thing while I do mine. I was restructuring files in my photo archives, moving files from folder to folder, renaming stuff. Nothing extreme. But in few days when Crashplan said it backed everything up, I tried to restore some files to check how it works. And it just lost the whole photos folder, tens gigabytes of photos. That was the folder I was fiddling with.&lt;/p>
&lt;p>I understand this is unfair. Trying to make a reliable copy of the file system while it changes is hard. But:&lt;/p>
&lt;ol>
&lt;li>Should it fail, it must &lt;strong>fail gracefully&lt;/strong>. Losing all the files is unacceptable.&lt;/li>
&lt;li>The end user should &lt;strong>never worry&lt;/strong> about this stuff. I wasn&amp;rsquo;t doing some crazy hacking, I just moved files around.&lt;/li>
&lt;/ol>
&lt;p>Oh, and Crashplan&amp;rsquo;s Android app seems to be made by a very intelligent puppy as well. Is there a software company ran by puppies I know nothing about?!&lt;/p>
&lt;h3 id="arq">Arq&lt;/h3>
&lt;p>&lt;a href="https://www.arqbackup.com/">Arq&lt;/a> is only an app for your Mac or PC. It doesn&amp;rsquo;t offer any cloud backup storage itself. It&amp;rsquo;s not even a guy like Backblaze or Crashplan. It&amp;rsquo;s a faceless, soulless robot who says nothing. This is what backup software should be like.&lt;/p>
&lt;p>&lt;img src="https://rakhim.org/images/posts/arq.png" alt="">&lt;/p>
&lt;p>Arq can backup to Amazon Cloud Drive, AWS, Amazon Glacier, Google Drive, Google Cloud Drive, Dropbox, OneDrive, your SFTP server or NAS. You can set multiple sources and destinations. For example, I have the following setup:&lt;/p>
&lt;ol>
&lt;li>Home folder → Dropbox&lt;/li>
&lt;li>Photo archive → Dropbox and Amazon Cloud Drive&lt;/li>
&lt;li>Podcasting archive → Dropbox&lt;/li>
&lt;li>Current video projects → Dropbox and Amazon Cloud Drive&lt;/li>
&lt;li>Work-related podcasting archive → Google Drive&lt;/li>
&lt;/ol>
&lt;p>And at any point I can add other destinations, including a network attached storage.&lt;/p>
&lt;p>My Mac&amp;rsquo;s SSD is just 120G, but my Dropbox is 1TB. It&amp;rsquo;s great to finally make all that space useful by setting Dropbox as a destination for Arq. And &lt;a href="https://www.amazon.com/clouddrive/home">Amazon Cloud Drive&lt;/a> is just a great deal — unlimited storage for $60 per year. Of course, you have to remember, that it&amp;rsquo;s not really unlimited, and if you go crazy, Amazon is allowed to kick you out.&lt;/p>
&lt;p>Some cool features of Arq include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Local encryption&lt;/strong>. Arq encrypts files before sending it, and sends it with SSL.&lt;/li>
&lt;li>Backups are stored in &lt;strong>open documented format&lt;/strong>. Even if Arq dies and completely disappears, your encrypted data is safe and accessible.&lt;/li>
&lt;li>A very nice and straight-forward &lt;strong>native app&lt;/strong> with some advanced features like CPU usage, upload rate, scheduling, data validation, budget restrictions (relevant for AWS, for example). You can also make Arq run shell scripts before and after backup sessions.&lt;/li>
&lt;li>Arq truly backups everything. All the files, any format, any size (even crazy tens-of-gigs files) &lt;strong>without restrictions&lt;/strong>.&lt;/li>
&lt;li>Fixed price and no recurring fees.&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://rakhim.org/images/posts/arq2.png" alt="">&lt;/p>
&lt;p>Arq also can &lt;a href="https://www.arqbackup.com/arq_help/pages/archiving.html">archive&lt;/a>: backup a folder, click &amp;ldquo;Detach&amp;hellip;&amp;rdquo; and Arq will stop backing it up, but will store the previous backups indefinitely. This is great if you want to backup some external drives that rarely update.&lt;/p>
&lt;h3 id="local-backups-time-machine-and-carbon-copy-cloner">Local backups: Time Machine and Carbon Copy Cloner&lt;/h3>
&lt;p>My photos and work-related audio- and video-projects live on an external drive and are backed up to the cloud along with the home folder. The whole internal SSD gets only local backups, because it&amp;rsquo;s not that important. There are two things I need from this system:&lt;/p>
&lt;ol>
&lt;li>Restore the system to a previous state. І never needed this with Macs, but I just feel safer this way when I upgrade the OS.&lt;/li>
&lt;li>Boot from USB drive if internal drive fails. As I said, I didn&amp;rsquo;t have failing drives (neither HDD&amp;rsquo;s nor SSD&amp;rsquo;s) in my life, but SATA cables do fail sometimes. Should that happen, I can just boot from an external drive and continue working until the problem is solved.&lt;/li>
&lt;/ol>
&lt;p>Time Machine is good enough for quick restoration. I use &lt;a href="https://bombich.com/">Carbon Copy Cloner&lt;/a> to make an external bootable copy. Alternatively, &lt;a href="http://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html">SuperDuper!&lt;/a> is also nice. I like CCC more because it also copies the &lt;a href="https://bombich.com/kb/ccc4/cloning-apples-recovery-hd-partition">recovery partition&lt;/a>, which is used to reinstall macOS. You don&amp;rsquo;t &lt;strong>really&lt;/strong> need it, because all modern Macs come with a Network Recovery option.&lt;/p>
&lt;p>That&amp;rsquo;s it folks.&lt;/p></description></item></channel></rss>