Closed natefaubion closed 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)?
It's still useful when you don't care about it being lazy, I think.
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.
@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
?
@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.
Yes something like that. Would that let us keep the constructor hidden for your use case?
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.
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).
I'm fine with that too 👍
Closing in favor of #92
Fixes #90