Live data from Hacker News

Exotic Programming Ideas, Part 3: Effect Systems

stephendiehl.com

21–30 of 95 posts

Re: Exotic Programming Ideas, Part 3: Effect Systems

#21
post #6

I expect that, as with any other type system extension, the more granular your effects are, the more likely you are to run into a “what color is my function” problem. If you have a public API that declares certain effects, you’re stuck with those unless you break backward compatibility. In a practical system, when writing a library and especially an abstract interface, you’d want to be careful what you promise and de…

A properly done effects system with type-level annotation of the "color" of functions helps this, rather than hurts as you might surmise.

Take for example logging or tracing - we almost always in a modern backend application want an ambient trace or span ID and a log destination. What we don't want is to have to add those as parameters to _every function_.

So we want to paint these functions with the "logger" and "traced" colors, which respectively allow a function to send messages to an ambient logger via log(message: str) and to get the current trace context via getTraceContext() each with no other arguments.

The compiler will tell us if we call these functions from an unlogged, untraced function, so at a very high level - say at the RPC request level, we paint them early on. In Haskell we might even be able to do something like

    handleReq req = 
      withTraceSpan (...)
      . withLogging (...)
      $ handleReqInner req
Where handleReqInner requires these "colors".

Having too many effect handlers is never a problem either - you can always call an untraced function from a traced function. The "trace" effect handler just falls off at that call. Or in other words: you can always call a function whose colors are a subset of the call-site's.

This is somewhat a foreign concept to people who haven't worked with type systems like this and whose only concept of the color of function is purely binary - either async or not. In a good typed effect system, the compiler will assist you in knowing where you're missing an effect handler, and it will be easy to add those effect handlers sufficiently far from the business logic that you won't have to think about it.

Re: Exotic Programming Ideas, Part 3: Effect Systems

#22
post #10
post #7

Earlier quoted context omitted.

> Oleg can write unhygienic syntax-rules macros. Not only can he do it, he even wrote a paper about it: https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.36...

IMO implementing hygienic macros properly is one of the hardest tasks in programming languages, especially since variable capture can cause subtle bugs.

Well, certainly no one seems to understand how e.g. syntax-case works. But my impression is that macro hygiene in itself is a solution looking for a problem. The key advantage e.g. racket's macro system has over clojure or common lisp is not hygiene but being sufficiently well structured and rich to allow proper tooling. Good error messages with accurate locations >> macro hygiene.

Re: Exotic Programming Ideas, Part 3: Effect Systems

#23

I've had this idea of "dynamic returns" (akin to dynamic scope) in my head for a while. Reading this, it feels like a dynamically typed companion to effect systems. The idea of a dynamic return is just to give a formal way to accumulate things during a set of function calls, without having every function to be aware of what might be happening. In Python context managers are often used for this (e.g., contextlib.redir…

> E.g., if your effect is writing to stdout, you could rewrite all those changes

Could you? Once you write something into stdout, as far as your program knows it could already be sent across the world and turned into a set of bank transactions, or missiles fired.

Re: Exotic Programming Ideas, Part 3: Effect Systems

#25
post #6

I expect that, as with any other type system extension, the more granular your effects are, the more likely you are to run into a “what color is my function” problem. If you have a public API that declares certain effects, you’re stuck with those unless you break backward compatibility. In a practical system, when writing a library and especially an abstract interface, you’d want to be careful what you promise and de…

The "what color is my function" problem is insurmountable in Javascript, because the runtime does not allow you to call an async function from a non-async one. However, most effects aren't like this. If you say "this function needs randomness" then you can create a pure PRNG and call the function with the PRNG providing randomness. If you say "this function needs logging" you can tell it to log to a string and parse/ignore the string. If it throws an exception, you can catch the exception. Haskell even provides `unsafePerformIO`, which lets you run arbitrary computations as if they were pure.

Re: Exotic Programming Ideas, Part 3: Effect Systems

#26
post #3

I highly recommend Oleg Kiselyov's talk titled "Having an Effect"[0] in which he talks about - purely functional model of computation and its pitfalls - Actor model, effectful programming with requests and responses and an implementation in Haskell. - denotational semantics and combining effects. Once you have a model of your language, what if you want to extend it by adding another effect? It forces you to rewrite t…

The recent Unison language uses the effect system described in the Frank language https://www.unisonweb.org/

Re: Exotic Programming Ideas, Part 3: Effect Systems

#27
post #6

I expect that, as with any other type system extension, the more granular your effects are, the more likely you are to run into a “what color is my function” problem. If you have a public API that declares certain effects, you’re stuck with those unless you break backward compatibility. In a practical system, when writing a library and especially an abstract interface, you’d want to be careful what you promise and de…

The "what color is my function" problem is insurmountable in Javascript, because the runtime does not allow you to call an async function from a non-async one. However, most effects aren't like this. If you say "this function needs randomness" then you can create a pure PRNG and call the function with the PRNG providing randomness. If you say "this function needs logging" you can tell it to log to a string and parse/…

There is the specific issue with async functions, but that's only one example of a general problem, what I'm calling "function coloring." Workarounds are often possible, but they are still workarounds and often result in bad code.

We've been there with Java. An API takes a Runnable. You need to do something that does IO, so you catch the exception... and then what? Log and suppress it? This is how bad code happens. At best you get chained exceptions.

With Callable, they got smarter and declared it to throw Exception, which means it can only be easily propagated by other methods that declare they throw Exception. So it's a viral declaration, unless you engage in workarounds like exception wrapping.

You don't need an effect system for this to happen, though. In Go, you don't have the problem with different kinds of errors, but you can still classify functions into two categories: those that may return an error, and those that (apparently) always succeed. If you need to fix the API, you might end up with lots of refactoring [1].

Mistakes at the API level can be hard to fix without touching lots of API's. This is unsolvable in general. The only way to avoid workarounds (via escape hatches or by mocking things out) is to standardize whatever you can so it works together.

[1] https://www.dolthub.com/blog/2020-11-16-panics-to-errors/

Re: Exotic Programming Ideas, Part 3: Effect Systems

#28
post #6

I expect that, as with any other type system extension, the more granular your effects are, the more likely you are to run into a “what color is my function” problem. If you have a public API that declares certain effects, you’re stuck with those unless you break backward compatibility. In a practical system, when writing a library and especially an abstract interface, you’d want to be careful what you promise and de…

A properly done effects system with type-level annotation of the "color" of functions helps this, rather than hurts as you might surmise. Take for example logging or tracing - we almost always in a modern backend application want an ambient trace or span ID and a log destination. What we don't want is to have to add those as parameters to _every function_. So we want to paint these functions with the "logger" and "tr…

You're saying it's not a problem, but let's say you didn't think ahead and want to add tracing later, and there are many intermediate function calls between top level and the place where you want to trace. You still have to change every function signature to add the colors, right?

Re: Exotic Programming Ideas, Part 3: Effect Systems

#29
post #22
post #10

Earlier quoted context omitted.

IMO implementing hygienic macros properly is one of the hardest tasks in programming languages, especially since variable capture can cause subtle bugs.

Well, certainly no one seems to understand how e.g. syntax-case works. But my impression is that macro hygiene in itself is a solution looking for a problem. The key advantage e.g. racket's macro system has over clojure or common lisp is not hygiene but being sufficiently well structured and rich to allow proper tooling. Good error messages with accurate locations >> macro hygiene.

I think in theory they’re orthogonal but in practice the two go together. It’s all fine and dandy when you’ve got a restricted set of battle tested macros from a single library interacting in real world code, but it rapidly breaks down when you’ve got application authors of various skill sets all contributing their own macros because the dare not touch the ones that came before. Macro hygiene provides the equivalent of type level guarantees to metaprogramming scope.

Re: Exotic Programming Ideas, Part 3: Effect Systems

#30
post #6

I expect that, as with any other type system extension, the more granular your effects are, the more likely you are to run into a “what color is my function” problem. If you have a public API that declares certain effects, you’re stuck with those unless you break backward compatibility. In a practical system, when writing a library and especially an abstract interface, you’d want to be careful what you promise and de…

An effects system like this is more about controlling your own code and allowing for switching off implementations easily versus declaring what effects it has. Your declaration of effects on your function is saying, for example, "I need to output some text," and then in the caller of that function you have to do some action to "consume" that effect. For instance, your example might be an effect called "WriteState" an…

Refactoring tools are nice so long as you are in a closed-world environment where you can see all the code and make whatever changes are needed. They don't help nearly as much in an open environment where there are many code owners and not all code is visible to you. When you publish a library, a refactoring tool isn't going to tell you everyone who uses your library, and you don't have permission to change the call sites anyway.

The only thing for it is to push a new, incompatible version and other people will have to migrate. So you're pushing off the work onto them.

I don't see how an effects system helps with this much? It might help you better understand how you painted yourself in a corner, but I don't see how it helps you get out of it.

Post reply on HN