9 ms·
Do effect systems actually avoid colored functions? Don’t most typed effect systems require the used effects in the signature?
by HeyImAlex 4mo ago
Do effect systems actually avoid colored functions? Don’t most typed effect systems require the used effects in the signature?
- codebje 4mo agoWhen you need to use an effect, you need it in the type. If you directly call a function using some other effect, it propagates into your function. So far, so colourful. But you can have generic effects. Your arguments and return type can specify "any effect", indicating your function can use a type with any effect safely, or can be used in any effect context safely. Passing an async value to a function doesn't mean that function must now also be an async function. It can be a "for all effects, do the thing" function. The code duplication problem is gone.
- mrkeen 4mo agoNo, they are function colouring. That's the point. Someone writes a post lamenting red and blue functions, and everyone eats it up. Substitute colour for something meaningful and the idea becomes idiotic. "Top level function declares that it is non-blocking, but when I try to call a small blocking function from it, I have to change the declaration to blocking???" Yes, yes you do. Total functions can't call non-total functions. Deterministic functions can't call nondeterministic functions. Non-IO functions can't call IO functions.
- captaincrowbar 4mo ago‘Non-IO functions can't call IO functions.’ How do you handle logging then? If f() calls g(), how can I add logging to g() without having to change or recompile f() (and everything in the call stack above it)? ‘You can’t’ is not an acceptable answer.
- CuriousSkeptic 4mo agoYou can’t is an acceptable answer. The entitle point of such a feature is to prevent people from doing that.
- mrkeen 4mo agoDon't declare it as non-logging.
- tome 4mo agoNot sure why people are saying "you can't" when it seems to me the whole point of algebraic effects that you can. You can define g so that it has no ability to do "general IO", all it can do is yield log messages. Then f can call g in a way that turns the log messages into writes to stdout. For example, here's how you would do it in Bluefin: type Log = Yield String -- workWithLogging cannot do arbitrary IO! -- All it can do is yield log messages, which -- must be processed elsewhere. workWithLogging :: (e1 :> es) => Log e1 -> Int -> Int -> Eff es Int workWithLogging l x y = do yield l ("x was " <> show x) yield l ("y was " <> show y) let result = x + y yield l ("result was " <> show result) pure result -- ghci> example -- x was 5 -- y was 7 -- result was 12 -- 12 example :: IO Int example = runEff $ \io -> do -- forEach determines how each log message -- should be handled. forEach (\l-> workWithLogging l 5 7) (\logMsg -> effIO io (putStrLn logMsg))
- codebje 4mo agoIf `left_pad()` calls `send_env_vars()`, how can you add exfiltration to `send_env_vars()` without having to change `left_pad()` to expose the use of the network? "You can't" should be the ONLY acceptable answer.
- brabel 4mo agoYes, this article is doing a bad job at explaining why you would want effects, and one of the main advantages is exactly that it becomes part of the type system, essentially coloring every single function with a set of effects it needs to be called. As the article used JavaScript it shows what untyped effects would look like, which in my opinion is awful. If you want to use algebraic effects today, I highly recommend Unison. If you’re on the JVM, Flix is doing major advances with effects! https://www.unison-lang.org/ https://www.unison-lang.org/ https://flix.dev/ https://flix.dev/