Join the discussion

Write your take first — we'll ask for email only when you're ready to publish.

  • Hacker News
  • > What would it even look like for the top-most function to have to know “everything” that all the child functions end up ever needing? For every function parameter change in the entire program to somehow force a change all the way up the entire call stack? Clearly this does not happen. It so thoroughly does not happen that you may be having difficulty even imagining what it is I am talking about.

    Really? In my experience, it certainly does happen and I'm generally happy (but not thrilled) when it does. It means the type system is doing its job, forcing me to provide all the dependencies that a function needs. If 38 layers deep something now needs a connection string that it didn't have before, that had better bubble up to the top-most function. Unless of course the top-most function has already provided it to an intermediate layer which can break the chain, but that is very different than the context.Background() example.

  • Generally when talking about colored functions, I think of an example like "async function" in javascript, those are definitely different colored.

    I don't know about what would constitute as colored function in go as I generally don't consider arguments to be function colors.

    Maybe methods in go would be different colored functions when they are defined on different struct types.

  • My personal yardsticks:

    How often does a conceptually incremental change correspond to a proportionally incremental code diff, vs. a surprisingly whole-world-upside-down rearchitect?

    Do the code patterns naturally leave enough room for you to incrementally shift/transpose how things are organized?

    When the answers to these are unfavorable, I often find what effects to "coloring" in the framework, library, or language, sometimes all at once.

  • Actually, the IO monad in haskell desugars to

    RealWorld -> (a, RealWorld)

    Where RealWorld is a "token" that is just meant to be threaded through. You cannot create one yourself, so you have to be given one.

    Thus, that function argument colors IO. Every PL feature is just an extension to System F.

  • A subset of function arguments are function colors because async/await is isomorphic to requiring and passing a `callback` arg around. It was named 'callback hell` because there wasn't an escape hatch and it affected every function in the callstack.

    I'd venture to guess that there's a more abstract way of formalizing this in the sense that monad instances are required to be threaded through the callstack in particular cases(would love to see this spelled out formally) where IO and Cont(?) have this requirement but State like the author points out, does not.

  • There's usually a way to run async on another thread (or this thread) and block on it, so async is not a color by your definition.

    I think color should be defined in terms of what you're likely going to do. You can call asyncio.run or context.Background or unsafePerformIO but they're both hacks that you shouldn't do because they are not correct, so we ignore them for colour analysis.

  • I think this is a fine distinction to make, though it's worth noting that function arguments _can be_ colors as the author describes if the arguments have to come from a specific place, e.g. in languages where only `main` receives certain arguments relating to system functionality, or a capability framework where only the top-level function receives the capability token.
  • Under this framework, Rust's async fns would not be colored, since any function in the callstack could be blocking on an executor. if you consider "spawn point" a part of the callstack, many other languages are similar.

    This doesn't feel like a useful distinction, but i don't think "function coloring" in general is a useful framework.

Explore Birbla archives