Notes

The last sentence of the OpenAI Harness Engineering post:

Our most difficult challenges now center on designing environments, feedback loops, and control systems that help agents accomplish our goal: build and maintain complex, reliable software at scale.

This is the defining concept of this era.

Everything has shifted from writing code, to setting up these feedback loops and control mechanisms.

Notes

note 02: python vs javascript `async`/`await`

I've historically found async in Python a bit confusing. Partly because I haven't taken the time to fully understand it, partly because it actually is confusing. The async story in Python has gone through significant changes to reach where it is now.

The Event Loop

Javascript has an implicit event loop that's always running. This is not the case in Python.

When you do a setTimeout or setInterval, you're interacting directly with this global event loop.

In Python, you need to create this loop yourself.

async def main():
    print('hello there')

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    loop.close()

# or: send this to the event loop
asyncio.run(main())

Futures, Generators, Promises

In Python, async functions return coroutine objects when called. This is different from JS.

async function foo() {
    console.log('hello there')
}

foo() // this logs 'hello there'
async def py_foo():
    print('hello here')

coro = py_foo() # this just creates the coroutine object

# let's run the func
asyncio.create_task(coro)

asyncio.create_task runs the task without blocking asyncio.run() or loop.run_until_complete blocks until finished

Notes

Monads, monoids, categories...oh my

I have this thing where sometimes I want to understand a complex topic, even though it has little relevance to my life at the moment.

I've been looking at Effect.ts which has a somewhat steep learning curve. Trying to understand the theoretical basis of what an Effect is, lead me back to monads. And so here we are...I'm working on writing a proper article about how to think about Monads, from both a category theory and a programmer's perspective.

Like most math topics, it takes a few clicks of insight to fully grasp. Here are some of those for me:

  • Categories are "easy" to grasp abstractly: Objects and Arrows.
  • Similarly, functors are easy: they map objects to objects, and arrows to arrows, in a way that preserves structure
  • A functor \(F: \mathcal{C} \to \mathcal{D}\) has to be defined on all of \(\mathcal{C}\), but can collapse multiple objects to a single object in \(\mathcal{D}\).
  • The simple descriptions above get more confusing when you get concrete. Most objects in interesting categories, are actually themselves composed of items. For example, sets and groups. A single set is an object in \(\mathbf{Set}\), but there are also elements of that set. You can work at either level of abstraction.
  • monoids are just like groups but you don't have inverses.
  • A monoidal category: you take a regular category and equip a bifunctor. Given two objects, you need a "natural" way to get out another object. And similarly for two arrows. And you need an identity object.
  • There are alot of requirements in category theory that you need to be precise, but, imo, often hinder understanding. For example, in the above, you have coherence relations and a few other things that you need, to make sure that things play nicely together.
  • The category \(\mathbf{Type}\) is where programming comes in. This is category where objects are \(types\) and the arrows are actual functions, in the programmatic sense!
  • The category of endofunctors on \(\mathbf{Type}\) is where monads come up.
  • Monads are actually easier to grasp from a programming perspective. Conceptually, it is the wrapping up of a value in another type! You've probably come across this heavily in Rust with the Option<T> type.
  • A monad is a triple:
    • a map from types to types, and function to functions.
    • a unit operation that lets you enter monad space: \(A\to M[A]\)
    • a flatten operation: given a doubly wrapped monad object, peel back one layer: \(M[M[A]]\to M[A]\)
  • The flatten operation is not at all the same as extract which goes from \(M[A]\to A\). Why? Look at Option<T>. If T is something, you can get Some<T>, but in the other case, there's no T to return! Since nil is not a T
  • For a long time, I was under the impression that a monad was just a function of a certain kind, but no, it's a triple!