@@ -118,8 +118,8 @@ When we are creating and combining `Fetch` values, we are just constructing a re
118118dependencies.
119119
120120``` tut:silent
121- import cats.Id
122121import fetch.implicits._
122+ import fetch.syntax._
123123
124124val fetchUser: Fetch[User] = getUser(1)
125125```
@@ -137,14 +137,14 @@ Note that Fetch provides `MonadError` instances for a variety of different monad
137137Let's run our first fetch!
138138
139139``` tut:book
140- val result: User = Fetch.run[Id](fetchUser)
140+ val result: User = fetchUser.runA[Eval].value
141141```
142142
143143In the previous examples, we:
144144
145- - brought the implicit instance of ` MonadError[Id , Throwable] ` into scope importing ` fetch.implicits._ `
145+ - brought the implicit instance of ` MonadError[Eval , Throwable] ` into scope importing ` fetch.implicits._ `
146146- created a fetch for a ` User ` using the ` getUser ` function
147- - interpreted the fetch to a ` Id [User]` (which is just a ` User ` ) using ` Fetch.run `
147+ - interpreted the fetch to a ` Eval [User]` using the syntax ` runA ` that delegate to ` Fetch.run `
148148
149149As you can see, the fetch was executed in one round to fetch the user and was finished after that.
150150
@@ -162,7 +162,7 @@ val fetchTwoUsers: Fetch[(User, User)] = for {
162162When composing fetches with ` flatMap ` we are telling Fetch that the second one depends on the previous one, so it isn't able to make any optimizations. When running the above fetch, we will query the user data source in two rounds: one for the user with id 1 and another for the user with id 2.
163163
164164``` tut:book
165- val result: (User, User) = Fetch.run[Id](fetchTwoUsers)
165+ val result: (User, User) = fetchTwoUsers.runA[Eval].value
166166```
167167
168168### Batching
@@ -180,7 +180,7 @@ val fetchProduct: Fetch[(User, User)] = getUser(1).product(getUser(2))
180180Note how both ids (1 and 2) are requested in a single query to the data source when executing the fetch.
181181
182182``` tut:book
183- val result: (User, User) = Fetch.run[Id](fetchProduct)
183+ val result: (User, User) = fetchProduct.runA[Eval].value
184184```
185185
186186### Deduplication
@@ -194,7 +194,7 @@ val fetchDuped: Fetch[(User, User)] = getUser(1).product(getUser(1))
194194Note that when running the fetch, the identity 1 is only requested once even when it is needed by both fetches.
195195
196196``` tut:book
197- val result: (User, User) = Fetch.run[Id](fetchDuped)
197+ val result: (User, User) = fetchDuped.runA[Eval].value
198198```
199199
200200### Caching
@@ -214,7 +214,7 @@ val fetchCached: Fetch[(User, User)] = for {
214214The above fetch asks for the same identity multiple times. Let's see what happens when executing it.
215215
216216``` tut:book
217- val result: (User, User) = Fetch.run[Id](fetchCached)
217+ val result: (User, User) = fetchCached.runA[Eval].value
218218```
219219
220220As you can see, the ` User ` with id 1 was fetched only once in a single round-trip. The next
@@ -257,9 +257,9 @@ implicit object PostSource extends DataSource[PostId, Post]{
257257def getPost(id: PostId): Fetch[Post] = Fetch(id)
258258
259259val postInfoDatabase: Map[PostId, PostInfo] = Map(
260- 1 -> PostInfo("monad "),
261- 2 -> PostInfo("applicative "),
262- 3 -> PostInfo("monad ")
260+ 1 -> PostInfo("Run Wild, Run Free "),
261+ 2 -> PostInfo("American Psycho "),
262+ 3 -> PostInfo("Torrente 3 ")
263263)
264264
265265implicit object PostInfoSource extends DataSource[PostId, PostInfo]{
@@ -292,7 +292,7 @@ val fetchMulti: Fetch[(Post, User)] = for {
292292We can now run the previous fetch, querying the posts data source first and the user data source afterwards.
293293
294294``` tut:book
295- val result: (Post, User) = Fetch.run[Id](fetchMulti)
295+ val result: (Post, User) = fetchMulti.runA[Eval].value
296296```
297297
298298In the previous example, we fetched a post given its id and then fetched its author. This
@@ -316,7 +316,7 @@ val fetchConcurrent: Fetch[(Post, User)] = getPost(1).product(getUser(2))
316316The above example combines data from two different sources, and the library knows they are independent.
317317
318318``` tut:book
319- val result: (Post, User) = Fetch.run[Id](fetchConcurrent)
319+ val result: (Post, User) = fetchConcurrent.runA[Eval].value
320320```
321321
322322Since we are interpreting the fetch to the ` Id ` monad, that doesn't give us any parallelism; the fetches
@@ -344,7 +344,7 @@ val fetchSequence: Fetch[List[User]] = List(getUser(1), getUser(2), getUser(3)).
344344Since ` sequence ` uses applicative operations internally, the library is able to perform optimizations across all the sequenced fetches.
345345
346346``` tut:book
347- val result: List[User] = Fetch.run[Id](fetchSequence)
347+ val result: List[User] = fetchSequence.runA[Eval].value
348348```
349349
350350As you can see, requests to the user data source were batched, thus fetching all the data in one round.
@@ -360,7 +360,7 @@ val fetchTraverse: Fetch[List[User]] = List(1, 2, 3).traverse(getUser)
360360As you may have guessed, all the optimizations made by ` sequence ` still apply when using ` traverse ` .
361361
362362``` tut:book
363- val result: List[User] = Fetch.run[Id](fetchTraverse)
363+ val result: List[User] = fetchTraverse.runA[Eval].value
364364```
365365
366366# Interpreting a fetch to an async capable monad
@@ -390,12 +390,11 @@ val fetchParallel: Fetch[(User, Post)] = (getUser(1) |@| getPost(1)).tupled
390390We can now interpret a fetch into a future:
391391
392392``` tut:book
393- val fut: Future[(User, Post)] = Fetch.run [Future](fetchParallel)
393+ val fut: Future[(User, Post)] = fetchParallel.runA [Future]
394394Await.result(fut, 1 seconds) // this call blocks the current thread, don't do this at home!
395395```
396396
397- Since futures run in a thread pool, we need to explicitly set println output to the standard output. Note how both requests
398- to the data sources run in parallel, each in its own logical thread.
397+ Since futures run in a thread pool, both requests to the data sources run in parallel, each in its own logical thread.
399398
400399# Caching
401400
@@ -415,7 +414,7 @@ val cache = InMemoryCache(UserSource.identity(1) -> User(1, "@dialelo"))
415414We can pass a cache as the second argument when running a fetch with ` Fetch.run ` .
416415
417416``` tut:book
418- val result: User = Fetch.run[Id](fetchUser, cache)
417+ val result: User = fetchUser.runA[Eval]( cache).value
419418```
420419
421420As you can see, when all the data is cached, no query to the data sources is executed since the results are available
@@ -428,23 +427,23 @@ val fetchManyUsers: Fetch[List[User]] = List(1, 2, 3).traverse(getUser)
428427If only part of the data is cached, the cached data won't be asked for:
429428
430429``` tut:book
431- val result: List[User] = Fetch.run[Id](fetchManyUsers, cache)
430+ val result: List[User] = fetchManyUsers.runA[Eval]( cache).value
432431```
433432
434433## Replaying a fetch without querying any data source
435434
436435When running a fetch, we are generally interested in its final result. However, we also have access to the cache
437436and information about the executed rounds once we run a fetch. Fetch's interpreter keeps its state in an environment
438437(implementing the ` Env ` trait), and we can get both the environment and result after running a fetch using ` Fetch.runFetch `
439- instead of ` Fetch.run ` .
438+ instead of ` Fetch.run ` or ` value.runF ` via it's implicit syntax .
440439
441440Knowing this, we can replay a fetch reusing the cache of a previous one. The replayed fetch won't have to call any of the
442441data sources.
443442
444443``` tut:book
445- val populatedCache = Fetch.runEnv[Id](fetchManyUsers) .cache
444+ val populatedCache = fetchManyUsers.runE[Eval].value .cache
446445
447- val result: List[User] = Fetch.run[Id](fetchManyUsers, populatedCache)
446+ val result: List[User] = fetchManyUsers.runA[Eval]( populatedCache).value
448447```
449448
450449## Implementing a custom cache
@@ -481,7 +480,7 @@ val myCache = MyInMemoryCache(Map(UserSource.identity(1) -> User(1, "dialelo")))
481480We can now use our implementation of the cache when running a fetch.
482481
483482``` tut:book
484- val result: User = Fetch.run[Id](fetchUser, myCache)
483+ val result: User = fetchUser.runA[Eval]( myCache).value
485484```
486485
487486# Error handling
@@ -494,17 +493,19 @@ One of the most interesting combinators is `attempt`, which given a `M[A]` yield
494493in the ` Eval ` monad to an ` Xor ` and not worry about exceptions. Let's create a fetch that always fails when executed:
495494
496495``` tut:silent
497- import cats.data.Xor
498- import fetch.implicits.evalMonadError
499-
500- val fetchError: Fetch[User] = Fetch.error(new Exception("Oh noes"))
496+ val fetchError: Fetch[User] = (new Exception("Oh noes")).fetch
501497```
502498
503499We can now use the Eval MonadError's ` attempt ` to convert a fetch result into a disjuntion and avoid throwing exceptions.
504500
505501``` tut:book
506- val result: Eval[User] = Fetch.run[Eval](fetchError)
507- val safeResult: Eval[Throwable Xor User] = evalMonadError.attempt(result)
502+ import cats.data.Xor
503+ import cats.MonadError
504+
505+ val ME = implicitly[MonadError[Eval, Throwable]]
506+
507+ val result: Eval[User] = fetchError.runA[Eval]
508+ val safeResult: Eval[Throwable Xor User] = ME.attempt(result)
508509val finalValue: Throwable Xor User = safeResult.value
509510```
510511
@@ -522,9 +523,82 @@ about the execution of the fetch.
522523
523524# Syntax
524525
526+ ## Implicit syntax
527+
528+ Fetch provides implicit syntax to lift any value to the context of a ` Fetch ` in addition to the most common used
529+ combinators active within ` Fetch ` instances.
530+
531+ ### pure
532+
533+ Plain values can be lifted to the Fetch monad with ` value.fetch ` :
534+
535+ ``` tut:silent
536+ val fetchPure: Fetch[Int] = 42.fetch
537+ ```
538+
539+ Executing a pure fetch doesn't query any data source, as expected.
540+
541+ ``` tut:book
542+ val result: Int = fetchPure.runA[Eval].value
543+ ```
544+
545+ ### error
546+
547+ Errors can also be lifted to the Fetch monad via ` exception.fetch ` . Note that interpreting
548+ an errorful fetch to ` Eval ` won't throw the exception unless we access the value with the ` .value ` method.
549+
550+ A safer way to deal with errors is to use MonadError's ` attempt ` to turn the exception into a ` Xor.Left ` value:
551+
552+ ``` tut:silent
553+ val ME = implicitly[MonadError[Eval, Throwable]]
554+
555+ val fetchFail: Fetch[Int] = (new Exception("Something went terribly wrong")).fetch[Int]
556+ val result: Eval[Int] = fetchFail.runA[Eval]
557+ val safeResult: Eval[Throwable Xor Int] = ME.attempt(result)
558+ val finalValue: Throwable Xor Int = safeResult.value
559+ ```
560+
561+ ### join
562+
563+ We can compose two independent fetches with ` fetch1.join(fetch2) ` .
564+
565+ ``` tut:silent
566+ val fetchJoined: Fetch[(Post, User)] = getPost(1).join(getUser(2))
567+ ```
568+
569+ If the fetches are to the same data source they will be batched; if they aren't, they will be evaluated at the same time.
570+
571+ ``` tut:book
572+ val result: (Post, User) = fetchJoined.runA[Eval].value
573+ ```
574+
575+ ### runA
576+
577+ Run directly any fetch to a target any target ` Monad ` with a ` MonadError ` instance in scope ` fetch1.runA[Eval] ` .
578+
579+ ``` tut:silent
580+ val post: Eval[Post] = getPost(1).runA[Eval]
581+ ```
582+
583+ ### runE
584+
585+ Extract a fetch an get it's runtime environment ` fetch1.runE[Eval] ` .
586+
587+ ``` tut:silent
588+ val env: Eval[FetchEnv] = getPost(1).runE[Eval]
589+ ```
590+
591+ ### runF
592+
593+ Run a fetch obtaining the environment and final value ` fetch1.runF[Eval] ` .
594+
595+ ``` tut:silent
596+ val env: Eval[(FetchEnv, Post)] = getPost(1).runF[Eval]
597+ ```
598+
525599## Companion object
526600
527- We've been using cats' syntax throughout the examples since it's more concise and general than the
601+ We've been using ` cats.syntax' and ` fetch. syntax` throughout the examples since it's more concise and general than the
528602methods in the ` Fetch ` companion object. However, you can use the methods in the companion object
529603directly.
530604
@@ -541,16 +615,23 @@ val fetchPure: Fetch[Int] = Fetch.pure(42)
541615Executing a pure fetch doesn't query any data source, as expected.
542616
543617``` tut:book
544- val result: Int = Fetch.run[Id ](fetchPure)
618+ val result: Int = Fetch.run[Eval ](fetchPure).value
545619```
546620
547621### error
548622
549623Errors can also be lifted to the Fetch monad, in this case with ` Fetch#error ` . Note that interpreting
550- an errorful fetch to ` Id ` will throw the exception so we won't do that:
624+ an errorful fetch to ` Eval ` won't throw the exception unless we access the value with the ` .value ` method.
625+
626+ A safer way to deal with errors is to use MonadError's ` attempt ` to turn the exception into a ` Xor.Left ` value:
627+
628+ ``` tut:book
629+ val ME = implicitly[MonadError[Eval, Throwable]]
551630
552- ``` tut:silent
553631val fetchFail: Fetch[Int] = Fetch.error(new Exception("Something went terribly wrong"))
632+ val result: Eval[Int] = fetchFail.runA[Eval]
633+ val safeResult: Eval[Throwable Xor Int] = ME.attempt(result)
634+ val finalValue: Throwable Xor Int = safeResult.value
554635```
555636
556637### join
@@ -564,7 +645,7 @@ val fetchJoined: Fetch[(Post, User)] = Fetch.join(getPost(1), getUser(2))
564645If the fetches are to the same data source they will be batched; if they aren't, they will be evaluated at the same time.
565646
566647``` tut:book
567- val result: (Post, User) = Fetch.run[Id ](fetchJoined)
648+ val result: (Post, User) = Fetch.run[Eval ](fetchJoined).value
568649```
569650
570651### sequence
@@ -579,7 +660,7 @@ val fetchSequence: Fetch[List[User]] = Fetch.sequence(List(getUser(1), getUser(2
579660Note that ` Fetch#sequence ` is not as general as the ` sequence ` method from ` Traverse ` , but performs the same optimizations.
580661
581662``` tut:book
582- val result: List[User] = Fetch.run[Id ](fetchSequence)
663+ val result: List[User] = Fetch.run[Eval ](fetchSequence).value
583664```
584665
585666### traverse
@@ -593,7 +674,7 @@ val fetchTraverse: Fetch[List[User]] = Fetch.traverse(List(1, 2, 3))(getUser)
593674Note that ` Fetch#traverse ` is not as general as the ` traverse ` method from ` Traverse ` , but performs the same optimizations.
594675
595676``` tut:book
596- val result: List[User] = Fetch.run[Id ](fetchTraverse)
677+ val result: List[User] = Fetch.run[Eval ](fetchTraverse).value
597678```
598679
599680## cats
@@ -624,7 +705,7 @@ val fetchThree: Fetch[(Post, User, Post)] = (getPost(1) |@| getUser(2) |@| getPo
624705Notice how the queries to posts are batched.
625706
626707``` tut:book
627- val result: (Post, User, Post) = Fetch.run[Id](fetchThree)
708+ val result: (Post, User, Post) = fetchThree.runA[Eval].value
628709```
629710
630711More interestingly, we can use it to apply a pure function to the results of various
@@ -635,7 +716,7 @@ val fetchFriends: Fetch[String] = (getUser(1) |@| getUser(2)).map({ (one, other)
635716 s"${one.username} is friends with ${other.username}"
636717})
637718
638- val result: String = Fetch.run[Id](fetchFriends)
719+ val result: String = fetchFriends.runA[Eval].value
639720```
640721
641722The above example is equivalent to the following using the ` Fetch#join ` method:
@@ -645,7 +726,7 @@ val fetchFriends: Fetch[String] = Fetch.join(getUser(1), getUser(2)).map({ case
645726 s"${one.username} is friends with ${other.username}"
646727})
647728
648- val result: String = Fetch.run[Id](fetchFriends)
729+ val result: String = fetchFriends.runA[Eval].value
649730```
650731
651732# Resources
0 commit comments