Skip to main content

Writing

Hey look! I write, technically

Using lambda term statistics to show how hard we try to declutter programs

There are a couple of [a]symmetries in programming that really grip me, lifting my hands off the keyboard and pushing me deep into a mind vortex for some time. I'm especially entranced by the flows of control, data and abstraction it seems. When arguments and returns are treated differently (in a symmetry sense I mean... outside of the fact that they're polar opposites), my evening plans start clearing out. Some examples uphold this ying-yang but in an initially unintuitive way, like co- and contravariance of subtyping rules. Sometimes a dichotomy should be symmtric but is skewed by the forces of developer experience, like the gaping divide in popularity between ReactiveX and InteractiveX. Recently, I've come across another asymmetry of this sort that I thought was interesting enough to study a bit.

Condensed Asterius quickstart

Here's an Asterius quickstart that aims to be a little more make-it-work-oriented than the official docs.

Coding

deepseq for Data

Here's a very short but useful snippet I recently started using to force Data instances deeply:

{-# LANGUAGE Rank2Types #-}
import Data.Functor.Identity
import Data.Generics ( Data(..), GenericT )

gmapT' :: GenericT -> GenericT
gmapT' f x0 = runIdentity (gfoldl k Identity x0)
  where
    k :: Data d => Identity (d->b) -> d -> Identity b
    k (Identity c) x = Identity $! c $! f x -- let the user control strictness in `f`

strictify :: GenericT
strictify = gmapT' strictify

Reified types in Hack

Hack is beginning to support reified types in their latest 4.17 release, which adds a very interesting dimension to Hack's type system. Its interactions with Hack's unique medley of type features can be quite subtle, and when any new type feature emerges, I always like to learn how and where it interacts with other features. To be more specific, I like to find this out by trying to break the new type system. So let's try together!


Simplifying reactive operators

I've argued that I have reason to believe that ReactiveX (Rx) is backwards. At first I promoted InteractiveX (Ix), which inverts the control, swapping callbacks for await. If you follow the road of trying to simplify Ix and its iterator acrobatics, I believe you'll arrive where I was, reimplementing Reagent. Originally built to shim all of JVM/JS's reactive interfaces into Kotlin's concurrency framework, the result ends up suiting async-await languages better than both Rx and Ix. For many details, see "Reagent is theoretically the best…", but in short, Reagent approaches an exact equivalence to natural code — an isomorphism to what you would write in that situation without the library. In particular, Reagent provides expressive and natural features to implement all aspects of an asynchronous loop to chomp away at a data source.

Reagent is theoretically the best of ReactiveX

    This extended article discusses the merits of Jake Wharton's Reagent framework for Kotlin which has deep theoretical advantages standing in for ReactiveX. I've stripped away any class definitions to make a lightweight functional version that I'll derive and justify from first principles here, with implementations for Hacklang, Javascript and Kotlin. Jake Wharton has put the project on hiatus, and while I agree that it's hard to fight the massive mindshare of ReactiveX, Reagent is at least a powerful lens to the semantics of streaming and the minimum that we require of our languages to make them reactive. At least I hope to convey that if one feels ReactiveX is wasteful on top of an async-await language, their feeling isn't wrong.

    The maximum is in the middle

    Tracing values with state

      The development of ra.hs has called for an interesting task in the domain of symbolic programming. ra.hs is a Haskell linter for reactive programs, the first implementation I'm undertaking to identify possible race conditions in reactive code. The violation to look for is a subtle one: if two threads consume the same stream and read and write these values to the same shared memory, the ordering rules of the stream might be violated [citation to come]. To decide this, it's necessary to trace the flow of values through the program to see which stream they come from, if any.

      The degree of sophistication of analysis against the program could be incredibly varied, bounded above only by the undecideability of the halting problem (fun fact: with probability 1, halting can actually be decided). I believe that it's meaningfully bounded below by a type analysis, where the conditional nature of branches is ignored and all values that are the correct type that could flow to a call site are considered. This allows for a perfectly sensitive result that sacrifices specificity (i.e. it may report many false positives).

      Guide to InteractiveX in Kotlin

        I was just peeking at Kotlin to see if they supported asynchronous generators. I wasn't expecting much, because when I was making InteractiveX implementations for PHP and Hack last year, Kotlin coroutines weren't publicly documented. Man, I got way more than I bargained for. I knew even just looking at this title to this guide in kotlinx.coroutines:

        Guide to reactive streams with coroutines

        How to make a ReactiveX

        • Project_writeup
        • Reactive
        • Software

        I was tipped off to Reactive Frameworks from @Robert Harvey on a long-winded StackExchange question of mine for what "reactive databases" were called. I was infatuated with Hack's elegant async API and let me tell you it absolutely expanded my world in all the right ways.

        Why ReactiveX operators are hard to make and InteractiveX operators aren't

          Full-featured reactive stream libraries, like Rx, come with a very large set of operators to create, transform, combine and otherwise process the corresponding streams. Creating your own operators with support for back-pressure is notoriously difficult.

          Coroutines and channels are designed to provide an opposite experience. There are no built-in operators, but processing streams of elements is extremely simple and back-pressure is supported automatically without you having to explicitly think about it.

          from Kotlin's guide to reactive channels

          Logo sketches: Shard

            My logo already uses forced perspective to create an illusion, so why not really force it a little harder? Shard splits polygons at random vertices and carefully hurls them in front of a camera where they appear like the original. That is... until you put a spin on them!

            Logo sketches: Trace

            While designing some branding around my personal logo, I noticed the following in-spiral that felt like it was leading somewhere interesting: