• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

bench/H14-Aug-2019-4032

src/Data/H14-Aug-2019-4,6572,170

test/H25-Dec-2019-505423

ChangeLog.mdH A D25-Dec-20194.9 KiB178104

LICENSEH A D14-Aug-20191.1 KiB2117

README.mdH A D25-Dec-201912.4 KiB299199

Setup.hsH A D14-Aug-201946 32

mono-traversable.cabalH A D25-Dec-20192.2 KiB9790

README.md

1mono-traversable
2================
3
4Type classes for mapping, folding, and traversing monomorphic and polymorphic containers.
5Haskell is good at operating over polymorphic containers such as a list `[a]`.
6A monomorphic container is one such as Text which has a type `Text` that does not expose a type variable for the underlying characters.
7
8mono-traversable also adds
9
10  * `IsSequence`, etc for operating over sequential data types
11  * `IsSet`, `IsMap`, etc for unifying set and map APIs
12  * `NonNull` for making partial functions (head, tail) total
13
14In addition to this package, the
15[mono-traversable-instances](https://www.stackage.org/package/mono-traversable-instances)
16package provides a number of orphan instances.
17
18
19Using Typeclasses
20-----------------
21
22There are 2 use cases for mono-traversable: application authors and library authors.
23
24### Library authors
25
26As a library author, if you want to allow a user to pass in a `Text` or a `String`,
27then you need to expose an API with a mono-traversable typeclass.
28You should think twice about using mono-traversable though because
29
30* Using Typeclasses makes type inference more difficult. It is usually better to force the user to give a `Text`. Another option is to just have multiple APIs.
31* If you are operating on polymorphic structures in which the normal typeclasses suffice, you should just use them from base. On the other hand, even if you are using polymorphic containers you may want to leverage `IsSequence` or `MinLen`.
32
33### Application authors
34
35As an application author, you should consider using classy-prelude, which leans heavily on mono-traversable.
36
37When writing your own function signatures, you should default to making them concrete: if you are actually using a list, then make your function take a list rather than an `IsSequence`. This will improve type inference, error messages, and make your code easier to understand. When you decide to use a `Vector` instead of a list, change the type signature to use a `Vector`. When you actually need a function to both accept a `Vector` and a list, it is easy to change the function signature to the more abstract typeclasses that you require.
38
39
40Standard Typeclasses
41--------------------
42
43in the upcoming GHC 7.10, using `Functor`, `Foldable`, and `Traversable` will become common-place. This means that rather than using `List.map`, `Vector.map`, etc, the map from the prelude will work on all data types that are a Functor. Of course, you can already do this now using `fmap`.
44
45For a Haskeller, it is important to understand `Functor`, `Applicative`, `Monad`, `Foldable`, and `Monoid`: these are encountered in every day code. For mono-traversable, it is most important to understand [Foldable](https://www.haskell.org/haskellwiki/Typeclassopedia#Foldable).
46
47mono-traversable Typeclasses
48----------------------------
49
50### MonoFunctor
51
52Same as Functor, but cannot change the type.
53
54``` haskell
55type family   Element mono
56type instance Element Text = Char
57type instance Element [a] = a
58```
59
60Element is a type family. This tells the compiler to substitute `Char` for `Element Text`.
61We can create this rule for every monomorphic container we want to operate on such as `Text`
62And we can also create it for a polymorphic container.
63
64Now lets compare MonoFunctor to the normal Functor.
65
66``` haskell
67fmap :: Functor f => (a -> b) -> f a -> f b
68omap :: MonoFunctor mono => (Element mono -> Element mono) -> mono -> mono
69```
70
71So there is no type-change from `a` to `b`, the contained type must stay the same (`Element mono -> Element mono`).
72
73Here is the MonoFunctor typeclass definition
74
75``` haskell
76class MonoFunctor mono where
77    omap :: (Element mono -> Element mono) -> mono -> mono
78    default omap :: (Functor f, Element (f a) ~ a, f a ~ mono) => (a -> a) -> f a -> f a
79    omap = fmap
80```
81
82And we can write some instances
83
84``` haskell
85instance MonoFunctor T.Text where
86    omap = T.map
87
88instance MonoFunctor [a]
89```
90
91The list definition was able to default to using `fmap` so no body was needed.
92
93
94### MonoFoldable
95
96Same as Foldable, but also operates over monomorphic containers.
97
98MonoFoldable is the heart of the power of mono-traversable (and arguably the package should be named mono-foldable) because anything that can be done with `Foldable` can be done with `MonoFoldable`.
99The reason why is that a monomorphic container can never change its type.
100So `omap` is a restricted `fmap`.
101However, folding generates a *new* structure, so we have no such concerns.
102In the classy-prelude package, map is set to `fmap` and omap must be used separately.
103However, foldMap is set to just use the mono-traversable version: `ofoldMap`
104
105``` haskell
106class Foldable t where
107  foldMap :: Monoid m => (a -> m) -> t a -> m
108  foldr   :: (a -> b -> b) -> b -> t a -> b
109  ...
110
111class MonoFoldable mono where
112  ofoldMap :: Monoid m => (Element mono -> m) -> mono -> m
113  ofoldr :: (Element mono -> b -> b) -> b -> mono -> b
114  ...
115```
116
117There are additional Typeclasses which build on MonoFoldable
118
119``` haskell
120class (MonoFoldable mono, Monoid mono) => MonoFoldableMonoid mono where
121    oconcatMap :: (Element mono -> mono) -> mono -> mono
122
123class (MonoFoldable mono, Ord (Element mono)) => MonoFoldableOrd mono where
124    maximumEx :: mono -> Element mono
125    minimumEx :: mono -> Element mono
126
127class MonoPointed mono where
128    opoint :: Element mono -> mono
129```
130
131MonoPointed abstracts over the concept of a singleton. For any `Applicative`, `opoint` is the same as `pure` from Applicative. Since mono-traversable did not bother with a `MonoApplicative` typeclass, we added `MonoPointed` to still have the functionality of `pure`.
132
133
134### MonoTraversable
135
136`MonoTraversable` is `Traversable` for monomorphic containers, just as
137`MonoFunctor` is `Functor` for monomorphic containers.
138
139``` haskell
140class (Functor t, Foldable t) => Traversable t where
141  traverse  :: Applicative f => (a -> f b) -> t a -> f (t b)
142  ...
143
144class (MonoFunctor mono, MonoFoldable mono) => MonoTraversable mono where
145  otraverse :: Applicative f => (Element mono -> f (Element mono)) -> mono -> f mono
146  ...
147```
148
149
150### Containers
151
152* SetContainer: unifies operations across `Set` and `Map`
153* PolyMap: differenceMap and intersectionMap
154* IsSet: unifies operations across different `Set`s
155* IsMap: unifies operations across different `Map`s
156* MonoZip: zip operations on MonoFunctors.
157
158Note that because `Set` is not a Functor (and therefore neither a MonoFunctor nor MonoTraversable), one must use `setFromList` and `setToList` to `omap` or `otraverse` over the elements of a `Set`.
159
160
161### Sequences
162
163`IsSequence` contains list-like operations.
164
165``` haskell
166-- | Sequence Laws:
167--
168-- > fromList . otoList = id
169-- > fromList (x <> y) = fromList x <> fromList y
170-- > otoList (fromList x <> fromList y) = x <> y
171class (Monoid seq, MonoTraversable seq, SemiSequence seq, MonoPointed seq) => IsSequence seq where
172    fromList :: [Element seq] -> seq
173    break :: (Element seq -> Bool) -> seq -> (seq, seq)
174    ...
175```
176
177The laws state that an IsSequence is a list-like (sequential) structure.
178
179* an `IsSequence` is not just something that can be converted to a list (`MonoFoldable`), but something that can be created from a list.
180* Converting to and from a list does not change the `IsSequence`, and it doesn't even change the `IsSequence` if you do the conversions on chunks of the `IsSequence`.
181
182SemiSequence is required by IsSequence. It is conceptually the same as IsSequence, but contains operations that can also be used on a `NonEmpty` or a `MinLen` (which are SemiGroups) because they do not reduce the number of elements in the sequence.
183
184
185There are some more typeclasess that build on top of IsSequence.
186
187``` haskell
188class (IsSequence seq, Eq (Element seq)) => EqSequence seq where
189class (EqSequence seq, MonoFoldableOrd seq) => OrdSequence seq where
190class (IsSequence t, IsString t, Element t ~ Char) => Textual t where
191    words :: t -> [t]
192    unwords :: [t] -> t
193    lines :: t -> [t]
194    unlines :: [t] -> t
195    toLower :: t -> t
196    toUpper :: t -> t
197    ...
198```
199
200Textual functions are always safe to use with Unicode (it is possible to misuse other functions that operate on individual characters).
201
202
203### MinLen
204
205Did you notice minimumEx and maximumEx from above? Ex stands for 'Exception'.
206An exception will occur if you call minimumEx on an empty list.
207MinLen is a tool to guarantee that this never occurs, and instead to prove that there are one or more elements in your list.
208
209``` haskell
210minimumEx :: MonoFoldable mono => mono -> Element mono
211
212-- | like Data.List, but not partial on a MonoFoldable
213minimum :: MonoFoldableOrd mono => MinLen (Succ nat) mono -> Element mono
214minimum = minimumEx . unMinLen
215
216newtype MinLen nat mono = MinLen { unMinLen :: mono }
217    deriving (Eq, Ord, Read, Show, Data, Typeable, Functor)
218
219-- Type level naturals
220data Zero = Zero
221data Succ nat = Succ nat
222```
223
224The `minimum` function exposed from `MinLen` is very similar to `minimumEx`, but has a `MinLen` wrapper that ensures it will never throw an exception.
225`MinLen` is a newtype with a phantom type that contains information about the minimum number of elements we know are in the structure. That is done through type-level Peano numbers.
226
227What do we know about the input to minimum? If nat is Zero, then it reduces to `MinLen (Succ Zero) mono`. Succ means successor, and the successor of 0 is 1, so the data structure has a minimum length of 1.
228
229Lets see this in practice
230
231``` haskell
232> minimum []
233<interactive>:3:9:
234    Couldn't match expected type ‘MinLen (Succ nat0) mono’
235                with actual type ‘[t0]’
236
237
238> minimum [1,2,3]
239-- same error as above
240
241> minimum (toMinList (3 :| [2,1]))
2421
243> minimum (3 `mlcons` toMinLenZero [2,1])
2441
245```
246
247Here we used Data.List.NonEmpty combined with toMinList or we just work with a List and prove through the usage of cons that it has more than one element.
248
249
250
251Adding instances
252----------------
253
254If you have a _polymorphic_ data type which is a member of one of the relevant typeclasses ([Functor](http://hackage.haskell.org/package/base/docs/Data-Functor.html),
255[Foldable](http://hackage.haskell.org/package/base/docs/Data-Foldable.html),
256[Traversable](http://hackage.haskell.org/package/base/docs/Data-Traversable.html)), it's quite easy to add an instance for
257[MonoFunctor](https://hackage.haskell.org/package/mono-traversable/docs/Data-MonoTraversable.html#t:MonoFunctor), [MonoFoldable](https://hackage.haskell.org/package/mono-traversable/docs/Data-MonoTraversable.html#t:MonoFoldable) or [MonoTraversable](https://hackage.haskell.org/package/mono-traversable/docs/Data-MonoTraversable.html#t:MonoTraversable).
258
259You just have to declare the proper type instance:
260
261``` haskell
262{-# LANGUAGE TypeFamilies         #-}
263
264type instance Element (CustomType a) = a
265```
266
267And then, we can use the default implementation to declare instances:
268
269``` haskell
270instance MonoFunctor (CustomType a)
271instance MonoFoldable (CustomType a)
272instance MonoTraversable (CustomType a)
273```
274
275Now you are ready to use ```CustomType a``` with the functions defined in this package.
276
277**Note**: if your type is a _monomorphic_ container without the proper typeclasses, then you will have to provide an implementation rather than using the default. However, this should be fairly simple, as can be seen [in the code](https://github.com/snoyberg/mono-traversable/blob/d85e4ed3c11afec2d49c0f4fe2812122a279e5d4/src/Data/MonoTraversable.hs#L428)
278
279
280mono-traversable versus lens Traversal
281--------------------------------------
282
283lens is a library with a lot of functionality covering a variety of patterns. One piece of functionality it exposes is `Fold` and `Traversal` which can also be used to deal with monomorphic containers.
284
285You could prefer mono-traversable to using this part of lens because
286
287* Familiar API - If you know `Foldable`, you can use `MonoFoldable` just as easily
288* mono-traversable's typeclass based approach means many methods are included in the class but can be given specialised optimized implementations
289* You don't explicitly pass around the `Traversal`
290
291The last point is also a point of inflexibility and points to a use case where you could prefer using a lens `Traversal`. mono-traversable treats `ByteString` as a sequence of bytes. If you want to treat it as both bytes and characters, mono-traversable would require a newtype wrapper around `ByteString`, whereas a lens `Traversal` would use a different traversal function.
292
293mono-traversable is only an alternative for `Fold` and `Traversal`, not for `Lens`, `Prism`, `Iso`, `Getter`, `Setter`, `Review`, or `Equality`.
294
295
296
297
298[![Build Status](https://secure.travis-ci.org/snoyberg/mono-traversable.png)](http://travis-ci.org/snoyberg/mono-traversable)
299