GeistHaus
log in · sign up

https://wa9ace.net/feed

rss
10 posts
Polling state
Status active
Last polled May 19, 2026 04:56 UTC
Next poll May 20, 2026 02:57 UTC
Poll interval 86400s
ETag "4e1df0fb6248176c13d7e0a78bd214c3-gzip"

Posts

Change
Uncategorizedphilosphy
We often solidify and even petrify our positions and thoughts, especially in the fields in which we are most experienced. We try to protect what is, because of the risk of what could or will be. Respectable elders are regarded because of their experience and ability to see past the immediate effects of a given
Show full content

We often solidify and even petrify our positions and thoughts, especially in the fields in which we are most experienced. We try to protect what is, because of the risk of what could or will be. Respectable elders are regarded because of their experience and ability to see past the immediate effects of a given change; whether that change be presented by an inexperienced child, adult, or nature herself. You might see change coming to your industry, like myself, and think we should enforce and codify ways to protect what we know to be correct and good, to prevent the corruption and damage of what is new.

I often find myself thinking “I know nothing, yet I know too much and never enough”. For someone to have experience is to be burdened by knowledge that is sometimes only relevant to exactly you and your circumstances, and other times larger audiences, up to the whole of humanity. Knowing which is which is hard. Coercing those without it to learn it from you without experiencing themselves is harder. Learning who to trust, whose experience is valuable, and whether or not it is applicable to them now, let alone in the futures unknown, is itself meta-experience that has to be learned on it’s own. In many cases people don’t even make it that far. All that leaves you with is information.

Recorded information is the forward progress of humanity for those who will read the experience of those who came before, and apply it. There’s so much information, too much, and again still not enough. Determining what is valuable information to the modern era, and what should be left to history is a job for a thousand humans living thousand year lifetimes. Which is a travesty given that each person does not get to live long enough to build a determination of what is useful and what is pulp. And so you’re left with a life, short, and perpetually inexperienced to timelines of humanity, to live your life to the degree you can, hopefully in a way that you enjoy.

This short essay is for me to re-read as I get older. At the time of writing I am 32. For future Caleb: Consider that we don’t have time to learn from our elders about the best and most correct way for every path traveled. Sometimes trails are taken back by nature through disuse; maybe because the road washed out up ahead. But if you never let anyone go down it again for centuries, you might miss that the washed out road became a cresting watershed worthy of simply visiting. Having historical context is laudable and important, but even you cannot learn all the variables of history to inform the origination of the present, or to predict the future. Let the youngins try their ideas to gain their own experience. You won’t be around long enough to stop them anyhow. All information is valuable, but not all value is in information.

https://wa9ace.net/?p=460
Extensions
const, let, and var. Why your linter is wrong.
Uncategorizedjavascriptsoftware developmentweb development
Tell me young padawan, have you ever needed to assign a variable inside an if block and realized “oh no! I can’t access this outside the block further down where I need it!”, then end up writing something like this? Of course, you tried to use const first like a good frontend dev, but realized,
Show full content

Tell me young padawan, have you ever needed to assign a variable inside an if block and realized “oh no! I can’t access this outside the block further down where I need it!”, then end up writing something like this?

let foo

if(condition) {
  foo = 'bar'
} else {
  foo = 'bbq'
}

doThingsWith(foo)

Of course, you tried to use const first like a good frontend dev, but realized, oh yeah, “constants” can’t be reassigned. You can mutate the objects they hold sure (because that’s totally how constants work everywhere else…), but you can’t reassign them! So you switch to defining a let, which will always start life out as null. What you may not know if you started doing JavaScript development within the past 5-6 years, is that there’s more than const and let. In fact, for the majority of JavaScript’s life, nothing was ever defined as const or let. There was only var. Most frontend linters of today try to disallow any usage of the supposedly dreaded var keyword, and maybe mostly for good reason, but there are still cases where it’s useful. I contend that the above code snippet which I’ve seen from junior, mid, and senior engineers alike is an artifact of being stockholm syndromed by their own tooling, beaten into submission, and conditioned by the overall frontend community to avoid var at all costs. Here, in my opinion, is the perfect counter example.

if(condition) {
  var foo = 'bar'
} else {
  var foo = 'bbq'
}

doThingsWith(foo)

There truly is no reason to disregard this here, and if using typescript you have avoided completely the requirement that the previous version be inferred or explicitly typed as null | string. So go forth and use var when the situation calls for it. There’s no reason not to.

https://caleb.tn/?p=413
Extensions
TypeScript React Beauty in the Eye of the Handler
Uncategorizedtypescriptweb development
Compare these two TypeScript components with their handlers. One uses function composition to produce handlers with required inline type signatures because typescript cannot infer their usage, and the other just requires repeating the same line a couple of times. Which one do you prefer? Or And my personal preference here, while I love TypeScript, is
Show full content

Compare these two TypeScript components with their handlers. One uses function composition to produce handlers with required inline type signatures because typescript cannot infer their usage, and the other just requires repeating the same line a couple of times. Which one do you prefer?

function HeightWidthComponent() {
  const [width, setWidth] = useState(512)
  const [height, setHeight] = useState(332)

  const handler = (
    e: React.ChangeEvent<HTMLInputElement>,
    fn: React.Dispatch<React.SetStateAction<number>>
  ) => fn(parseInt(e.currentTarget.value, 10))

  const widthHandler = (e: React.ChangeEvent<HTMLInputElement>) => handler(e, setWidth)
  const heightHandler = (e: React.ChangeEvent<HTMLInputElement>) => handler(e, setHeight)

  return (
    <div>
      <input type='number' name='width' min={320} defaultValue={512} onChange={widthHandler} step={1} />
      <input type="number" name="height" min={258} defaultValue={332} onChange={heightHandler} step={1} />
    </div>
  )
}

Or

function HeightWidthComponent() {
  const [width, setWidth] = useState(512)
  const [height, setHeight] = useState(332)

  const widthHandler = (e) => {
    setWidth(parseInt(e.currentTarget.value, 10))
  }

  const heightHandler = (e) => {
    setHeight(parseInt(e.currentTarget.value, 10))
  }

  return (
    <div>
      <input type='number' name='width' min={320} defaultValue={512} onChange={widthHandler} step={1} />
      <input type="number" name="height" min={258} defaultValue={332} onChange={heightHandler} step={1} />
    </div>
  )
}

And my personal preference here, while I love TypeScript, is this much simpler untyped JavaScript version of both.

function HeightWidthComponent() {
  const [width, setWidth] = useState(512)
  const [height, setHeight] = useState(332)

  const handler = (e, fn) => fn(parseInt(e.currentTarget.value, 10))

  const widthHandler = e => handler(e, setWidth)
  const heightHandler = e => handler(e, setHeight)

  return (
    <div>
      <input type='number' name='width' min={320} defaultValue={512} onChange={widthHandler} step={1} />
      <input type="number" name="height" min={258} defaultValue={332} onChange={heightHandler} step={1} />
    </div>
  )
}
https://caleb.tn/?p=409
Extensions
Roku’s Ultimatum: Surrender Jury Trial Rights or Lose Access to Your TVs
Uncategorizedsome bullshitwtf
Roku Community Thread My TV today after switching off an airplay stream decided it’s no longer mine. I’m no longer allowed to use it. I either accept my several hundred dollar tech is unusable, or accept these egregious terms. I’ve always been a big fan of dumb TVs with an Apple TV plugged in. That
Show full content

Roku Community Thread

My TV today after switching off an airplay stream decided it’s no longer mine. I’m no longer allowed to use it. I either accept my several hundred dollar tech is unusable, or accept these egregious terms. I’ve always been a big fan of dumb TVs with an Apple TV plugged in. That doesn’t matter for this case apparently. Even using this Roku as a host for HDMI devices is broken. What’s unacceptable is this rejection of the idea that IT IS MY GOT DAMN TELEVISION. There is no disagree button. I have the https://raw.githubusercontent.com/hkamran80/blocklists/main/smart-tv block list on my pi hole, but it could not save me from this. There you have it. A television that you OWN, is entirely defunct without giving up your right to sue, or following a likely onerous process to opt out of forced arbitration. The fact that it is legal to force waiving your rights under threat of disabling access to physical hardware you bought and paid for is a fucked up joke.

https://caleb.tn/?p=398
Extensions
Does a Flash Drive Weigh More Full?
Uncategorizedquestions
Hypothesis before research: Yes, as current is introduced into the flash drive the electrons from the external power source are stored in various configurations such that the total mass of the flash drive is increased, even if only by me (mass of an electron) times the number of electrons required to store final state. Deep
Show full content
Hypothesis before research:

Yes, as current is introduced into the flash drive the electrons from the external power source are stored in various configurations such that the total mass of the flash drive is increased, even if only by me (mass of an electron) times the number of electrons required to store final state.

Deep Dive How is digital information stored at a high level?

Let’s start by breaking down how information is stored inside a flash drive at various depths of hardware technicality, down to the physical processes. At a high level most probably understand information in computing is stored in bits that are flipped between 0 and 1. Flash memory does this through something called NAND memory chips. NAND, if you’re familiar with logic gates, is a NOT-AND gate. NAND memory is composed of multiple transistors, usually metal-oxide-semiconductor field-effect transistors or MOSTFETs. Given a NAND gate, output is low when both of its inputs are high, otherwise the output is high. Inputs are given to the NAND gate with voltages applied to the gate terminals of the input transistors.

How can a NAND gate hold information?

For our simplified flash drive we’re going to assume they’re simply Floating Gate Transistors made of a combination of NMOS (N-channel metal-oxide semiconductors) and PMOS (p-channel metal–oxide–semiconductor) MOSFET transistors. When a charge is “stored” on the floating gate, it affects the threshold voltage of the transistor allowing it to maintain a binary state. None of this is particularly interesting, noteworthy, or even necessarily correct for all types of flash memory, but we’re not trying to answer the question of most common types of transistors and gate types used to store memory because under the hood they usually use the same physical properties to “hold” the value, so let’s take a look at how that physical process occurs.

What are the constituent parts of a PMOS transistor?

PMOS transistors are made of

  • substrate material
  • gate material
  • gate insulator
  • source and drain contacts
  • interlayer dielectrics for insulating
  • metal interconnects for interfacing with the rest of the circuit

Of these, the important ones for our question are the substrate and gate. The gate material is usually made of a polycrystalline silicon as it can be easily doped to be n-type or p-type depending on the transistor type (we’re not going to discuss doping types here). In p-type polysilicon conductors there are empty electron orbits in the valence band. These orbits are created by the introduction of acceptor dopant atoms, like boron, which contain fewer valence electrons than the host silicon. These empty orbits allow electrons to move between the atoms easily. Now we’re getting somewhere. We know memory uses induced voltage differences to trigger certain states in the circuit to cause current to pass through various gates and store the value, but how does that part work?

Where do the electrons come from?

When the voltage is applied to the gate of a PMOS transistor, the p-type polysilicon gains a positive charge storing a 0, corresponding to the higher voltage attracting electrons to the polysilicon gate. Likewise, to store a 0, you drop the voltage lower to displace the region’s electrons. When proper voltage is applied an inversion layer forms in the substrate material. This inversion layer is made of mobile electrons that move into the polysilicon creating a conductive path between the source and drain. Electron movement between these two materials, through a quantum process called Fowler-Nordheim tunneling, decides the final stored state of the transistor. This makes sense as you can power off a flash drive and maintain the circuit state. They wouldn’t be very useful without it.

TL;DR: Answer

No, a flash drive does not contain more mass, as the entire mass required to store values already exists inside the flash drive. The state of the drive’s electron configuration is simply shuffled to exist in various positions after induced voltages cause electrons to accumulate on the gate material or recede back to the substrate.

More Questions: What about other information?

What about DNA? Biology is filled with information and we have mass, but we do lots of other things besides copying information to produce new cells. Maybe the real question isn’t about flash drive’s, but what is the mass of information in general? Sounds like another research post to me.

https://caleb.tn/?p=258
Extensions
D&D 5.1e SRD Published Under Creative Commons
Uncategorizeddnd
I am re-hosting a copy of it here because I’m so excited and it’s totally valid to do under the new license! https://www.dndbeyond.com/posts/1439-ogl-1-0a-creative-commons
Show full content

I am re-hosting a copy of it here because I’m so excited and it’s totally valid to do under the new license!

SRD5.1-CCBY4.0License.pdf

https://www.dndbeyond.com/posts/1439-ogl-1-0a-creative-commons

https://caleb.tn/?p=210
Extensions
One Person Making a Difference
Uncategorizedhumanity
Let’s imagine that you discover the largest contributor to soil and air pollution by a factor of 10 was actually tire wear shedding microrubber into the surrounding air and leeching into the ground. Society would need that to change. But how do you do it? This example is quite useful for the fact that it’s
Show full content

Let’s imagine that you discover the largest contributor to soil and air pollution by a factor of 10 was actually tire wear shedding microrubber into the surrounding air and leeching into the ground. Society would need that to change. But how do you do it?

This example is quite useful for the fact that it’s important that it changes, could have massive impact on sustaining our environment and improving the overall health of the people affected. But solving this issue could probably take your entire life and still only succeed in moving the needle a fraction of what’s necessary. You would be going up against tire manufacturers to produce, possibly lesser quality tires that shed less or have less improved traction leading in actuality to more deaths due to lesser quality tires.

But wait, other solutions could include rezoning various areas, and reducing the need for vehicles in the first place, and improving public transit. Now you’re not just going against tire manufacturers, you’re also fighting to reverse the tide of public opinion, at least in America, of public transport bad, independence and cars good. You’ve gone from, tire shed hurting our world and our children to fighting against the entire transport, oil, and auto industry.

The fact remains the fight must be fought. But by how many? Only you? There are so many other things to think about it would seem. Silence is violence people say. You’re not doing enough activism folks will say. There are other more important things that need your attention RIGHT NOW! Twitter and other social media would have you always believe you’re not doing enough if you’re not outraged by the most recent injustice. And the most recent injustice is probably incredibly awful and quite worthy of attention. But there’s the rub. Every thing cannot be worthy of attention all of the time even if it probably should be. The truth is societal change comes from people working in the background long and hard on things that might not impact you for 20 years if ever in your lifetime.

Collectively humanity has to work in disjointed bubbles by people that care about their corner of humanity and that is exactly how the world moves forward. Each person who cares, one person making a difference in their time. Lobbying your local city council to turn an old empty high school lot into a park, volunteering at your local food bank, getting parts of your town rezoned to reduce the need for vehicle traffic, and being active in your area of expertise in the world to be better is how one person makes a difference.

We are part of a whole and it is damaging for anyone to think one person doesn’t make a difference. Your difference is made among the effort of many. Take an active role in improving your local community and your field. It is worth its weight in gold more than all the social media grandstanding you could ever see.

http://caleb.tn/?p=1
Extensions
Implementing a Pipeline Operator in Ruby
Uncategorizedruby
Many programming languages have the concept of a pipeline operator that looks a little like this |>. If you’re not familiar with them you’ll more than likely at least be familiar with the pipe | operator in bash for sending data between commands. There’s even a proposal to add the pipeline operator to JS. But I like writing Ruby.
Show full content

Many programming languages have the concept of a pipeline operator that looks a little like this |>. If you’re not familiar with them you’ll more than likely at least be familiar with the pipe | operator in bash for sending data between commands. There’s even a proposal to add the pipeline operator to JS.

But I like writing Ruby. Ruby had a proposed pipeline operator and much was written about it including this great post by Brandon Weaver. The feature was shelved and will likely never be brought back up again, but…

Because we’re writing Ruby in the powerful land of everyone’s favorite language to write expressive DSLs I decided to implement my own and bastardize the bitwise OR operator for my own purposes.

By the end of this blog post we’ll have a working implementation that will allow this code to be valid.

string_sym = pipe(:foo) | Foo('bar') | Wat
puts string_sym

num = pipe(2.0) | Times(2) | Unwrap
puts num

a = pipe(Alphabet) | Reverse | Upcase
puts a

Now that may look silly, but that’s only because it is. So let’s get to the implementation.

Let’s start by not clobbering the bitwise OR operator outright and only use it on values we have deemed should be piped into something else.

Overriding The Pipe Operator
def pipe(input)
  input.define_singleton_method(:|) do |filter|
    pipe filter.call(input)
  end

  input
end

With this we’ll only redefine the pipeline operator on objects we call with pipe(). Let’s try it.

alphabet = ('a'..'z').to_a

def reverse(arr)
  arr.reverse
end

pipe(alphabet) | reverse

This is going to fail with 'reverse': wrong number of arguments (given 0, expected 1) (ArgumentError). We’re actually calling the reverse method before our pipeline manages to pass itself in as an argument. An easy way around this is to wrap the reverse part of the pipeline as a proc or convert it to one using method(:reverse).

alphabet = ('a'..'z').to_a

def reverse(arr)
  arr.reverse
end

pipe(alphabet) | method(:reverse)

will return a reversed array of the alphabet! We have our first working pipeline in Ruby! Now let’s start attempting to pipe some more types.

def times(i, n)
  n * i
end
pipe(2.0) | method(:times)

Immediate Values

Hmm, it seems can’t use define_singleton_method on a Float. “Immediate values” as it were, described by Matz cannot have singleton methods defined on them at runtime. So we’ll need some more magic to make them pipeable. Why don’t we try wrapping the values into something more palatable.

Let’s try implementing a few new methods to go along with pipe.

def wrap_immediate_value(input)
  pipe_method_wrapper = OpenStruct.new(value: input)

  pipe_method_wrapper.define_singleton_method(:|) do |filter|
    pipe filter.call(input)
  end

  pipe_method_wrapper
end

def define_pipe(input)
  input.define_singleton_method(:|) do |filter|
    pipe filter.call(input)
  end

  input
end

def wrap?(type)
  type.is_a?(Numeric) || type.is_a?(Symbol)
end

def pipe(input)
  return wrap_immediate_value(input) if wrap?(input)

  define_pipe(input)
end

def times(i,n)
  n * i
end

pipe(2.0) | method(:times)

Awesome! We have a working pipeline with immediate values, however the astute reader may have noticed the arity of the times method is actually two, when in fact we’re only calling it with 1 in our pipeline. When running our newly wrapped values we get 'times': wrong number of arguments (given 1, expected 2). Now we need to get around this pesky method/proc conversion problem and handle currying. Let’s also go ahead and start containing this code properly. The easiest way around this is to write code in a much more unconventional way so that we can easily manipulate the blocks of code we’re pipelining via method_missing. If we instead write our methods as constants on a module we can redirect calls to them.

module Main
  include Pipe

  Times = -> (i, n) { n * i }

  def self.run
    num = pipe(2.0) | Times(2)
    puts num
  end
end

First we begin by fetching the const off our module, calling it if all parameters are fulfilled, or currying what’s left.

def method_missing(method, *args, &block)
  code = self.const_get(method)

  if code.arity.zero?
    code.call
  elsif code.arity == args.length
    code.call(*args)
  else
    curry(code, args)
  end
end

We can curry quite simply given our code is already in block form and we’re being handed the arguments.

def curry(code, args)
  -> (input) {
    if code.arity == 1
      code.call(input)
    else
      code.call(input, *args)
    end
  }
end

Now bring it all together.

require 'ostruct'

module Pipe
  module ClassMethods
    def wrap_immediate_value(input)
      pipe_method_wrapper = OpenStruct.new(value: input)

      pipe_method_wrapper.define_singleton_method(:|) do |filter|
        Pipe.pipe filter.call(input)
      end

      pipe_method_wrapper
    end

    def define_pipe(input)
      input.define_singleton_method(:|) do |filter|
        Pipe.pipe filter.call(input)
      end

      input
    end

    def wrap?(type)
      type.is_a?(Numeric) || type.is_a?(Symbol)
    end

    def pipe(input)
      return wrap_immediate_value(input) if wrap?(input)

      define_pipe(input)
    end

    def curry(code, args)
      -> (input) {
        if code.arity == 1
          code.call(input)
        else
          code.call(input, *args)
        end
      }
    end

    def method_missing(method, *args, &block)
      code = self.const_get(method)

      if code.arity.zero?
        code.call
      elsif code.arity == args.length
        code.call(*args)
      else
        curry(code, args)
      end
    end
  end

  extend ClassMethods
  def self.included(other)
    other.extend(ClassMethods)
  end
end

Finally this will let us write code that can execute like so.

module Main
  include Pipe

  Times = -> (i, n) { n * i }

  def self.run
    num = pipe(2.0) | Times(2)
    puts num
  end
end

Almost there! If you ran the code above you’ll notice we don’t actually get 4.0 at the end of the pipeline! We instead get #<OpenStruct value=4.0>. That’s because we never unwrapped the immediate value and are simply returning the OpenStruct. We can solve this by creating a noop Unwrap function that’s explictly checked when defining the singleton method.

module Pipe
  Noop = -> {}
  Unwrap = Noop

  module ClassMethods
    def wrap_immediate_value(input)
      pipe_method_wrapper = OpenStruct.new(value: input)

      pipe_method_wrapper.define_singleton_method(:|) do |filter|
        if Unwrap.object_id == filter.object_id
          input
        else
          Pipe.pipe filter.call(input)
        end
      end

      pipe_method_wrapper
    end
  end
end

Now finally the pipeline would look like this

module Main
  include Pipe

  Times = -> (i, n) { n * i }

  def self.run
    num = pipe(2.0) | Times(2) | Unwrap
    puts num
  end
end

Now we can write full fledged pipelines! Here’s an example of a module with several pipelines covering lots of cases.

module Main
  include Pipe

  Alphabet = ('a'..'z').to_a

  Reverse = -> (arr) { arr.reverse }
  Upcase  = -> (arr) { arr.map(&:upcase) }

  Times = -> (i, n) { n * i }

  Foo = -> (foo, bar) { "#{foo} #{bar}" }

  Wat = -> (omg) { "wtf bbq #{omg}" }

  Join = -> (arr) { arr.join }

  def self.run
    string_sym = pipe(:foo) | Foo('bar') | Wat
    puts string_sym

    num = pipe(2.0) | Times(2) | Unwrap
    puts num

    a = pipe(Alphabet) | Reverse | Upcase | Join
    puts a
  end
end

Main::run()

Running the above should output:

wtf bbq foo bar
4.0
ZYXWVUTSRQPONMLKJIHGFEDCBA

If you made it this far, congratulations! We’ve successfully, albeit painfully, implemented pipelines in Ruby. This truly is a testament to how moldable Ruby’s syntax is. I hope you enjoyed this adventure and learned something new!

https://caleb.tn/?p=9
Extensions
Getting Things Done in 15 Minutes
Uncategorizedlinklogorganization
I’ve got the book and have been using the GTD method for 2 years now, but this will get anyone started immediately. https://hamberg.no/gtd
Show full content

I’ve got the book and have been using the GTD method for 2 years now, but this will get anyone started immediately.

https://hamberg.no/gtd

https://caleb.tn/?p=11
Extensions