-
Notifications
You must be signed in to change notification settings - Fork 178
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
Implement toList and toNonEmpty for SCC #1057
Conversation
The laziness bug slipped in in #987. |
containers/src/Data/Graph.hs
Outdated
@@ -220,11 +220,18 @@ instance F.Foldable SCC where | |||
foldr c n (AcyclicSCC v) = c v n | |||
foldr c n (NECyclicSCC vs) = foldr c n vs | |||
|
|||
null _ = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine GHC can figure this out from the default, but I agree it's nice to make it clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently it doesn't. However, I realized that we're making null
lazier here. The default definition using foldr
would need to inspect the SCC
. Is this the right thing to do? I'm not sure now, though many Foldable
instances in base do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh. I hate those kinds of decisions. I think the change is probably fine, but must be logged. null
isn't normally used to force computations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some thought, I feel it's best to let this lie. It is very unlikely someone is using null
on an SCC
, so this is unlikely to help someone with performance. But in case they are, this is an unnecessary breaking change to deal with.
Let me know if you feel otherwise.
I've said it before and I'll say it again: |
Isn't this just a case of functions being too lazy? I don't see a problem with the definition of the type. What would you prefer? |
Modulo naming, I would personally prefer data NE a = End a | a :< NE a
instance Foldable1 NE where
-- foldrMap1 :: (a -> b) -> (a -> b -> b) -> NE a -> b
foldrMap1 e _c (End a) = e a
foldrMap1 e c (a :< as) = a `c` foldrMap1 e c as There would still be judgement calls for some things, but I suspect there would be more "natural" defaults. There are two options for uncons1, uncons2 :: NE a -> (a, Maybe (NE a))
uncons1 (End a) = (a, Nothing)
uncons1 (a :< as) = (a, Just as)
uncons2 q = (a, r)
where
(a, r) = uncons1 q Doesn't Zips are a bit more subtle, because we have the option of being somewhat lazy in the second list. Let's recons for convenience: recons :: a -> Maybe (NE a) -> NE a
recons a Nothing = End a
recons a (Just as) = a :< as
zipWith1 f (uncons1 -> (a, mas)) (uncons1 -> (b, mbs)) = recons (f a b) $ do
as <- mas
bs <- mbs
Just $ zipWith1 f as bs
zipWith2 f (uncons1 -> (a, mas)) (uncons1 -> ~(b, mbs)) = recons (f a b) $ do
as <- mas
bs <- mbs
Just $ zipWith2 f as bs
Obviously, there's no objective way to choose between one and another, but the relationship between |
I see, certainly this type is better suited for some situations, though I'm not convinced that it should be the default. |
The default implementations perform an avoidable list copy for NECyclicSCC. Also fix flattenSCC being made too lazy accidentally in a previous commit.
The default implementations perform an avoidable list copy for NECyclicSCC. Also
Implement null, which is always False