Zulip Chat Archive

Stream: lean4

Topic: algebraic effects and handlers?


Jason Gross (Mar 23 2021 at 17:43):

I've heard that algebraic effects and handlers form a nicer basis for stateful computation than monads (for example, there's no need to lift operations as there is when you nest monads). Has anyone thought about basing things in Lean on algebraic effects and handlers rather than monads? (c.f. https://xnning.github.io/papers/haskell-evidently.pdf , for example)

Sebastian Ullrich (Mar 23 2021 at 17:50):

Yes, it's a topic we've considered before. The very short answer is that AFAIK there are only two ways to avoid lifting, neither of which is acceptable for building nontrivial programs like Lean itself: by blowing up the runtime overhead (open union encoding etc.) or the compile time overhead (monomorphization).

Daniel Selsam (Mar 23 2021 at 18:06):

Jason Gross said:

I've heard that algebraic effects and handlers form a nicer basis for stateful computation than monads (for example, there's no need to lift operations as there is when you nest monads). Has anyone thought about basing things in Lean on algebraic effects and handlers rather than monads? (c.f. https://xnning.github.io/papers/haskell-evidently.pdf , for example)

FWIW I have found the auto-lifting in Lean4 to be so good that I almost never need to think about this.

Jason Gross (Mar 23 2021 at 19:28):

Open union encoding is where everything is polymorphic over all the other effects (or underlying monads) that might be present?

Locria Cyber (Jul 26 2022 at 21:48):

Maybe have compile-time support for union?

Locria Cyber (Jul 26 2022 at 22:00):

I made a library for this. https://github.com/locriacyber/lean-eff

Locria Cyber (Jul 26 2022 at 22:01):

See Example.Handler and Example.HandlerControl for example.

I don't know how to bench mark, but the runtime of those seem minimal.

using this:

#!/usr/bin/fish
time for i in (seq 10000)
    ./test_control
end >/dev/null


time for i in (seq 10000)
    ./test_handle
end >/dev/null

Locria Cyber (Jul 26 2022 at 22:02):

There is no difference in time used. (probably because it's IO bound)

Locria Cyber (Jul 26 2022 at 22:03):

I would love to remove the sorry in my code, if anyone want to help.

Mario Carneiro (Jul 26 2022 at 22:10):

Can you make a #mwe? Those should be proved automatically

Locria Cyber (Jul 27 2022 at 11:15):

inductive Free : (_monadish: Type u -> Type v) -> (_ret: Type u) -> Type _ where
| pure : α -> Free ε α
| bind : ε α -> (α -> Free ε β) -> Free ε β

/-- Proof that a list has an element -/
class inductive Elem : α -> List α -> Type _ where
| zero : Elem x (x::xs)
| succ : Elem x xs -> Elem x (y::xs)

namespace Elem
  instance : Elem a (a :: xs) := Elem.zero
  instance [prev: Elem a xs] : Elem a (x::xs) := Elem.succ prev
end Elem

inductive Union : {α : Type u} -> List (α -> Sort v) -> α -> Sort _ where
| mk [prf: Elem ε εs] : (x : ε α) -> Union εs α

namespace Union
  example : Type _ := Union [IO] Unit

  def simplify : Union [ε] α -> ε α
  | Union.mk (prf := Elem.zero) x => x
end Union

@[reducible]
def Eff es := Free (Union es)

namespace Eff
  def run1 [Pure ε] [Bind ε] : (stacked: Eff [ε] α) -> ε α
  | Free.pure x => pure x
  | Free.bind val cont => Bind.bind (m:=ε) (Union.simplify val) (λ a => run1 (cont a))
  decreasing_by sorry
end Eff

Locria Cyber (Jul 27 2022 at 11:15):

I can't prove Eff.run1 terminate.

Locria Cyber (Jul 27 2022 at 11:23):

Lean doesn't let me do this

mutual
  def bbb (_ : Nat) : Free Option Nat := Free.bind (Option.some 1) aaa
  def aaa (_ : Nat) : Free Option Nat := Free.bind (Option.some 1) bbb
end

So it's safe, right?

François G. Dorais (Jul 27 2022 at 13:44):

If you replace sorry by { simp_wf; done } you will see the issue: there is no reasonable way to assign a meaningful sizeOf value to cont. With bind : ε α -> (α -> Free ε β) -> Free ε β you want the size of bind val cont to be larger than that of cont x for every x : α. This doesn't make sense, for example, when α is Nat and sizeOf (cont n) > n for every n : Nat.

Bottom line, the Free monad is too big for a measure into Nat. You will need to use a more general form well-founded recursion to make this work.

Andrés Goens (Jan 12 2023 at 12:22):

Locria Cyber said:

I made a library for this. https://github.com/locriacyber/lean-eff

@Locria Cyber did you migrate this repository somewhere else (i.e. is it still public somewhere)?

Locria Cyber (Feb 03 2023 at 21:31):

Andrés Goens said:

Locria Cyber said:

I made a library for this. https://github.com/locriacyber/lean-eff

Locria Cyber did you migrate this repository somewhere else (i.e. is it still public somewhere)?

https://git.envs.net/iacore/lean-eff


Last updated: Dec 20 2023 at 11:08 UTC