More from tonsky.me
Чем Datomic отличается от других баз данных и почему иногда остутствие оптимизатора лучше, чем его присутствие
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?)
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+.
More in programming
In the early 1990s, three companies pioneered online transactions, facing challenges of security and user accessibility. They are hardly known today. The post Three attempts at making payments secure appeared first on The History of the Web.
Welcome to my ongoing series on x86-64 assembly programming, designed for programmers who want to peel back the abstraction and understand how code really runs at the machine level.
According to Statcounter, Linux has claimed 5% market share of desktop computing in the US. That's double of where it was just three years ago! Really impressive. Windows is still dominant at 63%, and Apple sit at 26%. But for the latter, it's quite a drop from their peak of 33% in June 2023. These are just browser stats, though (even if it's backed up by directionally-similar numbers from Cloudflare). There's undoubtedly some variability in the numbers, by the season, and by what lives in the relatively large 4% mystery box of "other". But there's no denying that Linux is trending in the right direction in the US. As a Dane, though, I find it sad that Denmark is once again a laggard when it comes to adoption. Windows is even more dominant there at almost 70% (with Apple at 15%). Linux is just under 2%. Interestingly, though, ChromeOS, which is basically a locked-down Linux distribution, is at almost 5%. I guess I really shouldn't be disappointed because this is how it always was. It was a big reason why I moved to the US back in 2005. When Ruby on Rails was taking off, it was in America first and foremost. Danish companies were too conservative, too complacent, too married to Microsoft to really pay attention. There are early indications that a willingness to change this laggard mentality might be sprouting, but we've yet to see any evidence that a shift has actually taken hold yet. It's hard to change culture! So while the Danes continue to fiddle, the Americans continue to push forward. Linux is on the up and up!
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