Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
7
Чем Datomic отличается от других баз данных и почему иногда остутствие оптимизатора лучше, чем его присутствие
6 days 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

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 5 votes
Logo:

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

a month ago 18 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 13 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

Computers Are a Feeling

Exploring diagram.website, I came across The Computer is a Feeling by Tim Hwang and Omar Rizwan: the modern internet exerts a tyranny over our imagination. The internet and its commercial power has sculpted the computer-device. It's become the terrain of flat, uniform, common platforms and protocols, not eccentric, local, idiosyncratic ones. Before computers were connected together, they were primarily personal. Once connected, they became primarily social. The purpose of the computer shifted to become social over personal. The triumph of the internet has also impoverished our sense of computers as a tool for private exploration rather than public expression. The pre-network computer has no utility except as a kind of personal notebook, the post-network computer demotes this to a secondary purpose. Smartphones are indisputably the personal computer. And yet, while being so intimately personal, they’re also the largest distribution of behavior-modification devices the world has ever seen. We all willing carry around in our pockets a device whose content is largely designed to modify our behavior and extract our time and money. Making “computer” mean computer-feelings and not computer-devices shifts the boundaries of what is captured by the word. It removes a great many things – smartphones, language models, “social” “media” – from the domain of the computational. It also welcomes a great many things – notebooks, papercraft, diary, kitchen – back into the domain of the computational. I love the feeling of a personal computer, one whose purpose primarily resides in the domain of the individual and secondarily supports the social. It’s part of what I love about the some of the ideas embedded in local-first, which start from the principle of owning and prioritizing what you do on your computer first and foremost, and then secondarily syncing that to other computers for the use of others. Email · Mastodon · Bluesky

2 days ago 4 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

2 days ago 4 votes
Thoughts on Motivation and My 40-Year Career

I’ve never published an essay quite like this. I’ve written about my life before, reams of stuff actually, because that’s how I process what I think, but never for public consumption. I’ve been pushing myself to write more lately because my co-authors and I have a whole fucking book to write between now and October. […]

3 days ago 10 votes
Single-Use Disposable Applications

As search gets worse and “working code” gets cheaper, apps get easier to make from scratch than to find.

3 days ago 8 votes