Zulip Chat Archive

Stream: lean4

Topic: Tail recursive bind


Juan Pablo Romero (Sep 22 2022 at 22:54):

I'm curious about a recent change to mapM:
image.png

the part do loop as ((← f a)::bs) is the same as bind (f a) fun b => loop as (b :: bs) right?

How is that tail recursive? (as bind is the last expression of the function call, not loop).

Leonardo de Moura (Sep 23 2022 at 20:00):

Quick remarks:

  • We specialize mapM for different monads.
  • StateT.bind, ExceptT.bind, ReadetT.bind, ... are all inlined by the code generator.
  • The specialized mapM is tail recursive for these commonly used monads. This is not true for every monad, but it is true for the ones we use most of the time.

Mario Carneiro (Sep 23 2022 at 20:10):

Basically, for that expression to be tail recursive you need bind a f to call f in tail position, and this is true for all the standard monads except List and the CPS monadsStateCpsT and ExceptCpsT

Juan Pablo Romero (Sep 23 2022 at 23:04):

I see... so it is tail rec because after f a is evaluated and the resulting value b passed to the lambda, the resulting expression loop as (b :: bs) is in tail position and can be evaluated without having to keep any stack information around?

Mario Carneiro (Sep 23 2022 at 23:06):

well, like Leo said it is also required for all the intermediate stuff to get inlined for it to be syntactically recognized as a tail recursion, but semantically yes that's what's happening

Juan Pablo Romero (Sep 23 2022 at 23:07):

got it, thanks!


Last updated: Dec 20 2023 at 11:08 UTC