Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
2
Any person who has used a computer in the past ten years knows that doing meaningless tasks is just part of the experience. Millions of people create accounts, confirm emails, dismiss notifications, solve captchas, reject cookies, and accept terms and conditions—not because they particularly want to or even need to. They do it because that’s what the computer told them to do. Like it or not, we are already serving the machines. Well, now there is a new way to serve our silicon overlords. LLMs started to have opinions on how your API should look, and since 90% of all code will be written by AI comes September, we have no choice but to oblige. You might’ve heard a story of Soundslice adding a feature because ChatGPT kept telling people it exists. We see the same at Instant: for example, we used tx.update for both inserting and updating entities, but LLMs kept writing tx.create instead. Guess what: we now have tx.create, too. Is it good or is it bad? It definitely feels strange. In a...
21 hours 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 15 votes

More in programming

A Programmer’s Guide to x86-64 Assembly (Series Overview)

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.

16 hours ago 4 votes
Three attempts at making payments secure

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.

10 hours ago 3 votes
Understanding Registers and Data Movement in x86-64 Assembly

A hands-on guide to general-purpose registers and data movement in x86-64

8 hours ago 3 votes
Linux crosses magic market share threshold in US

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!

3 hours ago 2 votes