Great article, love that you enumerated all the costs in buying a home. I don't like how renters romanticize home ownership and fail to understand how many costs are involved.
> You've probably heard someone say something to the effect of "renting is just throwing your money away". Don't believe it. It's a glib statement that simply isn't true.
No, the statement is completely true: 100% of your rent money goes to someone else, and you also don't get any asset to sell later on.
However, this statement doesn't exist in a vacuum. You need some place to live, and you have to compare the cost of renting to the cost of owning.
To give an example, the typical rent in Toronto is $1000~2000/month, and the typical home ownership cost (including principal, interest, taxes, and maintenance) is $2000~3000/mo. We can just pretend that both are around $2000/mo.
If owning is still $2000/mo but suddenly rent is $500/mo, then renting suddenly becomes a great deal - even though you are still literally "throwing money away". You can use that differential $1500/mo to invest in a savings account, stocks, etc.
And speaking of that, I realized that the biggest cost in owning a home isn't the mortgage (and you correctly pointed out that paying down the principal doesn't change your net worth). The biggest cost is the opportunity cost of the down payment, when you could have instead invested in the stock market at 7~10%/year.
Continuing with the Toronto example, if you bought a home for $500k with 20% down, then the counterfactual if you had continued renting is that the $100k chunk of money could've generated $7000~10000/year = $580~$830/mo, which is a substantial fraction of the $2000/mo rent.
Why don't we say this about other expenditures? Am I throwing my money away when I buy dinner and not a cow? Did I throw my money away by buying a carrot and not farmland? I don't think the statement is true or false, it's just meaningless.
You somehow missed the second part of that sentence: "No, the statement is completely true: 100% of your rent money goes to someone else, and you also don't get any asset to sell later on."
The oft-repeated statement that "renting is throwing your money" is an implicit contrast to owning a home, where the mortgage payment "builds equity" in your asset that can be sold later.
"Throwing money away" means you don't get to own something that can be sold for money later on. That's why we "throw money away" on gasoline, but not "throw money away" in a savings account.
The second part of my argument is that throwing money away isn't necessarily a bad thing, because the alternative (such as paying to own something) can end up being more expensive and being a worse deal financially.
No, I read that part of the sentence. I don't know why you'd assume I didn't. Should I claim you didn't even read my entire response?
I've never heard someone claim I am throwing away money when I buy something consumable except rent. It's a stupid statement that doesn't convey any useful information.
OMG...the phrase probably comes from some real estate agent. Truth is, 50 years ago investing in US real estate was such a good investment that if you could own you did. Today, so much "value" has been squeezed out of the housing market that owning is very very difficult. Most people under 40 who own, are now all in on an investment in both the housing and interest rate markets that could either crush them or be their best lifetime investment. Its very hard to argue at this point that making such bets is a good idea for most people. The worst part is that this was all unnecessarily engineered by people who believed Disney movies accurately portrayed public policy.
That’s not what that expression means though. Wikitionary has it as “To spend money foolishly or indiscriminately; to waste money without regard of the consequences.” Which sounds about right.
I'm not sure that your definition of "throwing money away" corresponds to the OP's.
OP uses that phrase to imply the (un)worthiness of spend. You're using it to mean that it doesn't build or maintain equity, which is true almost tautologically but wouldn't be very meaningful unless your audience doesn't understand what it means to rent something.
> lead to Dredd-like Mega Cities just taking over the entire continent
This is impossible. Look at even the densest cities today such as Hong Kong, with many 50-storey buildings packed closely. HK as a whole has maybe 25% land area allocated for buildings and the rest is forest and green space.
Or consider Tokyo - sure, it is a big sprawling metropolis and pretty much an uninterrupted patch of concrete. But the urban area does eventually end, and much of the land area of Japan is mountains, forests, farms, etc.
Java made opaque types possible from the very start by private and package-private constructors.
It's sad to see that many features regarding object-oriented programming and static typing are implemented worse in Python than Java. Various examples: __str__() vs. toString(); underscore vs. private; @staticmethod/@classmethod vs. static; generic types are so clunky in Python; types are not shown in the official Python standand library documentation; __init__() doesn't force you to call super() whereas it's mandatory in Java; @override (Python 3.12; year 2023) copying Java @Override (JDK 1.5; year 2004) very late; convention changing from duck typing (always available in Python) to structural typing (optional in Python, mandatory in Java).
I agree that these are implemented "worse" than Java but that's because Python wants these things to be optional! You can complain about it as much as you want but that just means it's the wrong language for you, if that's important to you.
(I'd like to note that I wrote a lot of code in Java and Python, and continue to use each language in its respective strong areas. This isn't meant as a drive-by attack of "Java r00lz, Python sux"; this is an experienced take.)
My real problem with the evolution of Python is that initially, the language and the community was positioned as anti-Java, anti-big-OOP-like-C++, and then it changed into the thing that it was against, but in a roundabout and suboptimal way. To me, the initial vibe of Python was, "write a 100-line script, don't worry about explicitly documenting types, don't worry about grand architecture, don't worry about creating custom classes, don't worry about encapsulation and public/private". I've been with Python since year 2007 in the 2.x days, and Java since 2002.
Initial examples: Why go through the ceremony of `public static void main(String[] args)` when Python just executes the script line by line at the top level? Oh wait, now you have things like `import` actually executing code instead of simply being a compile-time namespace convenience, and you need weird techniques like `if __name__ == "__main__"`. Why `System.out.println()` when `print()` is so much more concise? But now you're polluting the global namespace, and `print(file=sys.stderr)` isn't that elegant either.
Static typing in Python is the biggest hypocrisy ever. As I understood it, Python scripts were meant to be lightweight and free of the tyranny of enterprise OOP which was epitomized by Java. But people found out that keeping track of types in your head is laborious and error-prone, and getting a compiler to check {that the shape of your objects and function calls match} is a huge productivity boost. And so Python 3 enabled static type hints... which, like I said before, Java had from day zero. To make matters worse, static type hint features were introduced progressively over the years, leading to things getting deprecated from the `typing` module and moved to things like `T|None` and `list[T]` and `collections.abc`.
IIRC the old practice in Python was that you specified some kind of interface in prose or in code (e.g. `class IoStream: def read(); def close()`), but you didn't need to explicitly use that interface as a superclass; you can just duck-type your way around things. But this completely goes against static typing, so I'm pretty sure the new preferred way is to explicitly use abstract superclasses... just like Java did all along (and is mandatory).
I really don't think having top-level (module) variables and functions in Python is a good thing, especially because then they are duplicated as fields and methods in classes. In Java, fields and methods (whether static or instance) can only be placed in classes, and I think this particular straitjacket is a good thing.
> because Python wants these things to be optional
We can both agree that Python gives multiple ways to do things (e.g. no static type hints vs. static type hints). This flies in the face of:
Probably the most tragic example is the ways to build up strings in Python: `+` and str(), `%` operator, `str.format()`, f-string.
(To be fair, I have a laundry list of complaints about Java too, such as: .class files and the JVM being an intermediate layer that needs to be understood which is actually different from the Java source language, lack of in-place structs so `new Point[]` is very painful on the memory system, awkward string interpolation/formatting compared to Python's f-strings, very awkward JDBC compared to for example Python sqlite3 API, kinda clunky for web server programming, very awkward JSON handling, enterprisey libraries and APIs that are perfectly documented but are impossible to actually understand.)
Are you ever supposed to inherit from the protocol though(unless you’re defining another protocol)? One of the great things about protocols is that your class doesn’t even have to know about the protocol explicitly. What this code looks closer to doing (style wise) is an abstract base class
"To explicitly declare that a certain class implements a given protocol, it can be used as a regular base class. In this case a class could use default implementations of protocol members. Static analysis tools are expected to automatically detect that a class implements a given protocol. So while it’s possible to subclass a protocol explicitly, it’s not necessary to do so for the sake of type-checking."
> My real problem with the evolution of Python is that initially, the language and the community was positioned as anti-Java, anti-big-OOP-like-C++, and then it changed into the thing that it was against, but in a roundabout and suboptimal way. To me, the initial vibe of Python was, "write a 100-line script, don't worry about explicitly documenting types, don't worry about grand architecture, don't worry about creating custom classes, don't worry about encapsulation and public/private". I've been with Python since year 2007 in the 2.x days, and Java since 2002.
It really wasn't that anti-Java. Late 90s / early 2000s had a huge branch that was very dedicated to Java-style OOP. E.g. anything to do with Zope, and GvR himself worked on that at the time. Zope even has had its own ABC / interface system, specifically modeled after Java interfaces. stdlib logging is a reimplementation of log4j in Python and so on.
> Static typing in Python is the biggest hypocrisy ever
Yes, agreed. I used to work on a large python codebase and tried to add type hints where I could. The issue is that python was not the right tool for the job - except that switching to the right tool was a non-starter. So type hints were the best I could do.
It is indeed a significant undertaking. But... it is doable. I've worked on a code base that converted several functionalities to golang. It did take a lot of effort and quite a lot of planning.
> Why go through the ceremony of `public static void main(String[] args)` when Python just executes the script line by line at the top level? Oh wait, now you have things like `import` actually executing code instead of simply being a compile-time namespace convenience, and you need weird techniques like `if __name__ == "__main__"`.
I don't think this is a fair criticism. Python is a scripting language, it makes sense that the code is executed line by line at the top level. This is also how other programming languages from its time like Perl or Bash does it. Even newer scripting languages like Ruby does something similar.
> Why `System.out.println()` when `print()` is so much more concise? But now you're polluting the global namespace, and `print(file=sys.stderr)` isn't that elegant either.
Another criticism that I don't think it is fair. Lots of other languages "polutes" the global namespace. I actually can't think another language other than Java that doesn't. Python at least still allow you to manually `import builtins`, but Go for example AFAIK has no mechanism for you to reference builtins if you end up shadowing them.
Also I find `print(file=sys.stderr)` pretty much elegant, it works exactly how I would expect, it also means I can open a file and write to it using `print`.
> And so Python 3 enabled static type hints... which, like I said before, Java had from day zero.
Again, I don't think this is fair. I find Python 3 type-hints much more powerful than whatever type system Java has, especially because Python has Option types that actually make the type system useful (Java is infamous for its NullPointerException for a reason).
> Why go through the ceremony of `public static void main(String[] args)` when Python just executes the script line by line at the top level? Oh wait, now you have things like `import` actually executing code instead of simply being a compile-time namespace convenience, and you need weird techniques like `if __name__ == "__main__"`.
You'll note that even Java now recognises that "public static void main(String[] args)" was pointless ceremony.
> Python scripts were meant to be lightweight and free of the tyranny of enterprise OOP which was epitomized by Java. But people found out that keeping track of types in your head is laborious and error-prone, and getting a compiler to check {that the shape of your objects and function calls match} is a huge productivity boost. And so Python 3 enabled static type hints... which, like I said before, Java had from day zero.
Java didn't have "hints", it had mandatory types everywhere. Which, again, they've now recognised was a bad idea and gradually removed. Having a type system is a good idea, and maybe if Python had taken more inspiration from OCaml (or more people had used OCaml instead of either Java or Python) we'd all be better off, but the early-2000s-Java cure of mandatory types everywhere was worse than the disease.
> To make matters worse, static type hint features were introduced progressively over the years, leading to things getting deprecated from the `typing` module and moved to things like `T|None` and `list[T]` and `collections.abc`.
Which happens in any language that evolves over time. Java, despite having a type system built into the language from day 1, now has about ten different deprecated sets of nullness annotations with overlapping functionality.
> I really don't think having top-level (module) variables and functions in Python is a good thing, especially because then they are duplicated as fields and methods in classes. In Java, fields and methods (whether static or instance) can only be placed in classes, and I think this particular straitjacket is a good thing.
Nah. No other language has adopted Java's approach, for good reason. Functions and values are easier than classes and the language shouldn't force you to complicate your code when there's no need to.
> Probably the most tragic example is the ways to build up strings in Python: `+` and str(), `%` operator, `str.format()`, f-string.
It sucks, but what's the alternative? Either a language dies young or it lives long enough to have a bunch of deprecated crap in it.
> You'll note that even Java now recognises that "public static void main(String[] args)" was pointless ceremony.
There is still the part of the ceremony that actually mattered: having a single entrypoint instead of the option to litter side effects throughout the file and having those side effects execute automatically on import.
> It sucks, but what's the alternative?
3.0 was a big missed opportunity to kill a lot of the deprecated cruft.
Much older? Wikipedia says[^1][^2] java appeared in 1995 (started in 1991), while python appeared in 1991 (started in late 1980s). 4 years doesn't seem too far apart, considering both language are >30 years old now.
Not only that, but Python had the benefit of doing a very painful break in version 3 (2008), when they had the option to cleaned up almost anything they wanted.
(Some changes in Python 3 I can recall: bytes/str/unicode being the biggest one; fixing mutable variables in nested functions; changing some obscure behavior in class hierarchies and overload resolution; changing things like range() and map() to lazy evaluation.)
For better or for worse, Java has maintained very good (not perfect) compatibility throughout, even with painful changes like generics in 1.5, lambdas in 8, modules in 9, eventual removal of applets and SecurityManager, etc. This also contrasts with C#/.NET, which I think had some breaking changes over the decades.
I do think that a lot of people think that public roads are "free" and cost nothing to build and maintain. It is really hard to make people think about where the labor, materials, and funding come from.
Really this shows that the operators of 18-wheelers should be paying a lot more. Looks like diesel tax is 20-30% higher, not proportional to the thousands of times more damage the 4th power law implies. (A half-full 18 wheeler is about 40,000 pounds so, let's say 10x the weight of a passenger car).
If I extrapolate how much more road wear is caused by a bus and multiply by a EV fee we pay in NZ ($8 NZD / 100km), it would be cheaper to just zoom every passenger in their own Uber.
It's probably fake, but recently someone circulated a figure how much a $5 or $6 bus fare actually costs in Auckland - it's about $40 IIRC.
On flip side I do love rapid bus innovation in Auckland, when it works it works pretty well.
It's not flawed logic though. If, say, a mile of highway costs $1 million and it needs some expensive repaving/reconstruction every 20 years, who should bear the cost?
The current model is roughly that all of society shoulders the cost roughly equally per person, regardless of how much they use that road or how much they drive in general. But clearly, some people derive more benefit from the road than others. The guy who doesn't drive derives 0 units of benefit. The gal who drives on it once a year derives 1 unit of benefit. The daily commuter gets 100 units of benefit. For the trucker moving $10M of goods a year on that road, their company gets 3000 units of benefit. So in a sense, the people who drive less are subsidizing the people who drive more - kind of like going to a fixed-price buffet dinner (people who eat more are subsidized by people who eat less).
Targeting more of the cost burden on heavy goods vehicles isn't an issue in my opinion. The thing is, that highway costs $1M no matter what. The only thing we can decide as a society is how to split that cost among the people. In the current way, I think the truck is underpriced and is doing more than its fair share of damage. If we change the prices so that car drivers pay less (not zero) and the truck driver pays more, that's okay. The truck's costs get passed onto consumer, such that people who buy more goods pay more road tax - exactly as intended.
Taking a step back, I think a lot of (not all) problems in society are a result of mispricing - often for political, special-interest, and/or "feel-good" reasons. When people pay less than the true cost, they over-consume. When people pay more than the true cost, they under-consume.
When it comes to any good or service, there are only two choices: the user pays, or other people pay. The status quo is that drivers pay a lot for roads through gasoline taxes and vehicle registration fees, but the rest of society (including non-drivers) pay through income taxes, sales taxes, and property taxes. Moreover, a lot of taxes paid for road construction/maintenance are not proportional to how much you drive; a driver doing double the miles in a year is paying less than twice of another driver.
Please explain your ideal scenario of who pays for roads. And if your answer is "someone else" (e.g. "taxes", "government", "corporations", "billionaires"), further explain why "someone else" can't use the same argument to make you pay.
> but the rest of society (including non-drivers) pay through income taxes, sales taxes, and property taxes.
The rest of society also pays for the transportation costs of consumer goods, which include diesel and gas taxes which end up funding roads. Every time you buy something that rode on a truck, a fraction of the price you pay is road taxes.
While bored in high school math class around the year 2005, I forced myself to learn the Greek alphabet. That very much came in handy in university, as Greek letters are frequently used for variables in computer science, mathematics, and physics.
reply