Forgive me if this is an ignorant question, but does your use of the Mainline DHT mean that Bittorrent clients will be responding to P2P address lookups from Iroh?
HN user
weavejester
[ my public key: https://keybase.io/weavejester; my proof: https://keybase.io/weavejester/sigs/Fd7Z76Jpv6ca82C3pjIo0dv7uHAa7VQUKM68qINoCMk ]
Yes, a company needs to extract more value than it pays its employees, if only to cover its other costs. The problem is when employees are significantly underpaid compared to what they produce.
Negotiation clearly doesn't work in the general case, otherwise we wouldn't have billionaires. There's too much of a power difference between an employer and employee, and companies have a clear incentive to keep it that way.
There's an argument that if someone agrees to a bad deal, that's their own fault. Where I think it becomes unethical is where there's a significant power imbalance that disadvantages one side.
Suppose I buy a painting from a flea market for $100, get it evaluated by a specialist, and then discover it's actually worth $100,000. In this example I have no inherent advantage over the seller; neither of us knew the value of the painting at the time it was sold.
Now suppose a famous TV antique dealer stumbled across that painting instead, and immediately realizes its true value. The seller recognizes the dealer, and the antique dealer offers to buy the painting for $25. The seller, trusting the antique dealer's judgement, agrees to the discount.
Would you say in both examples everyone acted ethically? This is a genuine question, as I can certainly see the argument that using the assets you possess to secure yourself the best deal possible is just business, and yet I would personally see the antique dealer in the second example as being exploitative.
When it comes to companies there's a similar disparity in power. An employee requires money to live, while someone founding or investing in a company often has enough of a financial safety net that they won't starve if the venture fails. Equally, any would-be billionaire is explicitly looking for employees who generate vastly more value than their cost. You don't get rich by paying people what they're worth; you get rich by underpaying them and pocketing the difference.
The other problem, and one you've touched on, is how do we assess the value of an individual employee? This is obviously not easy, and businesses also have no incentive to work it out or reveal that information to their employees even if they knew. On the contrary it benefits employers to keep their employees as much in the dark as possible.
Aside from the ethical problems there's a practical one. The very existence of billionaires implies that a significant number of people are undervaluing their work. It's a pricing problem that the market isn't solving, and is only getting worse.
Not everyone can afford to take the financial risk of being a founder, and not every business type can be started with low initial capital.
That's not quite what I'm saying. You may very well have been paid fairly for each job you've taken, assuming that the value you generated for the business was not substantially higher than your salary.
But hiring people who are compensated fairly does not make someone a billionaire. If you generate $300,000 of value per year and I pay you $200,000, then I'm only making $100,000 profit off your work. I could hire more employees, but value does not scale linearly indefinitely. Doubling my number of employees does not guarantee I double my profits.
No, if I want to become a billionaire within my lifetime, I need an asset that generates far more money than it costs to buy and maintain it. In other words, I need employees who will generate millions for every thousand I pay them.
Now you might well argue that I'm taking a risk. How do I know if an asset or an employee or a team of employees is undervalued? Not every bet is going to pay dividends. However, while this is true, I don't think this makes it ethical. If I'm a venture capitalist looking to make it rich (or richer), the fact that I'm taking a risk doesn't change the fact that ultimately I'm looking for people who I can pay far less than they're worth.
How many businesses are there that are worth at least $1 billion and employ no-one but the founders?
When people say that it's not possible to earn a billion dollars, they're talking about the discrepancy between the wealth gained by those employed by the company versus the shareholders of the company. For example, when WhatsApp was sold to Meta for $19 billion, how many of WhatsApp's 55 employees walked away with hundreds of millions of dollars?
The fundamental problem is that it's possible for an employee to generate a hundreds of millions of value for a business, and yet be compensated for a vanishingly small fraction of that. Even if the employees agreed to a particular salary, is it ethical to pay them so little in comparison to the worth they generate, or is it exploitative?
Most, if not all billionaires, reach that status by paying people far less than the value they generate. If you want to become a billionaire, you need to find people who are willing to be paid thousands or tens of thousands of times less than they're worth. You need employees who will generate you $100 million in exchange for being given $100 thousand.
You effectively have to keep the types of all the things involved in your head and/or trace them to ensure that you don't run into a crash.
You make this sound difficult, but in practice type errors are rare in Clojure and generally caught in the REPL or by tests, since the moment you go down a branch with a type error an exception is thrown.
Contrast this to errors caused via mutable state, which are usually far harder to track down, because the failure condition is more specific.
This is trivial in TypeScript.
In the example you give you're omitting assoc entirely, which defeats the point. I'm using assoc as a minimal example, but the same principle applies to more complex functions, so replacing assoc with the equivalent expression doesn't tell us whether or not we can effectively type a function that deals with maps.
So lets try doing this properly. At minimum we need something like this:
type Assoc<M extends object, K extends string, V> =
Omit<M, K> & Record<K, V>;
function assoc<M extends object, K extends string, V>(
m: M, k: K, v: V): Assoc<M, K, V> {
return { ...m, [k]: v } as Assoc<M, K, V>;
}
(Note that we need to perform an explicit cast in order to inform TypeScript of the type of the key.)However, this produces some rather messy types consisting of nested Assocs. In order to get back to something a human can read, we can use an additional Simplify type to force the type system to reduce it back down into an typed object:
type Simplify<T> = {[K in keyof T]: T[K]} & {};
type Assoc<M extends object, K extends string, V> =
Simplify<Omit<M, K> & Record<K, V>>;
function assoc<M extends object, K extends string, V>(
m: M, k: K, v: V): Assoc<M, K, V> {
return { ...m, [k]: v } as Assoc<M, K, V>;
}
(The empty `& {}` intersection forces normalization, providing a cleaner reported type.)We're still not done, though, as if we want the same type checking that a class has, we need to ensure that a key cannot be overwritten with a value of a differing type. So we'll type the value argument as well to ensure it matches the type of an existing value within the map:
type Simplify<T> = {[K in keyof T]: T[K]} & {};
type Assoc<M extends object, K extends string, V> =
Simplify<M & Record<K, V>>;
type AssocValue<M extends object, K extends string, V> =
K extends keyof M ? (V extends M[K] ? V : never) : V;
function assoc<M extends object, K extends string, V>(
m: M, k: K, v: AssocValue<M, K, V>): Assoc<M, K, V> {
return { ...m, [k]: v } as Assoc<M, K, V>;
}
So this is possible to type in TypeScript (to its credit), but is it "trivial"? And is this type signature significantly less complex than one might find in Haskell?I don't see an asymmetry in the abstraction. Both vectors and maps are associative structures - you can assign a key to a value - the only difference is that vectors have a more constrained keyspace (i.e. ordered, consecutive integers starting from zero).
But that wasn't really my point. Even if we limit `assoc` solely to maps it would still be difficult to type effectively.
For instance, suppose we have some code like:
(let [m* (assoc m :number 3)]
(:number m*))
We can see that the return type of this expression is obviously an integer, but what is the type of m*? How do we type m* such that (:number m*) can be inferred to be an integer by the compiler?Most statically typed languages sidestep this problem: instead of using an open data structure like a map, a closed structure like a record or class is used instead, and these structures must be explicitly typed by the user.
The problem with this approach is that now every record is specific and bespoke. You lose access to all the general-purpose functions that operate on generic data structures, and as records and classes are closed, you also lose the ability to extend them.
This is the ultimate problem with static type systems: you're trading capability for safety. If you're programming within a static type system, there are options that are simply not available or feasible to use.
I too agree that "until you get better" isn't a good take. To err is human, and even the most experienced developers make mistakes.
That said, you don't get static typing for free. As with many things it's a trade-off: you catch some errors at compile time in exchange for working within the confines of the type system. The ultimate hope is that the time you spend fiddling with types is going to be less than the time you spend debugging type errors.
There is nothing you can do with dynamic typing that you cannot do with a sufficiently powerful static type system - and it doesn't have to be something absurd like Haskell's. You basically just need structural typing and type inference and some type-level programming constructs.
Haskell doesn't have a complex type system for no reason; it's necessary to encompass everything it wishes to do, and even then it's not as flexible as a dynamically typed language.
For instance, how would you statically type Clojure's `assoc` function? It's not at all trivial if you want to retain the type information of the keys and values.
I'm not sure I agree. Certainly there are differences other than syntax, but that doesn't mean syntax is irrelevant. For instance, would Clojure programmers use maps as much if there was no syntax for map literals?
Syntax determines what parts of a language are within easy reach, and therefore affects how programmers use the language. Tools that a syntax make easy are used often; tools that syntax makes hard are used infrequently. This indirectly impacts how a piece of software is designed.
But syntax must necessarily include what it's representing, no? For instance, `{:a 1}` represents an immutable map in Clojure, in the same way that `42` represents an immutable integer in Java.
The programming language informs the design of the system. As I said in my earlier comment, an idiomatic Java codebase is going to be designed very differently to an idiomatic Clojure codebase, even if they both intend to solve the same problem.
To be clear, I'm not questioning your choice of runtime or language. I'm just curious why you think that "Programming language syntax scarcely matters", as to me that seems the same as saying "How a codebase is architectured and designed scarcely matters".
Programming language syntax scarcely matters. It does to some extent but we the programmers tend to over-romanticize it. The runtime and its properties are the much better thing to optimize for.
I'm not sure I understand this argument. Java and Clojure share a runtime, but an idiomatic Java codebase is going to have a very different architecture and design to an idiomatic Clojure codebase. Conversely, a codebase written in Go may end up looking very similar to a codebase written in Java, despite using different runtimes.
In the UK there's been a recent spate of nationalist flag flying. Given the artist and location, "blinded by nationalism" is the most likely intended meaning.
Hype around switching from Windows servers?
I'm surprised the article didn't also mention Rich Hickey's metric of complexity; that is, complexity being a measure of how interconnected code is.
Typically you're either deploying via a container, in which case there's no more overhead than any other container deployment, or you're deploying directly to some Linux machine, in which case all you need is a JVM - hardly an arcane ritual.
I'll add a note to the cljfmt README to tell people about these commands, as your experience shows that it might not be obvious to people that they likely already have access to cljfmt in Emacs as a result of using LSP or CIDER.
cljfmt is included with both Clojure-LSP and CIDER, so if you have either installed it should work out of the box.
With LSP mode the standard `lsp-format-region` and `lsp-format-buffer` commands should work, and on the CIDER side `cider-format-defun`, `cider-format-region` and `cider-format-buffer` should also invoke cljfmt.
Is that a common issue? I guess I'm having a hard time imagining a scenario that would (a) come up often and (b) be a pain to fix.
With the amount of fiction available to read, why give your money to authors who are bad people?
I skimmed it. It's a story about a time traveler warning his ancestor about the horrors of Islam taking over the world. It's pretty yikes.
They’re not the ones bearing the cost.
I'm not sure that's necessarily true... Customers have limited space for games; it's a lot easier to justify keeping a 23GB game around for occasional play than it is for a 154GB game, so they likely lost some small fraction of their playerbase they could have retained.
Posting a question on SO and having it answered is interacting with people. I'm unsure how you could interpret that any other way. And given that podcasts and YouTube were part of the answers, I think it's clear that passively listening to people counts as an interaction as well within the context of the question.
The Python question I'd say is more narrow, as it asks specifically about "new tools and technologies". What if I have a question about an tool I've been using for a while?
In any case, my point is not what market share Clojure actually has, but that there's reasonable doubt in using SO's developer survey as a basis for that answer. If a far smaller percentage of the Clojure community uses SO than is average for a language, then it's going to skew the results.
I'll see if I can run the experiment again with Codex, if not on the exact same project then a similar one. The advice I'm getting in the other comments is that Codex is more state of the art.
As a quick check I asked Codex to look over the existing source code, generated via Copilot using the GPT-5 agent. I asked it to consider ways of refactoring, and then to implement them. Obviously a fairer test would be to start from scratch, but that would require more effort on my part.
The refactor didn't break anything, which is actually pretty impressive, and there are some improvements. However if a human suggested this refactor I'd have a lot of notes. There's functions that are badly named or placed, a number of odd decisions, and it increases the code size by 40%. It certainly falls far short of what I'd consider a capable coder should be doing.
I'll try out Codex and see how that performs. Presumably I can just use OpenAI's Codex extension in VS Code?
I was using GitHub Copilot Pro with VS Code, and the agent was labelled "GPT-5". Is this a particularly poor version of the model?
I also briefly tried out some of the other paid-for models, but mostly worked with GPT-5.
"I’m not sure if anyone else feels this way, but with the introduction of generative AI, I don’t find coding fun anymore. It’s hard to motivate myself to code knowing that a model can do it much quicker. The joy of coding for me was literally the process of coding."
I experimented with GPT-5 recently and found its capabilities to be significantly inferior to that of a human, at least when it came to coding.
I was trying to give it an optimal environment, so I set it to work on a small JavaScript/HTML web application, and I divided the task into small steps, as I'd heard it did best under those circumstances.
I was impressed overall by how far the technology has come, but it produced a number of elementary errors, such as putting JavaScript outside the script tags. As the code grew, there was also no sense that it had a good idea of how to structure the codebase, even when I suggested it analyze and refactor.
So unless there are far more capable models out there, we're not at the stage where generative AI can match a human.
In general I find current model to have broad but shallow thinking. They can draw on many sources, which is extremely useful, but seem to have problems reasoning things through in depth.
All this is to say that I don't find the joy of coding to have gone at all. In fact, there's been a number of really thorny problems I've had to deal with recently that I'd love to have side-stepped, but due to the currently limitations of LLMs I had to solve them the old-fashioned way.
Stack Overflow is one of those sites that benefit from a network effect. If there are few users of a particular technology on it, people are less likely to get questions answered and therefore less likely to interact with it again.
That said, it's always worth checking the numbers, so I took a look at the 2024 State of Clojure Survey. Around 18% of those surveyed used Stack Overflow, while the 2024 Python Developers Survey had at least 43% of respondents using Stack Overflow.
Now, you might well say that even so Clojure is still a niche language - and I agree. But it may be the case that instead of a 1.3% share, Clojure has a 3% share - if we assume that the Python community's usage numbers are more typical.