Live data from Hacker News

Algebraic Effects in Practice with Flix

relax.software

41–50 of 55 posts

Re: Algebraic Effects in Practice with Flix

#41
I think it is a cool feature, but I see some practical issues.

For me, encapsulation is a feature. I would like to see that a function uses a network call deep down, but only in a static analysis sort of way. So I don't want to mark something as potentially using a network until it actually uses it. And at the same time I wouldn't want to change every effect of every intermediate function just because I made something use an LLM or Redis cache.

It also seems that cross-cutting components such as observability instruments are just going to contaminate all functions with their need for memory, network, io, files, clock, locale, reflection, etc.

Re: Algebraic Effects in Practice with Flix

#42

I really want effects to shine and thrive, but since this is a very academic topic, only academic people engage with the research and it comes with academic pedantism, perfectionist worldview, strange attraction to beautiful consistency, etc. This automatically places effect research FAR from any practicality and grounded realism. 2 points, as examples: 1. Ever tried adding a simple print statement for debugging purp…

The Unison language supports algebraic effects and optimizes handlers that call their continuation at most once in tail position (we call these "affine"), so you can have the best of both worlds. Some links at the end if you're curious.

Here are a few places where "multi-resumable" stacks are still useful, even outside of nondeterminism:

* For instance, in a workflow engine a la Temporal, a `sleep` primitive might serialize and store the continuation in a distributed priority queue. The workarounds of not having access to the continuation are all not nearly as good.

* A pure interpreter of a structured concurrency ability is quite useful for testing, since it can test different interleavings of threads and produce tests that fail or pass deterministically. For Unison Cloud's distributed programming API, we have an (in-progress) chaos monkey interpreter that you can use for local testing of distributed systems.

* You can implement a simple debugger... as a library. It lets you set breakpoints and go forwards and backwards in time. Here's an example: https://share.unison-lang.org/@pchiusano/stepwise

Basically, any time you want to stash and do something interesting with the continuation, even if you only end up ultimately using it once, you still need the more general form of algebraic effects.

And then there are various nondeterminism effects that do call the continuation more than once. I'd say these are somewhat niche, but when you need them, you need them, and the code comes out much nicer. I especially like it for testing. You generally want tests to just be the logic, not a bunch of looping code or map/flatMap.

Some links:

* https://www.linkedin.com/posts/pchiusano_dan-doel-has-been-d... has some details on the optimization we do in Unison

* https://dolio.unison-services.cloud/s/blog/posts/optimizing-... is Dan's blog post on optimizing affine handlers

* https://www.linkedin.com/posts/pchiusano_kestrel-is-a-higher... is a typed query DSL that uses nondeterminism in an interesting way. For a declarative query DSL, it's nice to avoid explicit looping, similar to what SQL does.

Re: Algebraic Effects in Practice with Flix

#43
post #39
post #19

Maybe I’m not getting it, but isn’t this just interfaces and implementations from the OO world? For example their movie one is: interface MovieApi { List getPopularMovies(); } What are effects providing over this?

Implementing an interface results in a class that implements it. From then on, the fact your class implements that interface is entirely obscured from your code. In the case of tracking effects, this is clearly undesirable. (Also, in a language that doesn't have classes, implementing interfaces is difficult for me to think about.) In the case of algebraic effects, they're functions with type signatures explicating th…

An interface is just a function signature. Perhaps it might be easier for you to think about as just a function:

    type MoviesApi = void => List[Movie]
I guess the difference is the tracking?

Re: Algebraic Effects in Practice with Flix

#44

Earlier quoted context omitted.

1. Nonsense [1] 2. It's implementation dependent, but of course you lose tracing, etc if you want to just log with language primitives, which is why you shouldn't when every effect system offers you tools to do so. If you want a system where each side effect is traced, monitored and encoded as a recipe, then you use the effectful version. [1] https://effect.website/play#769a55e0ea0a

on 1: i want to stab my eyes out. you cant surely implement any monadic and/or effect system on top of any native generators and effect.ts is no exception, but this is just so wrong, buegh on 2: can you please reformulate and elaborate more? i have re-read what youve said 5 times and it feels like gpt written rambling, sorry

Yes you can, why wouldn't you? It's an implementation detail in any case.

It's fine if you don't understand, when people comment about topics they don't know much about, such as effect systems in your case, it happens.

Re: Algebraic Effects in Practice with Flix

#45
post #26
post #21

Earlier quoted context omitted.

I _think_ the distinction is that the interface tells you what MovieAPI _does_, but this system also tracks what are contextual requirements to do that thing. So when they write their version of the MovieAPI that calls out to an existing web api, they have to write `\ {Net, IO}` on the main method that uses them. When they write their test that uses a canned recommendation, they can just write `\ {IO}`, and they have…

The checked exceptions analogy is a good one. Thinking of effect handlers as resumable checked exceptions with some syntactic sugar is very accurate. For someone with a Haskell background, thinking about them as "dependency injection" is also helpful (and these notions are equivalent!) but for the Java heads out there, yeah, resumable checked exceptions provides a really good mental model for what effect handlers are…

What’s the difference between a resumable checked exception and a function call?

Re: Algebraic Effects in Practice with Flix

#46
post #45
post #26

Earlier quoted context omitted.

The checked exceptions analogy is a good one. Thinking of effect handlers as resumable checked exceptions with some syntactic sugar is very accurate. For someone with a Haskell background, thinking about them as "dependency injection" is also helpful (and these notions are equivalent!) but for the Java heads out there, yeah, resumable checked exceptions provides a really good mental model for what effect handlers are…

What’s the difference between a resumable checked exception and a function call?

Function call always returns, and to one single caller, whereas effects can choose not to "return" at all, resume multiple times, etc

Re: Algebraic Effects in Practice with Flix

#47
post #45

Earlier quoted context omitted.

What’s the difference between a resumable checked exception and a function call?

Function call always returns, and to one single caller, whereas effects can choose not to "return" at all, resume multiple times, etc

Right, though the former is just an exception. So what general effect systems provide above and beyond what we already have in most languages is "multiply-resumable" checked exceptions (also known as multi-shot continuations and often provided by "delimited continuations").

At the time I developed my Haskell effect system Bluefin there was a conventional wisdom that "you can't implement coroutines without delimited continuations". That's not true: you can implement coroutines simply as function calls, and that's what Bluefin does.

(The story is not quite as simple as that, because in order for coroutines to communicate you need to be able to pass control between threads with their own stack, but you still don't need multi-shot continuation.)

Re: Algebraic Effects in Practice with Flix

#48
post #47

Earlier quoted context omitted.

Function call always returns, and to one single caller, whereas effects can choose not to "return" at all, resume multiple times, etc

Right, though the former is just an exception. So what general effect systems provide above and beyond what we already have in most languages is " multiply -resumable" checked exceptions (also known as multi-shot continuations and often provided by "delimited continuations"). At the time I developed my Haskell effect system Bluefin there was a conventional wisdom that "you can't implement coroutines without delimited…

Good point! You might be interested in reading this article on the topic: https://without.boats/blog/coroutines-and-effects/

Re: Algebraic Effects in Practice with Flix

#49
post #47

Earlier quoted context omitted.

Right, though the former is just an exception. So what general effect systems provide above and beyond what we already have in most languages is " multiply -resumable" checked exceptions (also known as multi-shot continuations and often provided by "delimited continuations"). At the time I developed my Haskell effect system Bluefin there was a conventional wisdom that "you can't implement coroutines without delimited…

Good point! You might be interested in reading this article on the topic: https://without.boats/blog/coroutines-and-effects/

Thanks, I did find that interesting. I would say Bluefin is another entry in the static/lexical row, whereas its cousin effectful is in the static/dynamic row (although this may be a slightly different interpretation of the terms than is used in the article).

Re: Algebraic Effects in Practice with Flix

#50
post #19

Maybe I’m not getting it, but isn’t this just interfaces and implementations from the OO world? For example their movie one is: interface MovieApi { List getPopularMovies(); } What are effects providing over this?

Flix does not have generic effects, yet it has generic traits.

This hints that effects are not meant to deal with data variability.

Effects are allowing variability of control flow.

Abstracting `try/catch/finally` or `async/await`.

For example, something like "durable workflows" is just a specific effect implementation, code stays the same.

Post reply on HN