containers
containers copied to clipboard
Inconsistent usage of `==` and `=` equality in docs
While reading the docs for Data.Set, I noticed that in some places ==
is used and in some =
is used. For example, the docs for powerSet
:
Calculate the power set of a set: the set of all its subsets.
t `member` powerSet s == t `isSubsetOf` s
Example:
powerSet (fromList [1,2,3]) = fromList $ map fromList [[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]
I guess that =
should be used only if we can prove it from the definitions, and only if both sides are "internally" equal. For instance, we can disprove the above example easily:
Prelude Data.Set> putTree = putStrLn . showTree
Prelude Data.Set> putTree $ powerSet (fromList [1,2,3])
fromList [1,3]
+--fromList [1,2]
| +--fromList [1]
| | +--fromList []
| | +--|
| +--fromList [1,2,3]
+--fromList [2,3]
+--fromList [2]
+--fromList [3]
Prelude Data.Set> putTree $ fromList $ Prelude.map fromList [[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]
fromList [1,2,3]
+--fromList [1]
| +--fromList []
| +--fromList [1,2]
+--fromList [2]
+--fromList [1,3]
+--fromList [2,3]
+--|
+--fromList [3]
The examples for cartesianProduct
and disjointUnion
can be disproved similarly. From looking around, other modules like IntSet and Map modules also have this issue.
I suppose every nontrivial example/law equation with containers on both sides should use ==
instead of =
.
I don't think we want to draw such fine distinctions. Internal structural equality is really Not Interesting, and we never make any promises about it.
@wiktorkuchta Thanks for bringing this up! :)
I think your interpretation does indicate that the docs are currently somewhat confusing and could be more consistent.
powerSet (fromList [1,2,3]) = fromList $ map fromList [[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]
I think this was meant as a simple example. In order to avoid repeating fromList
over and over again, the map fromList
shorthand was used.
For examples, I think we should adopt the doctest
style that is also increasingly being used in base
and other libraries. In this style, the example should look like this
>>> powerSet (fromList [1,2,3])
fromList [fromList [],fromList [1],fromList [1,2],fromList [1,2,3],fromList [1,3],fromList [2],fromList [2,3],fromList [3]]
This is very long though, so I would shorten it to
>>> powerSet (fromList [1,2])
fromList [fromList [],fromList [1],fromList [1,2],fromList [2]]
For laws and properties, I think we should standardize on the style that is being used for example in the docs for Applicative
:
t `member` powerSet s = t `isSubsetOf` s
@m-renaud I'm curious about your thoughts on this.
For examples, I think we should adopt the
doctest
style that is also increasingly being used inbase
and other libraries. In this style, the example should look like this>>> powerSet (fromList [1,2,3]) fromList [fromList [],fromList [1],fromList [1,2],fromList [1,2,3],fromList [1,3],fromList [2],fromList [2,3],fromList [3]]
Doesn't that prevent us from hyperlinking anything in the example? Seems bad.
And no, I do not want to put a quintillion fromList
s in there. map
is fine.
Doesn't that prevent us from hyperlinking anything in the example? Seems bad.
If we want hyperlinking, something like this should work:
-- @
-- >>> 'powerSet' ('fromList' [1,2,3])
-- fromList ...
-- @
I haven't needed this so far though.
And no, I do not want to put a quintillion
fromList
s in there.map
is fine.
I think the advantage of doctest
style examples is that you easily replicate them on the REPL yourself. The analogy to REPL sessions makes them easy to understand.
Something like the following example should IMHO be avoided because it breaks with the concept of doctests and would probably cause confusion:
>>> powerSet (fromList [1,2,3])
fromList $ map fromList [[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]
I also don't think that having a lot of fromList
s in the output is a big problem – people are generally good at recognizing patterns, so I think they'll mostly be able to look right past the many fromList
s.
I personally find the use of =
in a codeblock to represent "equality" a little confusing. For example:
powerSet (fromList [1,2,3]) =
fromList $ map fromList [[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]
reads really strange to me since its not valid Haskell code. So, my vote would be to use ==
throughout.
I don't like ==
because these things don't necessarily have Eq
instances, and because ==
is a Bool
-valued Haskell function rather than a logical predicate. Properties are not Haskell code.