/usr/share/doc/libghc-pipes-parse-doc/html/src/Pipes-Parse-Tutorial.html is in libghc-pipes-parse-doc 3.0.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<!-- Generated by HsColour, http://code.haskell.org/~malcolm/hscolour/ -->
<title>src/Pipes/Parse/Tutorial.hs</title>
<link type='text/css' rel='stylesheet' href='hscolour.css' />
</head>
<body>
<pre><a name="line-1"></a><span class='hs-comment'>{-# OPTIONS_GHC -fno-warn-unused-imports #-}</span>
<a name="line-2"></a>
<a name="line-3"></a><span class='hs-comment'>{-| @pipes-parse@ builds upon @pipes@ to add several missing features necessary
<a name="line-4"></a> to implement 'Parser's:
<a name="line-5"></a>
<a name="line-6"></a> * End-of-input detection, so that 'Parser's can react to an exhausted input
<a name="line-7"></a> stream
<a name="line-8"></a>
<a name="line-9"></a> * Leftovers support, which simplifies several parsing problems
<a name="line-10"></a>
<a name="line-11"></a> * Connect-and-resume, to connect a 'Producer' to a 'Parser' and retrieve
<a name="line-12"></a> unused input
<a name="line-13"></a>-}</span>
<a name="line-14"></a>
<a name="line-15"></a><span class='hs-keyword'>module</span> <span class='hs-conid'>Pipes</span><span class='hs-varop'>.</span><span class='hs-conid'>Parse</span><span class='hs-varop'>.</span><span class='hs-conid'>Tutorial</span> <span class='hs-layout'>(</span>
<a name="line-16"></a> <span class='hs-comment'>-- * Overview</span>
<a name="line-17"></a> <span class='hs-comment'>-- $overview</span>
<a name="line-18"></a>
<a name="line-19"></a> <span class='hs-comment'>-- * Parsers</span>
<a name="line-20"></a> <span class='hs-comment'>-- $parsers</span>
<a name="line-21"></a>
<a name="line-22"></a> <span class='hs-comment'>-- * Lenses</span>
<a name="line-23"></a> <span class='hs-comment'>-- $lenses</span>
<a name="line-24"></a>
<a name="line-25"></a> <span class='hs-comment'>-- * Getters</span>
<a name="line-26"></a> <span class='hs-comment'>-- $getters</span>
<a name="line-27"></a>
<a name="line-28"></a> <span class='hs-comment'>-- * Building Lenses</span>
<a name="line-29"></a> <span class='hs-comment'>-- $buildlenses</span>
<a name="line-30"></a>
<a name="line-31"></a> <span class='hs-comment'>-- * Conclusion</span>
<a name="line-32"></a> <span class='hs-comment'>-- $conclusion</span>
<a name="line-33"></a> <span class='hs-layout'>)</span> <span class='hs-keyword'>where</span>
<a name="line-34"></a>
<a name="line-35"></a><span class='hs-keyword'>import</span> <span class='hs-conid'>Pipes</span>
<a name="line-36"></a><span class='hs-keyword'>import</span> <span class='hs-conid'>Pipes</span><span class='hs-varop'>.</span><span class='hs-conid'>Parse</span>
<a name="line-37"></a>
<a name="line-38"></a><span class='hs-comment'>{- $overview
<a name="line-39"></a> @pipes-parse@ centers on three abstractions:
<a name="line-40"></a>
<a name="line-41"></a> * 'Producer's, unchanged from @pipes@
<a name="line-42"></a>
<a name="line-43"></a> * 'Parser's, which play a role analogous to 'Consumer's
<a name="line-44"></a>
<a name="line-45"></a> * 'Lens.Family2.Lens''es between 'Producer's, which play a role analogous to
<a name="line-46"></a> 'Pipe's
<a name="line-47"></a>
<a name="line-48"></a> There are four ways to connect these three abstractions:
<a name="line-49"></a>
<a name="line-50"></a> * Connect 'Parser's to 'Producer's using 'runStateT' \/ 'evalStateT' \/
<a name="line-51"></a> 'execStateT':
<a name="line-52"></a>
<a name="line-53"></a>> runStateT :: Parser a m r -> Producer a m x -> m (r, Producer a m x)
<a name="line-54"></a>> evalStateT :: Parser a m r -> Producer a m x -> m r
<a name="line-55"></a>> execStateT :: Parser a m r -> Producer a m x -> m ( Producer a m x)
<a name="line-56"></a>
<a name="line-57"></a>
<a name="line-58"></a> * Connect 'Lens.Family2.Lens''es to 'Parser's using
<a name="line-59"></a> 'Lens.Family.State.Strict.zoom'
<a name="line-60"></a>
<a name="line-61"></a>> zoom :: Lens' (Producer a m x) (Producer b m y)
<a name="line-62"></a>> -> Parser b m r
<a name="line-63"></a>> -> Parser a m r
<a name="line-64"></a>
<a name="line-65"></a> * Connect 'Producer's to 'Lens.Family2.Lens''es using ('Lens.Family.^.') or
<a name="line-66"></a> 'Lens.Family.view':
<a name="line-67"></a>
<a name="line-68"></a>> (^.) :: Producer a m x
<a name="line-69"></a>> -> Lens' (Producer a m x) (Producer b m y)
<a name="line-70"></a>> -> Producer b m y
<a name="line-71"></a>
<a name="line-72"></a> * Connect 'Lens.Family2.Lens''es to 'Lens.Family2.Lens''es using ('.') (i.e.
<a name="line-73"></a> function composition):
<a name="line-74"></a>
<a name="line-75"></a>> (.) :: Lens' (Producer a m x) (Producer b m y)
<a name="line-76"></a>> -> Lens' (Producer b m y) (Producer c m z)
<a name="line-77"></a>> -> Lens' (Producer a m x) (Producer c m z)
<a name="line-78"></a>
<a name="line-79"></a> You can obtain the necessary lens utilities from either:
<a name="line-80"></a>
<a name="line-81"></a> * The @lens-family-core@ library, importing @Lens.Family@ (for
<a name="line-82"></a> ('Lens.Family.^.') \/ 'Lens.Family.view' and 'Lens.Family.over') and
<a name="line-83"></a> @Lens.Family.State.Strict@ (for 'Lens.Family.State.Strict.zoom'), or:
<a name="line-84"></a>
<a name="line-85"></a> * The @lens@ library, importing @Control.Lens@ (for ('Control.Lens.^.') \/
<a name="line-86"></a> 'Control.Lens.view', 'Control.Lens.over' and 'Control.Lens.zoom')
<a name="line-87"></a>
<a name="line-88"></a> This tutorial uses @Lens.Family@ since it has fewer dependencies and simpler
<a name="line-89"></a> types.
<a name="line-90"></a>-}</span>
<a name="line-91"></a>
<a name="line-92"></a><span class='hs-comment'>{- $parsers
<a name="line-93"></a> 'Parser's handle end-of-input and pushback by storing a 'Producer' in a
<a name="line-94"></a> 'StateT' layer:
<a name="line-95"></a>
<a name="line-96"></a>> type Parser a m r = forall x . StateT (Producer a m x) m r
<a name="line-97"></a>
<a name="line-98"></a> To draw a single element from the underlying 'Producer', use the 'draw'
<a name="line-99"></a> command:
<a name="line-100"></a>
<a name="line-101"></a>> draw :: Monad m => Parser a m (Maybe a)
<a name="line-102"></a>
<a name="line-103"></a> 'draw' returns the next element from the 'Producer' wrapped in 'Just' or
<a name="line-104"></a> returns 'Nothing' if the underlying 'Producer' is empty. Here's an example
<a name="line-105"></a> 'Parser' written using 'draw' that retrieves the first two elements from a
<a name="line-106"></a> stream:
<a name="line-107"></a>
<a name="line-108"></a>> import Pipes.Parse
<a name="line-109"></a>>
<a name="line-110"></a>> drawTwo :: Monad m => Parser a m (Maybe a, Maybe a)
<a name="line-111"></a>> drawTwo = do
<a name="line-112"></a>> mx <- draw
<a name="line-113"></a>> my <- draw
<a name="line-114"></a>> return (mx, my)
<a name="line-115"></a>>
<a name="line-116"></a>> -- or: drawTwo = liftM2 (,) draw draw
<a name="line-117"></a>
<a name="line-118"></a> Since a 'Parser' is just a 'StateT' action, you run a 'Parser' using the
<a name="line-119"></a> same run functions as 'StateT':
<a name="line-120"></a>
<a name="line-121"></a>> -- Feed a 'Producer' to a 'Parser', returning the result and leftovers
<a name="line-122"></a>> runStateT :: Parser a m r -> Producer a m x -> m (r, Producer a m x)
<a name="line-123"></a>>
<a name="line-124"></a>> -- Feed a 'Producer' to a 'Parser', returning only the result
<a name="line-125"></a>> evalStateT :: Parser a m r -> Producer a m x -> m r
<a name="line-126"></a>>
<a name="line-127"></a>> -- Feed a 'Producer' to a 'Parser', returning only the leftovers
<a name="line-128"></a>> execStateT :: Parser a m r -> Producer a m x -> m ( Producer a m x)
<a name="line-129"></a>
<a name="line-130"></a> All three of these functions require a 'Producer' which we feed to the
<a name="line-131"></a> 'Parser'. For example, we can feed standard input:
<a name="line-132"></a>
<a name="line-133"></a>>>> evalStateT drawTwo Pipes.Prelude.stdinLn
<a name="line-134"></a>Pink<Enter>
<a name="line-135"></a>Elephants<Enter>
<a name="line-136"></a>(Just "Pink",Just "Elephants")
<a name="line-137"></a>
<a name="line-138"></a> The result is wrapped in a 'Maybe' because 'draw' can fail if the 'Producer'
<a name="line-139"></a> is empty:
<a name="line-140"></a>
<a name="line-141"></a>>>> evalStateT drawTwo (yield 0)
<a name="line-142"></a>(Just 0,Nothing)
<a name="line-143"></a>
<a name="line-144"></a> Parsing might not necessarily consume the entire stream. We can use
<a name="line-145"></a> 'runStateT' or 'execStateT' to retrieve unused elements that our parser does
<a name="line-146"></a> not consume:
<a name="line-147"></a>
<a name="line-148"></a>>>> import Pipes
<a name="line-149"></a>>>> (result, unused) <- runStateT drawTwo (each [1..4])
<a name="line-150"></a>>>> -- View the parsed result
<a name="line-151"></a>>>> result
<a name="line-152"></a>(Just 1,Just 2)
<a name="line-153"></a>>>> -- Now print the leftovers
<a name="line-154"></a>>>> runEffect $ for unused (lift . print)
<a name="line-155"></a>3
<a name="line-156"></a>4
<a name="line-157"></a>
<a name="line-158"></a>-}</span>
<a name="line-159"></a>
<a name="line-160"></a><span class='hs-comment'>{- $lenses
<a name="line-161"></a> @pipes-parse@ also provides a convenience function for testing purposes that
<a name="line-162"></a> draws all remaining elements and returns them as a list:
<a name="line-163"></a>
<a name="line-164"></a>> drawAll :: Monad m => Parser a m [a]
<a name="line-165"></a>
<a name="line-166"></a> For example:
<a name="line-167"></a>
<a name="line-168"></a>>>> import Pipes
<a name="line-169"></a>>>> import Pipes.Parse
<a name="line-170"></a>>>> evalStateT drawAll (each [1..10])
<a name="line-171"></a>[1,2,3,4,5,6,7,8,9,10]
<a name="line-172"></a>
<a name="line-173"></a> However, this function is not recommended in general because it loads the
<a name="line-174"></a> entire input into memory, which defeats the purpose of streaming parsing.
<a name="line-175"></a>
<a name="line-176"></a> You can instead use 'foldAll' if you wish to fold all input elements into a
<a name="line-177"></a> single result:
<a name="line-178"></a>
<a name="line-179"></a>>>> evalStateT (foldAll (+) 0 id) (each [1..10])
<a name="line-180"></a>55
<a name="line-181"></a>
<a name="line-182"></a> You can also use the @foldl@ package to simplify writing more complex folds:
<a name="line-183"></a>
<a name="line-184"></a>>>> import Control.Applicative
<a name="line-185"></a>>>> import Control.Foldl as L
<a name="line-186"></a>>>> evalStateT (purely foldAll (liftA2 (,) L.sum L.maximum)) (each [1..10])
<a name="line-187"></a>(55,Just 10)
<a name="line-188"></a>
<a name="line-189"></a> But what if you wanted to draw or fold just the first three elements from
<a name="line-190"></a> an infinite stream instead of the entire input? This is what lenses are
<a name="line-191"></a> for:
<a name="line-192"></a>
<a name="line-193"></a>> import Lens.Family
<a name="line-194"></a>> import Lens.Family.State.Strict
<a name="line-195"></a>> import Pipes
<a name="line-196"></a>> import Pipes.Parse
<a name="line-197"></a>>
<a name="line-198"></a>> import Prelude hiding (splitAt, span)
<a name="line-199"></a>>
<a name="line-200"></a>> drawThree :: Monad m => Parser a m [a]
<a name="line-201"></a>> drawThree = zoom (splitAt 3) drawAll
<a name="line-202"></a>
<a name="line-203"></a> 'Lens.Family.State.Strict.zoom' lets you delimit a 'Parser' using a
<a name="line-204"></a> 'Lens.Family2.Lens''. The above code says to limit 'drawAll' to a subset of
<a name="line-205"></a> the input, in this case the first three elements:
<a name="line-206"></a>
<a name="line-207"></a>>>> evalStateT drawThree (each [1..])
<a name="line-208"></a>[1,2,3]
<a name="line-209"></a>
<a name="line-210"></a> 'splitAt' is a 'Lens.Family2.Lens'' with the following type:
<a name="line-211"></a>
<a name="line-212"></a>> splitAt
<a name="line-213"></a>> :: Monad m
<a name="line-214"></a>> => Int -> Lens' (Producer a m x) (Producer a m (Producer a m x))
<a name="line-215"></a>
<a name="line-216"></a> The easiest way to understand 'splitAt' is to study what happens when you
<a name="line-217"></a> use it as a getter:
<a name="line-218"></a>
<a name="line-219"></a>> view (splitAt 3) :: Producer a m x -> Producer a m (Producer a m x)
<a name="line-220"></a>
<a name="line-221"></a> In this context, @(splitAt 3)@ behaves like 'splitAt' from the Prelude,
<a name="line-222"></a> except instead of splitting a list it splits a 'Producer'. Here's an
<a name="line-223"></a> example of how you can use 'splitAt':
<a name="line-224"></a>
<a name="line-225"></a>> outer :: Monad m => Producer Int m (Producer Int m ())
<a name="line-226"></a>> outer = each [1..6] ^. splitAt 3
<a name="line-227"></a>
<a name="line-228"></a> The above definition of @outer@ is exactly equivalent to:
<a name="line-229"></a>
<a name="line-230"></a>> outer = do
<a name="line-231"></a>> each [1..3]
<a name="line-232"></a>> return (each [4..6])
<a name="line-233"></a>
<a name="line-234"></a> We can prove this by successively running the outer and inner 'Producer'
<a name="line-235"></a> layers:
<a name="line-236"></a>
<a name="line-237"></a>>>> -- Print all the elements in the outer layer and return the inner layer
<a name="line-238"></a>>>> inner <- runEffect $ for outer (lift . print)
<a name="line-239"></a>1
<a name="line-240"></a>2
<a name="line-241"></a>3
<a name="line-242"></a>>>> -- Now print the elements in the inner layer
<a name="line-243"></a>>>> runEffect $ for inner (lift . print)
<a name="line-244"></a>4
<a name="line-245"></a>5
<a name="line-246"></a>6
<a name="line-247"></a>
<a name="line-248"></a> We can also uses lenses to modify 'Parser's, using
<a name="line-249"></a> 'Lens.Family.State.Strict.zoom'. When we combine
<a name="line-250"></a> 'Lens.Family.State.Strict.zoom' with @(splitAt 3)@ we limit a parser to the
<a name="line-251"></a> the first three elements of the stream. When the parser is done
<a name="line-252"></a> 'Lens.Family.State.Strict.zoom' also returns unused elements back to the
<a name="line-253"></a> original stream. We can demonstrate this using the following example
<a name="line-254"></a> parser:
<a name="line-255"></a>
<a name="line-256"></a>> splitExample :: Monad m => Parser a m ([a], Maybe a, [a])
<a name="line-257"></a>> splitExample = do
<a name="line-258"></a>> x <- zoom (splitAt 3) drawAll
<a name="line-259"></a>> y <- zoom (splitAt 3) draw
<a name="line-260"></a>> z <- zoom (splitAt 3) drawAll
<a name="line-261"></a>> return (x, y, z)
<a name="line-262"></a>
<a name="line-263"></a> The second parser begins where the first parser left off:
<a name="line-264"></a>
<a name="line-265"></a>>>> evalStateT splitExample (each [1..])
<a name="line-266"></a>([1,2,3],Just 4,[5,6,7])
<a name="line-267"></a>
<a name="line-268"></a> 'span' behaves the same way, except that it uses a predicate and takes as
<a name="line-269"></a> many consecutive elements as possible that satisfy the predicate:
<a name="line-270"></a>
<a name="line-271"></a>> spanExample :: Monad m => Parser Int m (Maybe Int, [Int], Maybe Int)
<a name="line-272"></a>> spanExample = do
<a name="line-273"></a>> x <- zoom (span (>= 4)) draw
<a name="line-274"></a>> y <- zoom (span (< 4)) drawAll
<a name="line-275"></a>> z <- zoom (span (>= 4)) draw
<a name="line-276"></a>> return (x, y, z)
<a name="line-277"></a>
<a name="line-278"></a> Note that even if the first parser fails, subsequent parsers can still
<a name="line-279"></a> succeed because they operate under a different lens:
<a name="line-280"></a>
<a name="line-281"></a>>>> evalStateT spanExample (each [1..])
<a name="line-282"></a>(Nothing,[1,2,3],Just 4)
<a name="line-283"></a>
<a name="line-284"></a> You can even nest 'Lens.Family.State.Strict.zoom's, too:
<a name="line-285"></a>
<a name="line-286"></a>> nestExample :: Monad m => Parser Int m (Maybe Int, [Int], Maybe Int)
<a name="line-287"></a>> nestExample = zoom (splitAt 2) spanExample
<a name="line-288"></a>
<a name="line-289"></a> All the parsers from @spanExample@ now only see a subset of the input,
<a name="line-290"></a> namely the first two elements:
<a name="line-291"></a>
<a name="line-292"></a>>>> evalStateT nestExample (each [1..])
<a name="line-293"></a>(Nothing,[1,2],Nothing)
<a name="line-294"></a>
<a name="line-295"></a>-}</span>
<a name="line-296"></a>
<a name="line-297"></a><span class='hs-comment'>{- $getters
<a name="line-298"></a> Not all transformations are reversible. For example, consider the following
<a name="line-299"></a> contrived function:
<a name="line-300"></a>
<a name="line-301"></a>> import Pipes
<a name="line-302"></a>> import qualified Pipes.Prelude as P
<a name="line-303"></a>>
<a name="line-304"></a>> map' :: Monad m => (a -> b) -> Producer a m r -> Producer b m r
<a name="line-305"></a>> map' f p = p >-> P.map f
<a name="line-306"></a>
<a name="line-307"></a> Given a function of type @(a -> b)@, we can transform a stream of @a@'s into
<a name="line-308"></a> a stream of @b@'s, but not the other way around. Transformations which are
<a name="line-309"></a> not reversible and cannot be modeled as 'Pipe's can only be modeled as
<a name="line-310"></a> functions between 'Producer's. However, 'Pipe's are preferable to functions
<a name="line-311"></a> between 'Producer's when possible because 'Pipe's can transform both
<a name="line-312"></a> 'Producer's and 'Consumer's.
<a name="line-313"></a>
<a name="line-314"></a> If you prefer, you can use lens-like syntax for functions between
<a name="line-315"></a> 'Producer's by promoting them to @Getter@s using 'Lens.Family.to':
<a name="line-316"></a>
<a name="line-317"></a>> import Lens.Family
<a name="line-318"></a>>
<a name="line-319"></a>> example :: Monad m => Producer Int m ()
<a name="line-320"></a>> example = each [1..3] ^. to (map' (*2))
<a name="line-321"></a>
<a name="line-322"></a> However, a function of 'Producer's (or the equivalent @Getter@) cannot be
<a name="line-323"></a> used transform 'Parser's (using 'Lens.Family.State.Strict.zoom' or
<a name="line-324"></a> otherwise) . This reflects the fact that such a transformation cannot be
<a name="line-325"></a> applied in reversed.
<a name="line-326"></a>-}</span>
<a name="line-327"></a>
<a name="line-328"></a><span class='hs-comment'>{- $buildlenses
<a name="line-329"></a> Lenses are very easy to write if you are willing to depend on either the
<a name="line-330"></a> @lens-family@ or @lens@ library. Both of these libraries provide an
<a name="line-331"></a> 'Lens.Family2.Unchecked.iso' function that you can use to assemble your own
<a name="line-332"></a> lenses. You only need two functions which reversibly transform back and
<a name="line-333"></a> forth between a stream of @a@s and a stream of @b@s:
<a name="line-334"></a>
<a name="line-335"></a>> -- "Forward"
<a name="line-336"></a>> fw :: Producer a m x -> Producer b m y
<a name="line-337"></a>>
<a name="line-338"></a>> -- "Backward"
<a name="line-339"></a>> bw :: Producer b m y -> Producer a m x
<a name="line-340"></a>
<a name="line-341"></a> ... such that:
<a name="line-342"></a>
<a name="line-343"></a>> fw . bw = id
<a name="line-344"></a>>
<a name="line-345"></a>> bw . fw = id
<a name="line-346"></a>
<a name="line-347"></a> You can then convert them to a 'Lens.Family2.Lens'' using
<a name="line-348"></a> 'Lens.Family2.Unchecked.iso':
<a name="line-349"></a>
<a name="line-350"></a>> import Lens.Family2 (Lens')
<a name="line-351"></a>> import Lens.Family2.Unchecked (iso)
<a name="line-352"></a>>
<a name="line-353"></a>> lens :: Lens' (Producer a m x) (Producer b m y)
<a name="line-354"></a>> lens = iso fw bw
<a name="line-355"></a>
<a name="line-356"></a> You can even do this without incurring any dependencies if you rewrite the
<a name="line-357"></a> above code like this:
<a name="line-358"></a>
<a name="line-359"></a>> -- This type synonym requires the 'RankNTypes' extension
<a name="line-360"></a>> type Lens' a b = forall f . Functor f => (b -> f b) -> (a -> f a)
<a name="line-361"></a>>
<a name="line-362"></a>> lens :: Lens' (Producer a m x) (Producer b m y)
<a name="line-363"></a>> lens k p = fmap bw (k (fw p))
<a name="line-364"></a>
<a name="line-365"></a> This is what @pipes-parse@ does internally, and you will find several
<a name="line-366"></a> examples of this pattern in the source code of the "Pipes.Parse" module.
<a name="line-367"></a>
<a name="line-368"></a> Lenses defined using either approach will work with both the @lens@ and
<a name="line-369"></a> @lens-family@ libraries.
<a name="line-370"></a>-}</span>
<a name="line-371"></a>
<a name="line-372"></a><span class='hs-comment'>{- $conclusion
<a name="line-373"></a> @pipes-parse@ introduces core idioms for @pipes@-based parsing. These
<a name="line-374"></a> idioms reuse 'Producer's, but introduce two new abstractions:
<a name="line-375"></a> 'Lens.Family2.Lens''es and 'Parser's.
<a name="line-376"></a>
<a name="line-377"></a> This library is very minimal and only contains datatype-agnostic parsing
<a name="line-378"></a> utilities, so this tutorial does not explore the full range of parsing
<a name="line-379"></a> tricks using lenses. For example, you can also use lenses to change the
<a name="line-380"></a> element type.
<a name="line-381"></a>
<a name="line-382"></a> Several downstream libraries provide more specific functionality, including:
<a name="line-383"></a>
<a name="line-384"></a> * @pipes-binary@: Lenses and parsers for @binary@ values
<a name="line-385"></a>
<a name="line-386"></a> * @pipes-attoparsec@: Converts @attoparsec@ parsers to @pipes@ parsers
<a name="line-387"></a>
<a name="line-388"></a> * @pipes-aeson@: Lenses and parsers for JSON values
<a name="line-389"></a>
<a name="line-390"></a> * @pipes-bytestring@: Lenses and parsers for byte streams
<a name="line-391"></a>
<a name="line-392"></a> * @pipes-text@: Lenses and parsers for text encodings
<a name="line-393"></a>
<a name="line-394"></a> To learn more about @pipes-parse@, ask questions, or follow development, you
<a name="line-395"></a> can subscribe to the @haskell-pipes@ mailing list at:
<a name="line-396"></a>
<a name="line-397"></a> <https://groups.google.com/forum/#!forum/haskell-pipes>
<a name="line-398"></a>
<a name="line-399"></a> ... or you can mail the list directly at:
<a name="line-400"></a>
<a name="line-401"></a> <mailto:haskell-pipes@googlegroups.com>
<a name="line-402"></a>-}</span>
</pre></body>
</html>
|