Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
70
Каким должен быть редактор кода в 2024 году? Почему Vim морально устарел, а IDEA, кажется, сдает позиции? Популярность Zed, минимализм SublimeText, гибкость Emacs и многое другое в новом выпуске.
9 months ago

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from tonsky.me

Podcast: Datomic: самая рок-н-рольная БД @ Тысяча фичей

Чем Datomic отличается от других баз данных и почему иногда остутствие оптимизатора лучше, чем его присутствие

a week ago 10 votes
Talk: Local-first is not going to win, but that’s okay @ Local-First Conf

We’ll explore the complexities of traditional stack (db-server-frontend), develop a theory of software evolution: which systems succeed and why. Then we’ll see how local-first fits into it and which local-first-adjacent practices are making software development easier and therefore are doomed for success (or not?)

a month ago 6 votes
Logo:

Cyrillic version of Internet Explorer logo. Because it’s iconic.

2 months ago 19 votes
When You Get to Be Smart Writing a Macro

Day-to-day programming isn’t always exciting. Most of the code we write is pretty straightforward: open a file, apply a function, commit a transaction, send JSON. Finding a problem that can be solved not the hard way, but smart way, is quite rare. I’m really happy I found this one. I’ve been using hashp for debugging for a long time. Think of it as a better println. Instead of writing (println "x" x) you write #p x It returns the original value, is shorter to write, and doesn’t add an extra level of parentheses. All good. It even prints original form, so you know which value came from where. Under the hood, it’s basically: (defn hashp [form] `(let [res# ~form] (println '~form res#) res#)) Nothing mind-blowing. It behaves like a macro but is substituted through a reader tag, so defn instead of defmacro. Okay. Now for the fun stuff. What happens if I add it to a thread-first macro? Nothing good: user=> (-> 1 inc inc #p (* 10) inc inc) Syntax error macroexpanding clojure.core/let at (REPL:1:1). (inc (inc 1)) - failed: vector? at: [:bindings] spec: :clojure.core.specs.alpha/bindings Makes sense. Reader tags are expanded first, so it replaced inc with (let [...] ...) and then tried to do threading. Wouldn’t fly. We can invent a macro that would work, though: (defn p->-impl [first-arg form fn & args] (let [res (apply fn first-arg args)] (println "#p->" form "=>" res) res)) (defn p-> [form] (list* 'p->-impl (list 'quote form) form)) (set! *data-readers* (assoc *data-readers* 'p-> #'p->)) Then it will expand to user=> '(-> 1 inc inc #p-> (* 10) inc inc) (-> 1 inc inc (p->-impl '(* 10) * 10) inc inc) and, ultimately, work: user=> (-> 1 inc inc #p-> (* 10) inc inc) #p-> (* 10) => 30 32 Problem? It’s a different macro. We’ll need another one for ->>, too, so three in total. Can we make just one instead? Turns out you can! Trick is to use a probe. We produce an anonymous function with two arguments. Then we call it in place with one argument (::undef) and see where other argument goes. Inside, we check where ::undef lands: first position means we’re inside ->>, otherwise, ->: ((fn [x y] (cond (= ::undef x) <thread-last> (= ::undef y) <thread-first>)) ::undef) Let’s see how it behaves: (macroexpand-1 '(-> "input" ((fn [x y] (cond (= ::undef x) <thread-last> (= ::undef y) <thread-first>)) ::undef))) ((fn [x y] (cond (= ::undef x) <thread-last> (= ::undef y) <thread-first>)) "input" ::undef) (macroexpand-1 '(->> "input" ((fn [x y] (cond (= ::undef x) <thread-last> (= ::undef y) <thread-first>)) ::undef))) ((fn [x y] (cond (= ::undef x) <thread-last> (= ::undef y) <thread-first>)) ::undef "input") If we’re not inside any thread first/last macro, then no substitution will happen and our function will just be called with a single ::undef argument. We handle this by providing an additional arity: ((fn ([_] <normal>) ([x y] (cond (= ::undef x) <thread-last> (= ::undef y) <thread-first>))) ::undef) And boom: user=> #p (- 10) #p (- 10) -10 user=> (-> 1 inc inc #p (- 10) inc inc) #p (- 10) -7 user=> (->> 1 inc inc #p (- 10) inc inc) #p (- 10) 7 #p was already very good. Now it’s unstoppable. You can get it as part of Clojure+.

2 months ago 14 votes
Talk: Clojure workflow with Sublime Text @ SciCloj

A deep overview of Clojure Sublimed, Socket REPL, Sublime Executor, custom color scheme, clj-reload and Clojure+. We discuss many usability choices, implementation details, and broader observations and insights regarding Clojure editors and tooling in general.

3 months ago 34 votes

More in programming

The 6 Hours of Lex

When I drive the 24 Hours of Le Mans, I spend a total of about 6-9 hours in the car, divided into stints of roughly two hours at a time. It's intense. But talking with Lex Fridman in Austin on his podcast? Over six hours straight! We only interrupted the session for five minutes total to take three bathroom breaks. All that endurance training has clearly paid off! But the magic of a good conversation, like the magic of driving at Le Mans, is that time flies by. Those six hours felt more like sixty minutes. This is what flow does: it compresses the moment. Besides, we had plenty to talk about. Lex prepares like no other podcast I've ever been on. Pages and pages of notes. Deep questions, endless attention for tangents. We covered the beauty of Ruby for half an hour alone! But also the future of AI, small teams, why we left the cloud, Elon Musk, fatherhood, money and happiness, and a million other topics (which Lex mercifully timestamps, so listeners without six hours to spare can hop around). It was a privilege to appear. If you're interested, the conversation is on YouTube, on Spotify, on X, and as a regular podcast.

7 hours ago 3 votes
Measurement and Numbers

Here’s Jony Ive talking to Patrick Collison about measurement and numbers: People generally want to talk about product attributes that you can measure easily with a number…schedule, costs, speed, weight, anything where you can generally agree that six is a bigger number than two He says he used to get mad at how often people around him focused on the numbers of the work over other attributes of the work. But after giving it more thought, he now has a more generous interpretation of why we do this: because we want relate to each other, understand each other, and be inclusive of one another. There are many things we can’t agree on, but it’s likely we can agree that six is bigger than two. And so in this capacity, numbers become a tool for communicating with each other, albeit a kind of least common denominator — e.g. “I don’t agree with you at all, but I can’t argue that 134 is bigger than 87.” This is conducive to a culture where we spend all our time talking about attributes we can easily measure (because then we can easily communicate and work together) and results in a belief that the only things that matter are those which can be measured. People will give lip service to that not being the case, e.g. “We know there are things that can’t be measured that are important.” But the reality ends up being: only that which can be assigned a number gets managed, and that which gets managed is imbued with importance because it is allotted our time, attention, and care. This reminds me of the story of the judgement of King Solomon, an archetypal story found in cultures around the world. Here’s the story as summarized on Wikipedia: Solomon ruled between two women who both claimed to be the mother of a child. Solomon ordered the baby be cut in half, with each woman to receive one half. The first woman accepted the compromise as fair, but the second begged Solomon to give the baby to her rival, preferring the baby to live, even without her. Solomon ordered the baby given to the second woman, as her love was selfless, as opposed to the first woman's selfish disregard for the baby's actual well-being In an attempt to resolve the friction between two individuals, an appeal was made to numbers as an arbiter. We can’t agree on who the mother is, so let’s make it a numbers problem. Reduce the baby to a number and we can agree! But that doesn’t work very well, does it? I think there is a level of existence where measurement and numbers are a sound guide, where two and two make four and two halves make a whole. But, as humans, there is another level of existence where mathematical propositions don’t translate. A baby is not a quantity. A baby is an entity. Take a whole baby and divide it up by a sword and you do not half two halves of a baby. I am not a number. I’m an individual. Indivisible. What does this all have to do with software? Software is for us as humans, as individuals, and because of that I believe there is an aspect of its nature where metrics can’t take you.cIn fact, not only will numbers not guide you, they may actually misguide you. I think Robin Rendle articulated this well in his piece “Trust the vibes”: [numbers] are not representative of human experience or human behavior and can’t tell you anything about beauty or harmony or how to be funny or what to do next and then how to do it. Wisdom is knowing when to use numbers and when to use something else. Email · Mastodon · Bluesky

6 hours ago 2 votes
New Edna feature: multiple notes

I started working on Edna several months ago and I’ve implemented lots of functionality. Edna is a note taking application with super powers. I figured I’ll make a series of posts about all the features I’ve added in last few months. The first is multiple notes. By default we start with 3 notes: scratch inbox daily journal Here’s a note switcher (Ctrl + K): From note switcher you can: quickly find a note by partial name open selected note with Enter or mouse click create new note: enter fully unique note name and Enter or Ctrl + Enter if it partially matches existing note. I learned this trick from Notational Velocity delete note with Ctrl + Delete archive notes with icon on the right star / un-star (add to favorites, remove from favorites) by clicking star icon on the left assign quick access shortcut Alt + <n> You can also rename notes: context menu (right click mouse) and This note / Rename Rename current note in command palette (Ctrl + Shift + K) Use context menu This note sub-menu for note-related commands. Note: I use Windows keyboard bindings. For Mac equivalent, visit https://edna.arslexis.io/help#keyboard-shortcuts

5 days ago 8 votes