
I've updated the bimap package to version 0.2. http://hackage.haskell.org/cgi-bin/hackage-scripts/package/bimap-0.2 The main difference is a pretty comprehensive interface shakeup: the Either variants have been dropped, all L variants have had the L removed from their name, and a few functions have been curried. The end result is an interface much closer to that of Data.Map. (This also means that 0.2 isn't backwards-compatible.) In addition, the package now supports GHC 6.8, and has a few more tests in the test suite. Stuart

Hi
The main difference is a pretty comprehensive interface shakeup: the Either variants have been dropped, all L variants have had the L removed from their name, and a few functions have been curried. The end result is an interface much closer to that of Data.Map. (This also means that 0.2 isn't backwards-compatible.)
Good, this seems much nicer. A few comments: 1) You depend on MTL, but I can't obviously see why. Perhaps the test suite? 2) The code: {-| Insert a pair of values into the bimap, without checking for overlapping bindings. If either value is already in the bimap, and is not bound to the other value, the bimap will become inconsistent. -} unsafeInsert :: (Ord a, Ord b) => a -> b -> Bimap a b -> Bimap a b unsafeInsert x y (MkBimap left right) = MkBimap (M.insert x y left) (M.insert y x right) To my understanding, this is always safe, since insert will overwrite any existing element in a map. Hence you don't need to do the delete's first. In addition, since Bimap is not exported internally, the invariant will never be violated. 3) insert x y = delete x >>> deleteR y >>> unsafeInsert x y Why not: insert x y = unsafeInsert x y . delete x . delete y Now you don't end up using the arrow combinators, and it becomes more readable (at least to me). Of course, this function may disappear entirely if what I wrote in (2) is correct. I will probably start using the bimap package in my current project, so you already have at least one user :-) Thanks Neil

Hello Neil, Tuesday, February 5, 2008, 1:11:47 PM, you wrote:
insert x y = delete x >>> deleteR y >>> unsafeInsert x y
i use the following trick: (.$) = flip ($) insert x y it = it.$ delete x .$ deleteR y .$ unsafeInsert x y -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

2008/2/5, Neil Mitchell
3) insert x y = delete x >>> deleteR y >>> unsafeInsert x y
Why not:
insert x y = unsafeInsert x y . delete x . delete y
Now you don't end up using the arrow combinators, and it becomes more readable (at least to me). Of course, this function may disappear entirely if what I wrote in (2) is correct.
I'd rather prefer the arrow combinator: it let me read the composition in natural order. Sure, ">>>" is more verbose than ".", and less idiomatic, but I tend to think it scale a bit more (in the case of bigger compositions). Well, maybe just a matter of taste... Loup

On Tue, Feb 5, 2008 at 9:11 PM, Neil Mitchell
Hi
The main difference is a pretty comprehensive interface shakeup: the Either variants have been dropped, all L variants have had the L removed from their name, and a few functions have been curried. The end result is an interface much closer to that of Data.Map. (This also means that 0.2 isn't backwards-compatible.)
Good, this seems much nicer. A few comments:
1) You depend on MTL, but I can't obviously see why. Perhaps the test suite?
The current implementation of (!) relies on the Monad instance for Either exported by Control.Monad.Error. There's no fundamental reason for this; it was just easier to code. Perhaps I'll get rid of it in a later version, but I haven't bothered yet because I don't think an MTL dependency is a big deal.
2) The code:
{-| Insert a pair of values into the bimap, without checking for overlapping bindings. If either value is already in the bimap, and is not bound to the other value, the bimap will become inconsistent. -} unsafeInsert :: (Ord a, Ord b) => a -> b -> Bimap a b -> Bimap a b unsafeInsert x y (MkBimap left right) = MkBimap (M.insert x y left) (M.insert y x right)
To my understanding, this is always safe, since insert will overwrite any existing element in a map. Hence you don't need to do the delete's first. In addition, since Bimap is not exported internally, the invariant will never be violated.
Consider the Bimap { (1, "alfa") }. If I do (unsafeInsert 1 "bravo"), the left-hand map will contain: { (1 => "bravo") } and the right-hand map will contain: { ("alfa" => 1), ("bravo" => 1) } If I then do (lookupR "alfa"), I'll incorrectly get (Just 1) instead of (Nothing). Heck, let me prove it to you -- here's what happens if I define (insert = unsafeInsert): prop_clobberR : Falsifiable, after 0 tests: fromList [(-1,1)] 0 prop_valid : Falsifiable, after 12 tests: fromList [(1,-5),(5,-5)] It is true that at least one of the deletes will always be unnecessary, but since a failed delete does nothing we might as well leave both of them in. The export of (valid) is mostly a debugging aid, so that users can check whether their problems are caused by my code.
3) insert x y = delete x >>> deleteR y >>> unsafeInsert x y
Why not:
insert x y = unsafeInsert x y . delete x . delete y
Now you don't end up using the arrow combinators, and it becomes more readable (at least to me). Of course, this function may disappear entirely if what I wrote in (2) is correct.
This is a matter of taste, I guess. In this case I felt that the "backwards" order of (.) was a little misleading, and I'm personally quite comfortable with using (>>>) for forward composition.
I will probably start using the bimap package in my current project, so you already have at least one user :-)
Glad to hear it, and thanks for your feedback. Stuart

Hi
1) You depend on MTL, but I can't obviously see why. Perhaps the test suite?
The current implementation of (!) relies on the Monad instance for Either exported by Control.Monad.Error. There's no fundamental reason for this; it was just easier to code. Perhaps I'll get rid of it in a later version, but I haven't bothered yet because I don't think an MTL dependency is a big deal.
Yes, an MTL dependency is nothing to worry about at all, and isn't worth even thinking about removing given its actually used.
Heck, let me prove it to you -- here's what happens if I define (insert = unsafeInsert):
Ah, I see now. You are of course correct.
3) insert x y = delete x >>> deleteR y >>> unsafeInsert x y
Why not:
insert x y = unsafeInsert x y . delete x . delete y
Now you don't end up using the arrow combinators, and it becomes more readable (at least to me). Of course, this function may disappear entirely if what I wrote in (2) is correct.
This is a matter of taste, I guess. In this case I felt that the "backwards" order of (.) was a little misleading, and I'm personally quite comfortable with using (>>>) for forward composition.
Fair enough. You've obviously thought about the issues in this package a great deal, which gives me a nice happy feeling when using the package! Thanks Neil

Neil Mitchell wrote:
Yes, an MTL dependency is nothing to worry about at all, and isn't worth even thinking about removing given its actually used.
I would appreciate haskell98 portability! We have a similar module, too, and would switch to a standard (if bimap becomes it). We've called it "injective maps". Does surjectivity make sense a all? Our other names are bad, but maybe "transpose" or "inverse" is better than "twist" (viewing maps as finite functions). Our delete function takes both values as input, possibly deleting two entries, but your two delete functions make more sense. http://www.dfki.de/sks/hets/src-distribution/daily/Hets/docs/Common-InjMap.h... or http://www.dfki.de/sks/hets/src-distribution/versions/Hets/docs/Common-InjMa... Cheers Christian

On Tue, Feb 5, 2008 at 11:33 PM, Christian Maeder
Neil Mitchell wrote:
Yes, an MTL dependency is nothing to worry about at all, and isn't worth even thinking about removing given its actually used.
I would appreciate haskell98 portability!
My development version has removed the need for Control.Monad.Exception and Control.Arrow. The only remaining H98 incompatibility I can think of is the use of foldl' in fromList.
We've called it "injective maps". Does surjectivity make sense a all? Our other names are bad, but maybe "transpose" or "inverse" is better than "twist" (viewing maps as finite functions).
In my mind, surjectivity is the property that each key in the right-hand map is a value in the left-hand map (and vice-versa). This is related to the unsafeInsert problem I mentioned earlier. I'm open to suggesions for alternatives to "twist". I think it's cute, because it suggests transposing something without fundamentally changing it, but a less fanciful name could be a good idea.
Our delete function takes both values as input, possibly deleting two entries, but your two delete functions make more sense.
Incidentally, someone might find it useful to have a two-argument delete defined as deletePair :: (Ord a, Ord b) => a -> b -> Bimap a b -> Bimap a b deletePair x y bi | (x, y) `pairMember` bi = delete x bi | otherwise = bi but it's easy enough to define that I probably don't need to provide it myself.
http://www.dfki.de/sks/hets/src-distribution/daily/Hets/docs/Common-InjMap.h... or http://www.dfki.de/sks/hets/src-distribution/versions/Hets/docs/Common-InjMa...
The getAToB and getBToA functions are interesting. I'm thinking of adding them as toMap and toMapR. Stuart

On Wed, Feb 6, 2008 at 11:43 AM, Stuart Cook
My development version has removed the need for Control.Monad.Exception and Control.Arrow. The only remaining H98 incompatibility I can think of is the use of foldl' in fromList.
Version 0.2.1 features: * almost-H98 compatibility (foldl' and hierarchical modules) * toMap/toMapR * big-O in function comments * version info in function comments Stuart

Stuart Cook wrote:
wrote: We've called it "injective maps". Does surjectivity make sense a all?
In my mind, surjectivity is the property that each key in the right-hand map is a value in the left-hand map (and vice-versa). This
Still, injectivity ("one-to-one") is the only interesting property. The inverse (on the range) is then injective, too. Christian
participants (5)
-
Bulat Ziganshin
-
Christian Maeder
-
Loup Vaillant
-
Neil Mitchell
-
Stuart Cook