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

church-encoded Free monad #8

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

church-encoded Free monad #8

wants to merge 6 commits into from

Conversation

mstksg
Copy link

@mstksg mstksg commented Jan 9, 2019

Just following up on FormationAI/dhall-bhat/pull/48 :) Tried my best to shift this to match the format of dada. From the original PR, I've also added corecursive and a FreeF base functor, although I'm not sure where the best place to put FreeF is, or also the best name (I've noticed yaya/dada prefer to have more descriptive names than just MyFunctorF). Let me know if there's anything I can fix!

One exception to the explicit style guide mentioned in FormationAI/dhall-bhat/pull/49 is that I sometimes use inline imports in situations that parallel other similar usages in the rest of the repo.

@sellout
Copy link
Owner

sellout commented Jan 10, 2019

I'm not sure where the best place to put FreeF is, or also the best name

This is actually 80% of the reason Free doesn’t yet exist in this repo 😆 – I’m not happy with any of the names for it. But FreeF is fine for now. It deserves to exist at least.

Copy link
Owner

@sellout sellout left a comment

Choose a reason for hiding this comment

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

Thanks, @mstksg, looking forward to getting this in.

We’re in the right ballpark now. I showed some ways to integrate this more into the generalized recursion approach. Definitely haven’t done all the work for you, and I can help walk through some of the changes if they’re not clear.

And I know it looks like a bunch of changes, but it’s mostly just taking advantage of the existing library – your semantics stay the same.

@@ -0,0 +1 @@
λ(f : Type → Type) → λ(a : Type) → ∀(r : Type) → (a → r) → (f r → r) → r
Copy link
Owner

Choose a reason for hiding this comment

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

This should now look like

λ(f : Type  Type)  λ(a : Type)  ../../Mu/Type (../../FreeF/Type f a)

Also, it doesn’t exist on master yet, but I’ve started putting types like this (i.e., recursive types) into a “data/” directory, so this would then be “data/Free/Type”. There are some examples of this on the kind-polymorphism branch.

Copy link
Author

@mstksg mstksg Jan 10, 2019

Choose a reason for hiding this comment

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

Hm, even though the two types are the same, I think there's some benefit in separating the fixed-point from the base functor. Basically like how we have List a and XNor, and we have Natural with Maybe, and we have NonEmpty and AndMaybe. And one benefit in implementing Free directly is that we can work directly with Free as a "normal function", instead of importing an intermediate data type to use it.

(edit: Although, now that I think about it, merge doesn't require importing constructor names, so it'd be possible to "make" a Free value without importing FreeF, but you'd need to import FreeF to use a Free value.)

In the end it might just be a stylistic thing, and I think there could be benefits both ways.

Free/functor Outdated
→ λ(kf : f r → r)
→ m r (λ(x : a) → kp (g x)) kf
}
: Functor (./Type f)
Copy link
Owner

Choose a reason for hiding this comment

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

And this can be

  λ(f : Type  Type)
 ../../Mu/functor (../../FreeF/Type f) (../../FreeF/bifunctor f)

Copy link
Author

Choose a reason for hiding this comment

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

Trying this out with the alternative representation; this doesn't quite work because it requires Functor f. We can implement it directly using the FreeF implementation, but we just can't do it through Mu FreeF, unfortunately :(

Free/recursive Outdated
(λ(x : a) → alg ((FreeF f a b).Pure x))
(λ(x : f b) → alg ((FreeF f a b).Free x))
}
: ./../Recursive/Type (./Type f a) (FreeF f a)
Copy link
Owner

Choose a reason for hiding this comment

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

This can be

  λ(f : Type  Type)
 λ(a : Type)
  ../../Mu/recursive (../../FreeF/Type f a)

Free/retract Outdated
monad
).join

in λ(m : ./Type f a) → m (f a) (monad.pure a) (join a)
Copy link
Owner

Choose a reason for hiding this comment

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

  λ(f : Type  Type)
 λ(monad : Monad f) -- note instance immediately after type
 λ(a : Type)
 ./recursive.cata
  a
  (  λ(free : ../../FreeF/Type f a (f a))
    merge { Pure = monad.pure, Free = join } free)

and the merge would be cleaner if I ever got my changes into the spec, then it could be

 ./recursive.cata (f a) (merge { Pure = monad.pure, Free = join })

Copy link
Owner

Choose a reason for hiding this comment

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

Basically, anything that applies m is going to instead do ./recursive.cata … merge

Free/retract Outdated Show resolved Hide resolved
Free/steppable Outdated
embed
(./../FreeF/functor f functor a)
}
: ./../Steppable/Type (./Type f a) (FreeF f a)
Copy link
Owner

Choose a reason for hiding this comment

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

  λ(f : Type  Type)
 λ(a : Type)
  ../../Mu/steppable (../../FreeF/Type f a)

FreeF/functor Outdated Show resolved Hide resolved
Free/foldFree Outdated
in λ(a : Type)
→ λ(n : ∀(x : Type) → f x → g x)
→ λ(m : ./Type f a)
→ m (g a) (monad.pure a) (λ(q : f (g a)) → join a (n (g a) q))
Copy link
Owner

Choose a reason for hiding this comment

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

This should probably look similar to the Free/retract one I proposed.

FreeF/Type Show resolved Hide resolved
@mstksg
Copy link
Author

mstksg commented Jan 15, 2019

I've made some stylistic adjustments re: imports, and added bifunctor for FreeF.

I've held back from changing Free f to be Mu (FreeF f a), since we can't actually implement most things in terms of Mu, which requires Functor f for most things. It also complicates functor a lot even if we write it in scratch without going through Mu/functor. However, I did take some inspiration using Mu's instances to implement Free's instances; I added toMu and fromMu, which are used to implement recursive and steppable in terms of Mu's instances. This is fine because recursive and steppable already require Functor anyway, and there's no way to get around the constraint even working with the original representation.

@sellout
Copy link
Owner

sellout commented Jan 16, 2019

@mstksg Sorry, I didn’t mean to let this languish after your previous response, but I haven’t had a chance to get back to it.

To address your most recent point:

The requirement of Functor f is a bug. I have a different branch with a better Mu/functor, but even that currently requires Functor f in the case of FreeF, because it uses Bifunctor/first, and FreeF/bifunctor (correctly) requires Functor f.

Instead, Mu/functor should require Functor (\(a : Type) -> f a b) which can be satisfied by FreeF/bifunctor, or can be implemented directly, which would allow us to avoid the Functor f requirement imposed by FreeF/bifunctor.

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