News for hackers. Stuff that matters.
Re:Exotic Programming Ideas, Part 3: Effect Systems by AaronFriel on Sunday November 22, 2020 @01:48PM (#25179878)
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 by skybrian on Sunday November 22, 2020 @03:44PM (#25180891)
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 by AaronFriel on Sunday November 22, 2020 @05:15PM (#25181622)
In a language and type system where the compiler infers all of the intermediate types, no.

The way effect systems are typically added to languages without this level of type system is that the effect "fails". Like making a database call in Python without "with use_database(...):" that provides the database context. In those languages and with those effects, typically the default behavior is the effect's methods are always available, but may either be a no-op/return a nullish value or throw an exception.

As an example, the way tracing works on Node.js is that you have to explicitly declare a span (or use a plugin for a framework which does that for you) and inside that span, you can do something like getSpanId() and it'll return a value. Outside a span, it returns null.

The worst case scenario is you have a language and framework where it's both difficult to annotate the types and difficult to provide some ambient context. In those cases, you're back to (essentially) passing your trace span, your logger, etc as arguments to every function again. Not ideal.

Re:Exotic Programming Ideas, Part 3: Effect Systems by dan-robertson on Sunday November 22, 2020 @05:32PM (#25181747)
In every language I’ve used where types can be inferred, almost everyone writes down the types of their top level functions (or at least the functions that are exposed to clients). I don’t think this is really a good counter argument.

The only ways I know of to reliably make this kind of change (or just about any nontrivial change) to an interface are increasing version numbers and letting clients suffer or having a monorepo and fixing all the clients yourself

Re:Exotic Programming Ideas, Part 3: Effect Systems by AaronFriel on Monday November 23, 2020 @03:08PM (#25191119)
In Haskell, it's the norm I think to do both: let the compiler infer the type and then add the inferred type to the code. This locks in compatibility.
Re:Exotic Programming Ideas, Part 3: Effect Systems by shthrow on Sunday November 22, 2020 @04:09PM (#25181134)
No, think of it like checked exceptions, you only need to handle the effect _at most once_ before it reaches the top (it's up to you to let it keep getting passed up or handle it immediately), otherwise the compiler will automatically infer the effect types in the layers between.
Re:Exotic Programming Ideas, Part 3: Effect Systems by skybrian on Sunday November 22, 2020 @05:09PM (#25181589)
Okay, so type inference is the vital feature here.

It can help in a closed code base. In an open code base, you could inadvertently change a public API, breaking callers that are unknown to you, if the type signature of something public is inferred.

Re:Exotic Programming Ideas, Part 3: Effect Systems by shthrow on Tuesday November 24, 2020 @01:34PM (#25201425)
Correct, but that is the same for any kind public API though. However that is slowly starting to change with statically typed APIs like GraphQL and Protocol Buffers. You can imagine a public API that is introspected and can tell the client about deprecation and all that.
Re:Exotic Programming Ideas, Part 3: Effect Systems by creata on Sunday November 22, 2020 @10:07PM (#25183224)
As Quekid5 said, isn't that the way things should be? Adding tracing is a breaking change, and adding logging inside a deeply nested layer is a breaking change to all the outer layers, too.
Re:Exotic Programming Ideas, Part 3: Effect Systems by Quekid5 on Sunday November 22, 2020 @04:09PM (#25181131)
Yes, you do... which is as it should be. What else would you expect?

If a function needs capability X then it must require that capability. Simple as that.