<div>I'm -1 on any kind of <code>Map = NEMap</code>.</div><br><div>An ordinary map and a non-empty map are semantically different. I believe that if I non-empty maps were already in <code>containers</code>, I would pretty much always care whether a <code>Map</code> I see in code is a 0-map or 1-map.</div><br><div>Similarly, I prefer <code>Int</code> and <code>Word</code> instead of <code>Int</code> and <code>Unsigned.Int</code>. (Luckily that's already the case.)</div><br><div>We already have a precedent with <code>Text</code> and <code>ByteString</code>, where the lazy and the strict versions are only distinguished by the module prefix. In my experience, modules where both are used are pretty common, and I end up just introducing <code>type LByteString = Lazy.ByteString</code> in all my projects, because otherwise I need to scroll to the imports section whenever I need to know which flavor of bytestring is being used. (Or if I'm reading haddocks, I have to look at the link because Haddock hides module prefixes.)</div><br><div>"why not both" is even worse. I still can't trust the <code>Map</code>, but now I also have to learn and remember that two modules are the same. Speaking from experience again – most people seem to be surprised by the fact that <code>Data.Map.Lazy</code> and <code>Data.Map.Strict</code> export the same <code>Map</code> type. The proposed module hierarchy would move <code>containers</code> to the top of my "packages that confuse beginners" list, beating even <code>aeson</code>.</div><br><div>As an aside, I wish we had a proper interface for container-like structures, or at least a solution to name scoping. I really like the way Rust does it, for instance, where certain functions can be "attached" to a type – I'm hesitant to call them "methods" because Rust is not an OOP language.</div><div class="gmail_quote_attribution">On Apr 25 2019, at 2:49 pm, Mario Blažević <mblazevic@stilo.com> wrote:</div><blockquote><div><div>On 2019-04-18 11:00 p.m., David Feuer wrote:</div><blockquote><div>I'm in favor of the proposal. I find the isomorphism between Map (a,b) v</div><div>and Map a (NonemptyMap b v) very pleasant. The fact that others have</div><div>written less-performant implementations of this idea is rather</div><div>convincing. The fact that doing this removes partial matches in the</div><div>implementation is nice. And I'll take performance improvements where I</div><div>can get them. The main question is the proper name of the type. Just</div><div>Data.Map.Nonempty.Map, or .NonemptyMap? Should the empty be capitalized?</div></blockquote><br><div>There seems to be a consensus for Data.Map.NonEmpty.NEMap, with the</div><div>type and the functions slightly off the regular ones. This design would</div><div>make it easier to use regular and non-empty containers together, but it</div><div>be annoying for the use case of replacing all uses of an existing</div><div>regular container with a non-empty one. I'd rather change just the</div><div>import declaration than all occurrences of the type name and functions.</div><br><div>I don't want to derail the implementation with bikeshedding, so I'm</div><div>just going to ask why not both? The library can both export the tweaked</div><div>names and add a module, say Data.NonEmpty.Map.Lazy, that exports the</div><div>type synonym Map = NEMap. It would also rename all the functions back to</div><div>their names from Data.Map.Lazy.</div><br><br><blockquote><br><div>On Thu, Apr 18, 2019, 7:15 PM John Cotton Ericson</div><div><John.Ericson@obsidian.systems> wrote:</div><br><div>In https://github.com/haskell/containers/issues/608 I proposed</div><div>adding non-empty variants of Map and Set, analogous to</div><div>Data.List.NonEmpty for List, to containers. semigroupoids</div><div>demonstrates the many uses and structure of non-empty containers in</div><div>general, and libraries such as</div><div>https://github.com/mstksg/nonempty-containers and</div><div>https://github.com/andrewthad/non-empty-containers demonstrate the</div><div>interest in non-empty maps and sets in particular. My favorite</div><div>use-case is that they're needed to "curry" containers: for example,</div><div>|Map (k0, k1) v| is isomorphic not to |Map k0 (Map k1 v)| but to</div><div>|Map k0 (NonEmptyMap k1 v)|. I like this use-case because it comes</div><div>from the containers themselves.</div><br><div>Importantly, there's no good way to do this outside of containers;</div><div>doing so leads to imbalancing / extra indirection, or massive code</div><div>duplication. If one wraps the container was an extra value like</div><div>Data.List.NonEmpty, one's left with an unavoidable extra</div><div>indirection/imbalance. One can rectify this by copying and modifying</div><div>the implementation of containers, but that's hardly maintainable;</div><div>even as though the algorithms are the same, enough lines are touched</div><div>that merging upstream containers is nigh impossible.</div><br><div>On the other hand, the non-empty containers can be elegantly and</div><div>sufficiently implemented alongside their originals by taking the Bin</div><div>constructor and breaking it out into it's own type, mutually</div><div>recursive with the original. This avoids the indirection/imbalancing</div><div>and code duplication problems: the algorithms work exactly as before</div><div>creating the same trees (remember the UNPACK), and no code</div><div>duplicated since the functions become mutually recursive matching</div><div>the types.</div><br><div>To briefly summarize the thread:</div><br><div>1. I proposed the issue after performing this same refactor on the</div><div>dependent-map package:</div><div>https://github.com/obsidiansystems/dependent-map/tree/non-empty,</div><div>a fork of containers.</div><div>2. I made https://github.com/haskell/containers/pull/616 which just</div><div>changes the types, to make sure UNPACK preserved the importance.</div><div>3. https://gist.github.com/Ericson2314/58709d0d99e0c0e83ad266701cd71841</div><div>the benchmarks showed rather than degrading performance, PR 616</div><div>actually /improved/ it.</div><br><div> If there is preliminary consensus, I'll make a second PR on top</div><div>which generalizes the functions like on my dependent-map branch.</div><br><div>Thanks,</div><br><div>John</div><br><div>_______________________________________________</div><div>Libraries mailing list</div><div>Libraries@haskell.org <mailto:Libraries@haskell.org></div><div>http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries</div><br><br><div>_______________________________________________</div><div>Libraries mailing list</div><div>Libraries@haskell.org</div><div>http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries</div></blockquote><br><div>_______________________________________________</div><div>Libraries mailing list</div><div>Libraries@haskell.org</div><div>http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries</div></div></blockquote>