Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Looks very cool! Can somebody enlighten me what's happening in the Algebraic Effects example? Specifically this part:

    handle f ()
    | flip () -> (resume true + resume false) / 2.0
Does `handle f ()` call `calculation` and the `| ...` part "injects" the `flip` effect? I am also quite confused by the part following `| flip ()`. It somehow returns true or false with a probability of 50%? And why does this give you the expected value of the whole calculation function in the end?


Author here, a good way to understand algebraic effects is as "resumeable exceptions." In this case the expected_value handler says to run `f ()` and whenever that computation "throws" a `flip ()` effect to handle it by resuming the computation with the value true returned for flip. The continuation continues as normal, subsequent uses of flip are also handled until the computation finishs. Then we evaluate the rest of `+ resume false) / 2.0` and resume the computation (a second time!) in the same place, this time with the value false. Finally, we add both values returned and divide by two to get the expected value.

This way of computing expected value works because for each flip we essentially have a case-split: the value can be true or false with a 50-50 chance, so the expected value of the whole thing is is 0.5 * the result of the true branch + 0.5 * the result of the false branch!

This is more of a fun use of effects than a practical one. If you're still curious about effects, check out the full page on them here: https://antelang.org/docs/language/#algebraic-effects which includes some actually useful effects like State, Generators, looping constructs, parsers, etc.


Thanks for the explanation, I will definitely check out the docs on algebraic effects!


Explanation based on my familiarity with Koka:

This expression matches on the effect flip() and handles it with the code on the right hand side of -> which becomes the value of the function invoking the effect. In this case the handler runs the continuation for both possible values (resume true and resume false, i.e. the function was called once, but it will return twice, with true and false substituted in the place of flip() in the place which used this effect) and returns their mean as the average.

I.e. each time calculation calls flip(), the effect handler will continue the function for both true and false and then take the mean of those two results. Comments explain that flip() should simulate a coin flip, which generally gives equal probability (1/2) to true and false. Each flip() in calculation was simulated with equal number of true and false values, so it fits the expected distribution. Each time taking the mean of those values is just an expansion of the integral calculating the expected value, as per definition of expected value.

Note that there is some recursion here. While the flip() handler is executing for the first invokation of flip(), another instance of the handler will execute for second and third invokations (inside resume true). I.e. it calculates the expected value by taking the mean of the expected values of each branch created by a flip. When a branch doesn't have any more flips inside, its expected value is taken to be the constant value that this branch returns.


I don't fully understand it yet, but I think it resumes twice, adds the results, and divides by two.

the `| flip ()` is pattern matching on that effect term, I believe. So essentially: when you see `flip ()` inside anything in the argument of `expected_value`, capture the continuation there, run that argument once with `true` and once with `false`, add the results, divide by two.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: