Zulip Chat Archive

Stream: general

Topic: Function Monad


Kevin Fisher (Dec 11 2024 at 22:21):

So, I know in Haskell we have a function monad:

instance Monad ((->) r) where
     return x = \_ -> x
     h >>= f = \w -> f (h w) w

Does such a thing exist in Lean?

I ask because I'm trying to define the following function pointfree:

def double (α β : Type) (f : α  α  β) (x : α) : β := f x x

I know in Haskell I can use the function monad like so:

double :: (a -> a -> b) -> a -> b
double = join

But using joinM or Monad.join doesn't seem to work in Lean.

Kyle Miller (Dec 11 2024 at 22:24):

I don't think there's a monad instance on the function type itself, but at least there's the Reader monad, which is the same thing but with a name.

def double (α β : Type) : (α  α  β)  α  β :=
  show ReaderM α (ReaderM α β)  ReaderM α β from
    joinM

Kyle Miller (Dec 11 2024 at 22:29):

This doesn't work:

instance (ρ : Type _) : Monad (ρ  ·) := inferInstanceAs <| Monad (ReaderM ρ)

def double (α β : Type) : (α  α  β)  α  β :=
  joinM -- fails

Kevin Fisher (Dec 11 2024 at 22:29):

Hm, I wonder why it's not defined. Does it break one of the monad laws or something?

Kyle Miller (Dec 11 2024 at 22:33):

A proximal reason is that it just doesn't work (like in my second code block)

But, for a design reason, I think it's better when monads are explicit. If you have α → α → β, is this α → α → β, α → ReaderM α β, ReaderM α (α → β), or ReaderM α (ReaderM α β)? Lean's hard enough, let's not make it impossible to guess in what way a value is meant to be a monad too :-)

Kevin Fisher (Dec 11 2024 at 22:34):

Fair enough, it is pretty magical in Haskell. Guess I'll have to read up about reader monads then

Kyle Miller (Dec 11 2024 at 22:35):

Given that you seem to know about the function monad, and that ReaderM α β is literally α → β by definition, I think you don't need to do any reading here. ReaderM α is exactly the function monad, but explicitly written.

Kyle Miller (Dec 11 2024 at 22:38):

The main operation it gives is read. Reading through the Lean definitions is a bit complicated, because it's meant to be convenient for a number of monads, but for the case of ReaderM we could define it as

def read {ρ : Type _} : ReaderM ρ ρ := id

It "reads" the state (hence why it is called "reader monad").

Matt Diamond (Dec 11 2024 at 23:00):

hmm...

import Mathlib

instance Function.instMonad (α : Type) : Monad (α  ·) where
  pure x := fun _ => x
  bind f g := fun x => g (f x) x

instance (α : Type) : LawfulMonad (α  ·) :=
LawfulMonad.mk'
  (m := (α  ·))
  (id_map := by aesop)
  (pure_bind := by aesop)
  (bind_assoc := by aesop)

Kyle Miller (Dec 11 2024 at 23:03):

This shouldn't be surprising @Matt Diamond, you redefined the ReaderM monad. Your func α is ReaderM α :-)

example (ρ : Type) : Function.instMonad ρ = (inferInstance : Monad (ReaderM ρ)) := rfl

Kyle Miller (Dec 11 2024 at 23:03):

(I commented before your edit)

Matt Diamond (Dec 11 2024 at 23:04):

your comment is still true, right? (oh you just meant you were referring to func?)

Kyle Miller (Dec 11 2024 at 23:04):

The monad definition in #general > Function Monad @ 💬 is also same as the one you give post-edit btw.

Kyle Miller (Dec 11 2024 at 23:05):

Yeah, I'm saying no need to define func, since that's already ReaderM.

Matt Diamond (Dec 11 2024 at 23:11):

just to be clear @Kevin Fisher, this does work (if you include my monad definition above):

def double (α β : Type) : (α  α  β)  α  β := Monad.join (m := (α  .))

Edit: Oh, I see what Kyle is saying... this works out of the box:

def double (α β : Type) : (α  α  β)  α  β := Monad.join (m := ReaderM _)

so it looks like you can use Monad.join after all... it just needs a type hint


Last updated: May 02 2025 at 03:31 UTC