containers icon indicating copy to clipboard operation
containers copied to clipboard

Inconsistent usage of `==` and `=` equality in docs

Open wikku opened this issue 4 years ago • 8 comments

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 =.

wikku avatar Jul 16 '20 19:07 wikku

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.

treeowl avatar Jul 16 '20 19:07 treeowl

@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

sjakobi avatar Jul 17 '20 13:07 sjakobi

@m-renaud I'm curious about your thoughts on this.

sjakobi avatar Jul 17 '20 13:07 sjakobi

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]]

Doesn't that prevent us from hyperlinking anything in the example? Seems bad.

treeowl avatar Jul 17 '20 14:07 treeowl

And no, I do not want to put a quintillion fromLists in there. map is fine.

treeowl avatar Jul 17 '20 14:07 treeowl

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 fromLists 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 fromLists 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 fromLists.

sjakobi avatar Jul 18 '20 11:07 sjakobi

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.

m-renaud avatar Jul 18 '20 17:07 m-renaud

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.

treeowl avatar Jul 18 '20 18:07 treeowl