Skip to content

Add 'compose' for maps #672

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

Merged
merged 7 commits into from
Apr 16, 2020
Merged

Add 'compose' for maps #672

merged 7 commits into from
Apr 16, 2020

Conversation

alexfmpe
Copy link
Contributor

Implement #647 based on community input in mailing list.

I see most functions don't have an entry in the benchmarks. Should I add anything with this PR?

cc @Ericson2314

-- @since UNRELEASED
compose :: IntMap c -> IntMap Int -> IntMap c
compose bc ab = if null bc
then empty
Copy link
Contributor

Choose a reason for hiding this comment

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

This is surprisingly lazy in the second argument. Is that special case a good special case? I'm not so convinced yet. Please make an argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think I yet sufficiently grasp the laziness expectations in these modules to answer properly. Here's what I managed to gather after reading your question:

The strict variants say

If all values stored in all maps in the arguments are in WHNF, then all values stored in all maps in the results will be in WHNF once those maps are evaluated.

Does the same apply to keys for lazy maps given we have

A Map is strict in its keys but lazy in its values.

, and in that case ab should have its keys (and values, if strict) always evaluated if the result map is evaluated? Thus making optimizations like this one here somewhat break the interface?

Copy link
Contributor

Choose a reason for hiding this comment

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

If the first map is empty, then we don't force the second map. This seems a little weird, so maybe we should just write compose bc !ab | null bc = ..... For style, we should use guards rather than if/then here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh I see, the second argument is potentially lazier than the first

{--------------------------------------------------------------------
Compose
--------------------------------------------------------------------}
-- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of
Copy link
Contributor

Choose a reason for hiding this comment

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

That bound looks wrong. Poke around the rest of the module to see what the bounds look like.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean that it should use n & m like the other functions? I strayed from that because other bounds seem to be commutative in their arguments, so they don't need to say which argument map is n and which is m, but compose is asymmetric

Copy link
Contributor

Choose a reason for hiding this comment

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

No, I mean that IntMap performance doesn't tend to involve logarithms of the size; instead, it usually relies on the word size. This is because the depth of the tree is not bounded by the logarithm of the total size. Check the bounds for (!?), which you use here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, makes sense

compose bc ab = if null bc
then empty
else mapMaybe (bc !?) ab

Copy link
Contributor

Choose a reason for hiding this comment

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

Same correction and comments apply here.

{--------------------------------------------------------------------
Compose
--------------------------------------------------------------------}
-- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of
Copy link
Contributor

Choose a reason for hiding this comment

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

This bound is a bit funny when |bc| ≤ 1. Is it worth fixing? Not sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wouldn't that also apply to the existing O(n log n) bounds?
Big O notation is asymptotic, so in a sense there's nothing to fix.
Is there something specific about this bound you intend to make clearer? e.g. the if null bc case

@alexfmpe
Copy link
Contributor Author

After doing a few rounds of tweaks for all 4 implementations, given that they all kind of have the same implementation, I wonder if it would simplify maintenance to have a neutral/generic compose:

compose empty' lookup' bc !ab
  | null bc = empty'
  | otherwise = mapMaybe (flip lookup' bc) ab

so that the actual implementations would be something like

compose = Utils.compose empty lookup

@alexfmpe
Copy link
Contributor Author

@treeowl I think I addressed the remarks - is there anything else I should do for this PR?

@treeowl
Copy link
Contributor

treeowl commented Mar 21, 2020

I'll try to have another look tonight. I expect this to go in before the next release.

@treeowl
Copy link
Contributor

treeowl commented Apr 16, 2020

Would you please rebase? I will then merge. Sorry it's been a bit.

@treeowl treeowl merged commit e661937 into haskell:master Apr 16, 2020
@treeowl
Copy link
Contributor

treeowl commented Apr 16, 2020

Thanks a lot!

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