purescript / purescript-free

Free monads, Cofree comonads, Yoneda and Coyoneda functors, and the Trampoline monad.
BSD 3-Clause "New" or "Revised" License
93 stars 27 forks source link

Export `Cofree` constructor #91

Closed natefaubion closed 6 years ago

natefaubion commented 6 years ago

Fixes #90

paulyoung commented 6 years ago

Ignoring backwards compatibility for a moment, is there a some benefit to keeping mkCofree around as opposed to using the Cofree constructor everywhere instead (including the :< operator)?

garyb commented 6 years ago

It's still useful when you don't care about it being lazy, I think.

paf31 commented 6 years ago

Thinking about this a bit more, what about adding a Lazy instance, and optionally moving the a under the Lazy? It needs one anyway if we want to express fixed points.

paulyoung commented 6 years ago

@natefaubion any thoughts on Phil's suggestion?

Does this make any difference to the use case we were discussing of implementing a union function using unfoldCofree?

natefaubion commented 6 years ago

@paf31 You mean something like

newtype Cofree f a = Cofree (Lazy (Tuple a (Cofree f a))

instance lazyCofree :: Lazy (Cofree f a) where
  defer k = Cofree $ Z.defer \_ ->
    let (Cofree thunk) = k unit in Z.force thunk

@paulyoung no this would make no difference to what we talked about. A lazy instance lets you create cycles in the graph.

paf31 commented 6 years ago

Yes something like that. Would that let us keep the constructor hidden for your use case?

natefaubion commented 6 years ago

How lazy would you want it:

newtype Cofree f a = Cofree (Lazy (Tuple a (f (Cofree f a))))
newtype Cofree f a = Cofree (Lazy (Tuple a (Lazy (f (Cofree f a)))))

The second is an awful lot of boxing, but you may not want to strictly assemble the f. Free doesn't use any sort of laziness, so maybe the first is appropriate.

natefaubion commented 6 years ago

I would rather then just export a function like: cofree :: forall f a. Functor f => (Unit -> Tuple a (f (Cofree f a))) -> Cofree f a Because otherwise using defer would always result in an extra unnecessary box that's immediately discarded. That's one of my frustrations with some Lazy instances (like for Lazy itself).

paf31 commented 6 years ago

I'm fine with that too 👍

natefaubion commented 6 years ago

Closing in favor of #92