Read the latest posts from

from quiet optimism

NIMYBY-ism cannot be combated on moral/ethical grounds; it's grounded in rational and logical argument. However, arguments around NIMBY-ism tend to manifest themselves in an irrational and post-hoc fashion. Arguments range from not enough parking, too much traffic, worries about “undesirables”, and a multitude of other excuses. In reality though, NIMBY's are not incentivized to allow new housing developments.

Incentivizing The Wrong Behavior

Homeowners/Landowners are financially incentivized to block and stop any development that could hurt the value of their property. Let's examine an example: Affordable Housing. Forget any actual definitions about what it means or program specifics; people are reminded of failed project housing of the 60's, 70's, and 80's. Not surprisingly, NIMBY's want to block affordable housing. They don't want a chance that low-income or subsidized housing could ruin the surrounding area. But forget the Affordable Housing boogey-man: homeowners are incentivized for there to be less housing, period.

Less Housing = More Money

Less housing stock means their assets are worth more money. Low supply + high demand = high asset prices. It's understandable that people want to watch their assets grow, but what other factors are at play here? Wage growth: “Over the last 40 years, wages for the vast majority of the U.S. workforce have grown slower than their potential and much slower than for those at the top.” [1] People are relying on asset growth to make up for lost wages. Unfortunately, for those who do not own, they have limited means for making up that difference.

Illiquid Markets

The real consequence of exercising power over developments (affordable housing or not), is that it's akin to cutting off your nose to spite your face. It creates a very illiquid housing market that prevents mobility for the US workforce to move into areas that are experiencing economic growth. It also forces homeowners to pay up for their next house, even if they get a high price for their current one! This causes more capital to be tied up into the housing market instead of flowing through the economy. The only people to truly benefit from this market are either: a) own multiple homes/properties that can liquidate and wait to buy at lower prices, b) people looking to retire and significantly downsize, or c) are able to move to a much lower cost of living region – potentially moving away from friends, family, job networks, and infrastructure, typically causing displacement and gentrification. There are large swathes of the US that are upset about Californian's moving somewhere cheaper: Texas, Nevada, Arizona, etc. They are pricing the locals out of the market. But whose fault is that? The Californian's? No. The real culprit: bad land/housing policy.

Incentivizing The Right Behavior

People tend to act rational given the system in which they operate. If a system is broken and focuses on short-termism, people act accordingly. If a system is scalable, equitable, and fair, people act accordingly. So, to fix NIMBY-ism, we must change the system by changing the incentives.

We must change incentives from people accruing wealth at the expensive of others (exclusive asset ownership) by introducing a tax on the value of land, while simultaneously reducing taxation on income, sales, and other types of regressive taxation. It kills two birds with one stone: No longer are landowners/homeowners going to grow their wealth through asset ownership, but rather effective deployment of capital and labor unburden by inefficient and unfair taxation. (See

Once the shackles of perverse financial incentives are broken, people's behavior will change. Zoning committees won't care to have power to approve or deny new housing if it's not a detriment to their wealth. In fact, quite the opposite. More people in an area increases business opportunity and wealth accumulation through more customers and more laborers. It also decreases the living costs, effectively increasing wages. YIMBY-ism stands no chance until we change the system of incentives. NIMBY's are going to fight tooth and nail to make sure their most valuable assets stay valuable, at the expense of everyone else.




from quiet optimism

The US residential real estate market is hot, real hot. US home prices are at all-time highs with the Case-Sheller US National Home Price Index over 20% higher than the 2006 peak [1]. The market is fueled by all-time low mortgage rates [2], low-supply of new homes [3], and COVID-19 driving shifts in the market where people are moving all around the country to try and adapt to our new way of life [4]. This should be great, right? Existing home owners should be happy they can get a great price if they want to sell. What's the problem?

What happens if you don't already have skin in the game? The rift between the have and have nots is growing even larger. Entire generations of home buyers are being priced out of the market even at all-time low mortgage rates [5]. People are going to be permanently closed off from affordable housing. What can be done where housing is just simply unaffordable for a lot of people? There are two potential forces that I believe can help alleviate market pressure: bitcoin and land value taxation.


The world is starting to take notice of bitcoin like never before. Publicly traded companies are starting to add it to their balance sheets as treasury reserves [6]. Family offices are starting to diversify their portfolios and hedge funds want exposure [7]. Retail interest in bitcoin has never been higher [8]. But how does this impact the US residential real estate market? We should consider that in the near future, investors in real estate are going to start to diversify their portfolios and move assets into bitcoin away from real estate. Why would they do this?

Cost of Carry – Real Estate

The cost of carry refers to costs associated with the carrying value of an investment [9]. These costs can include financial costs, interest on loans, storage costs, taxes, etc. The cost of carry to buy and hold residential property can be expensive. Consider ever increasing property taxes [10], mortgage interest, broker fees, insurance, assessments, and maintenance. All of these costs eat away at your investment over time. Cash buyers are only saved from mortgage interest, but everything else still applies. The SALT deduction cap that was passed in 2017 adds more salt to the wound. Owners of real estate in high tax states used to be able to deduct property taxes from their federal tax bill, but now they are capped at $10,000 [11].

Cost of Carry – Bitcoin

The cost of carry on bitcoin is basically zero. Investors in bitcoin can buy bitcoin and self-custody for a one-time fixed cost. For example, buying a hardware wallet for $120 [12] and keeping your private keys. There are also more serious custodial solutions that charge some small percentage based on assets under management. Coinbase custody quotes 50bps annualized [13].

Growth Potential

Bitcoin has a much higher growth potential over real estate in the long-term. There are only 21 million coins that will ever exist and theoretically less than that according to some research [14]. While there is a fixed supply of land on earth, the housing supply is more elastic. It can change drastically over time. Zoning laws also have a big impact on the supply of housing. Take for example, many cities around the US are up zoning, or planning to, which allows for more dense residential development [15]. Why would you want to invest in an asset over the long term where it's value can be eroded due to loosening zoning laws and new development? This can't happen with bitcoin.

The bitcoin market is also quite new and small relative to other classes. There is still a lot of upside growth, where it seems that real estate is a bit too expensive [16].

Tax Policy – Land Value Taxation

A few years ago, I wrote a post titled the The Least Bad Tax. This post discussed the nuance between existing property taxes (land + improvements) and tax on the unimproved value of land. The American political economist Henry George wrote about it famously in his magnum opus, Progress and Poverty. The main objective of a land value tax is to curb the speculation of land prices by taxing the unimproved value of land. Land price speculation is where buyers buy land in hopes to make a profit. Almost all Americans (and foreign buyers) consider US real estate to be a great investment. In fact, buyers think the US real estate is such a good investment that they'll take out increasing levels of debt to fuel their speculation. Unfortunately, this has a massive downside: housing affordability. If a land value tax were to be introduced, the cost of carry of real estate ownership would increase, decreasing the returns on investment over time. In effect, a downward spiral of real estate prices would occur, encouraging investors to seek investment elsewhere.


The combination of investors seeking a store of value through bitcoin instead of US real estate and the introduction of a tax on the unimproved value of land is the one-two punch that the US needs to create affordable housing organically. As investors diversify their portfolios into bitcoin away from real estate over time, we'll start to see a decline in US housing prices. Then, perhaps, we can start to see the rift between the have vs. have nots disappear.


[1] December 2020 234.395 vs. August 2006 184.609 [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16]


from quiet optimism

The new release of the coinbase-pro haskell client is feature complete! Check it out on github: or hackage:

The main objective of this project was to provide a nice, clean example of what a client side integration library should look like using servant-client. I've been fairly happy with the results. I think there is more opportunity to remove some boiler-plate code (some hand-rolled ToJSON/FromJSON instances), but I utilize the TemplateHaskell language extension as much as possible.


I designed the library with the end user in mind. The objective was to allow the user to start doing REST queries or websocket streaming with the fewest lines of code possible. I split up the codebase into three distinct sections:

  1. Streaming
  2. Unauthenticated API
  3. Authenticated API

I split up the unauthenticated and authenticated APIs, because the auth APIs needed a bit more plumbing to include the submission of the proper credentials and signatures that coinbase-pro requires.


Here is an example of running a full order book fed for BTC-USD:

main :: IO ()
main = do
    msgs <- subscribeToFeed [ProductId "BTC-USD"] [Ticker] Nothing
    forever $ msgs >>= print

Unauthenticated Requests

Here is an example of doing an unauthenticated request that gets you the latest trades for a product:

run Sandbox (trades (ProductId "BTC-USD")) >>= print

Authenticated Requests

Authenticated requests presented more of a challenge than streaming and unauthenticated request due to the necessity to always have some credentials in-scope. I solved this problem using a ReaderT that allows the state to be passed along in a greater monad stack:

newtype CBAuthT m a = CBAuthT { unCbAuth :: ReaderT CoinbaseProCredentials m a }
    deriving (Functor, Applicative, Monad, MonadIO, MonadTrans)

A runCbAuthT function is also defined that allows the user to specify CoinbaseProCredentials and an Environment. Users can then sequence authenticated requests as they see fit within the CBAuthT and ClientM monads. ClientM is required for servant-client.

runCbAuthT :: Runner a -> CoinbaseProCredentials -> CBAuthT ClientM a -> IO a
runCbAuthT runEnv cpc = runEnv . flip runReaderT cpc . unCbAuth

Here is an example of doing an authenticated request to get a list of fills for a given product:

runCbAuthT (run Sandbox) cpc $ do
    fills (Just btcusd) Nothing >>= liftIO . print
    accessKey  = CBAccessKey "<access-key>"
    secretKey  = CBSecretKey "<secret-key>"
    passphrase = CBAccessPassphrase "<passphrase>"
    cpc        = CoinbaseProCredentials accessKey secretKey passphrase


Coinbase-Pro has a sandbox environment for users to test API integration. Thus the Environment sum type was created:

data Environment = Production | Sandbox

Now, whenever a user wants to make a request, they can just specify which environment they want to run in via the run function:

-- | Runs a coinbase pro HTTPS request and returns the result `a`
-- > run Production products >>= print
run :: Environment -> ClientM a -> IO a
run env f = do
    mgr <- newManager tlsManagerSettings
    runWithManager mgr env f

Looking Ahead

I want to take some time and see if I can generate swagger docs from my client API. Considering the API types for the client and the server side are the same, I should be able to. If that's the case, my swagger docs might be a better source of documentation than the actual coinbase-pro docs themselves.

I must admit, even though API integration can get tedious, I enjoyed the work I've done here. I hope to continue tinkering and making some of the datatypes more robust and adding more documentation and testing. It's quite clear that servant is a very powerful library and worth the additional complexity required to use it. I hope this library can inspire others to create clean and simple REST/Streaming APIs with servant and servant-client. Thanks for reading!


from quiet optimism

Upgrading to a newer version of GHC is relatively painless when using stack to maintain the Haskell toolchain for a Haskell package. The workflow looks generally like this:

1) Change the resolver 2) Bump dependency version numbers 3) Make code modifications to account for breaking API changes (if any)

I'm going to demonstrate by upgrading the coinbase-pro package that I maintain.

Changing the resolver

First step is to change the resolver parameter in the project's stack.yaml. The latest lts for GHC 8.8.3 is lts-16.7, so that's what the new resolver should be:


resolver: lts-16.7

  "$locals": -Wall

- .

  - unagi-streams-0.2.6
  - unagi-chan-

system-ghc: false

extra-include-dirs: ["/usr/local/opt/openssl/include"]
extra-lib-dirs: ["/usr/local/opt/openssl/lib"]

Now I can just stack build and have stack tell me what package dependencies I need to change. Here is the stack output I get after changing the resolver:

Error: While constructing the build plan, the following exceptions were encountered:

In the dependencies for coinbase-pro-
    network- from stack configuration does not match >=2.6 && <2.9  (latest matching version is
    time-1.9.3 from stack configuration does not match ==1.8.*  (latest matching version is
needed since coinbase-pro is a build target.

Some different approaches to resolving this:

  * Set 'allow-newer: true' in /home/mdunn/.stack/config.yaml to ignore all version constraints and build anyway.

  * Recommended action: try adding the following to your extra-deps in /home/mdunn/Code/coinbase-pro/stack.yaml:

- network-,3011
- time-,5494

So stack is telling me that two packages can't be reconciled given the existing version bounds for the packages. Currently, I have the network and time package bounds set to:

  - network >= 2.6  && < 2.9
  - time >= 1.8  && < 1.9

This is problematic, considering the package versions that come packaged with lts-16.7 for network and time are and 1.9.3 respectively. I'll need to increase the upper bounds of these two packages to allow for newer versions:

  - network >= 2.6  && < 3.2
  - time >= 1.8  && < 2

If you'll notice though, I'm attempting to upgrade the network library to a new major release (2.x vs 3.x), so I'll expect to have to make some code changes (and testing) to be sure that everything still works properly with the new network package. As for time, I can be sure that not much as changed considering it is just a minor version upgrade – but you can never be too careful!

Fix Compilation

Attempting to stack build now results in the following error:

/home/mdunn/Code/coinbase-pro/src/lib/CoinbasePro/Environment.hs:11:43: error:
    Module ‘Network.Socket.Internal’ does not export ‘PortNumber’
11 | import           Network.Socket.Internal (PortNumber)
   |                                           ^^^^^^^^^^

Ok, interesting, so it doesn't look like the network library doesn't expose PortNumber via Network.Socket.Internal. The latest docs show that PortNumber is just exposed through Network.Socket. It was probably a mistake to import Network.Socket.Internal in the first place; usually importing from modules with Internal namespace is a red flag!

The new import is as such:

import           Network.Socket (HostName, PortNumber)

Now I can finish my build.

Wrapping Up

After upgrading to the new lts, I'll increment the minor version of the package and push it up to github and make a new release! As you can see, using stack to help guide you through managing dependencies is a fairly straight-forward process.

Here is the changeset that was made in this post.


Here is the newest changeset after I realized that I migrated the code incorrectly the first time.


from quiet optimism

After discussion with some of the community over at r/AdminCraft, I decided to take a stab at switching my FabricMC server over to PaperMC and run the DynMap plugin. It ended up being a fairly easy switch. It should be noted that I loaded in a world that I’ve been playing since 1.14, so new chunk generation was at a minimum.

Run Flags

I’m using the standard aikars flags, setting Xms and Xmx to 6G, which is just under the 8G capacity for the Raspberry Pi 4 8GB. This leaves some room for the OS and any other miscellaneous services I have running.

/usr/bin/java -Xms6G -Xmx6G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -Dusing.aikars.flags= -jar server.jar nogui

DynMap Rendering

I was able to do a full render, while having two simultaneous players move around the world, with very few complaints from the server except for the start:

Jul 09 20:30:42 ubuntu java[123761]: [20:30:42 WARN]: Can't keep up! Is the server overloaded? Running 48595ms or 971 ticks behind
Jul 09 20:31:26 ubuntu java[123761]: [20:31:26 WARN]: Can't keep up! Is the server overloaded? Running 14277ms or 285 ticks behind

During the full render, I was getting statistics around these (this many tiles rendered is at ~1 hour of full rendering):

Full render of map ‘flat’ of ‘world’ in progress - 18900
 tiles rendered (332.65 msec/tile, 293.30 msec per render)

After majority (if not all) of the map had had rendered, I switched to using /dynmap radiusrender.

Check out the current render of dynmap on my server:

Timings Report

Using the /timings report command, I was able to generate the following report after doing some nether fortress raiding with two concurrent players:

The largest percentage of time is spent on tickEntities. This isn’t surprising given how many villagers I have (plus animals). If I were to look into any optimization via plugins or otherwise, that would be my place to start.


Overall, I’d say that running PaperMC on a 8GB RasperryPi 4 is very suitable for a few players with little to no lag. I’m looking to see if I can get more players so I can do some more rigorous benchmarking!