Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change Monad polymorphism #11

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Conversation

WhatisRT
Copy link
Collaborator

@WhatisRT WhatisRT commented Nov 28, 2024

Monads don't need to work at all levels anymore, and can have a different level for their result type.

This as an attempt at fixing #2.

Monads don't need to work at all levels anymore, and can have a
different level for their result type.
@WhatisRT WhatisRT requested a review from omelkonian November 28, 2024 14:34
@omelkonian
Copy link
Collaborator

@WhatisRT Please try this PR on the current Cardano ledger formalization and confirm that all monadic code still works, or if any changes were required.

I will report on other instances I have lying around.

Copy link
Collaborator

@omelkonian omelkonian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not clear why one would prefer this polymorphism over the previous Type↑ approach:

  • results in significantly more boilerplate
  • restricts some use cases as demonstrated in the comments

However, it is still more flexible than the previous approach in the output levels, as well as being compatible to stdlib. So, the question becomes:

Do we need the extra lifting of the same-level restriction?
If so, is this PR preferable to generalizing the previous approach instead? e.g.

Level↑ = Level  Level

Type↑↑ : Level↑  Typeω
Type↑↑ ℓ↑ =  {ℓ}  Type[ ℓ ↝ ℓ↑ ℓ ]

private variable ℓ↑ : Level↑

record Functor (F : Type↑↑ ℓ↑) : Typeω where

(with the above change, all the instances typecheck without additional changes)

fmap-∘ : ∀ {A : Type a} {B : Type b} {C : Type c}
fmap-∘ : ∀ {A B C : Type a}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is where the restriction really shows, and in fact goes back to the definition of fmap: we now can't map across levels, e.g. the following code does not make sense any more:

postulate
  X : Type
  Y : Type₁
  g : X  Y
  xs : List X

ys : List Y
ys = fmap g xs

...let alone reasoning with functor laws across levels:

postulate
  Z : Type₂
  f : Y  Z

zs zs′ : List Z
zs  = fmap (f ∘ g) xs
zs′ = (fmap f ∘ fmap g) xs

_ : zs ≡ zs′
_ = fmap-∘ {f = f}{g} xs

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, this is another downside here. Though I wonder if it comes up all that often. If I have to choose between this and having a Functor instance for A ⊎_, I'd choose the latter.

@WhatisRT
Copy link
Collaborator Author

I probably should have added an example of what's possible with this change, so I've added a Monad instance for Sum now.

I tried your above suggestion, but it seems to have really bad inference properties. It works fine in the Functor and Applicative modules, but if I try it in Monad I get ~50 unsolved constraints.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants