o

fetchlib

UsageSection

object UsageSection extends AnyFlatSpec with Matchers with Section

Introduction

Fetch is a library that allows your data fetches to be written in a concise, composable way while executing efficiently. You don't need to use any explicit concurrency construct but existing idioms: applicative for concurrency and monad for sequencing.

Oftentimes, our applications read and manipulate data from a variety of different sources such as databases, web services or file systems. These data sources are subject to latency, and we'd prefer to query them efficiently.

If we are just reading data, we can make a series of optimizations such as:

  • batching requests to the same data source
  • requesting independent data from different sources in parallel
  • caching previously seen results

However, if we mix these optimizations with the code that fetches the data we may end up trading clarity for performance. Furthermore, we are mixing low-level (optimization) and high-level (business logic with the data we read) concerns.

Installation

To begin, add the following dependency to your SBT build file:

"com.47deg" %% "fetch" % "1.2.2"

Or, if using Scala.js:

"com.47deg" %%% "fetch" % "1.2.2"

Now you’ll have Fetch available in both Scala and Scala.js.

Usage

In order to tell Fetch how to retrieve data, we must implement the DataSource typeclass.

import cats.effect.Concurrent
import cats.data.NonEmptyList

trait DataSource[F[_], Identity, Result]{
  def data: Data[Identity, Result]

  def CF: Concurrent[F]

  def fetch(id: Identity): F[Option[Result]]

  /* `batch` is implemented in terms of `fetch` by default */
  def batch(ids: NonEmptyList[Identity]): F[Map[Identity, Result]]
}

It takes two type parameters:

  • Identity: the identity we want to fetch (a UserId if we were fetching users)
  • Result: the type of the data we retrieve (a User if we were fetching users)

There are two methods: fetch and batch. fetch receives one identity and must return a Concurrent containing an optional result. Returning an Option Fetch can detect whether an identity couldn't be fetched or no longer exists.

batch method takes a non-empty list of identities and must return a Concurrent containing a map from identities to results. Accepting a list of identities gives Fetch the ability to batch requests to the same data source, and returning a mapping from identities to results, Fetch can detect whenever an identity couldn’t be fetched or no longer exists.

The data method returns a Data[Identity, Result] instance that Fetch uses to optimize requests to the same data source, and is expected to return a singleton object that extends Data[Identity, Result].

Writing your first data source

Now that we know about the DataSource typeclass, let's write our first data source! We'll start by implementing a data source for fetching users given their id. The first thing we'll do is define the types for user ids and users.

type UserId = Int
case class User(id: UserId, username: String)

We’ll simulate unpredictable latency with this function.

import cats.effect._
import cats.syntax.all._

def latency[F[_] : Concurrent](msg: String): F[Unit] = for {
  _ <- Sync[F].delay(println(s"--> [${Thread.currentThread.getId}] $msg"))
  _ <- Sync[F].delay(Thread.sleep(100))
  _ <- Sync[F].delay(println(s"<-- [${Thread.currentThread.getId}] $msg"))
} yield ()

And now we're ready to write our user data source; we'll emulate a database with an in-memory map.

import cats.data.NonEmptyList
import cats.instances.list._
import fetch._

val userDatabase: Map[UserId, User] = Map(
  1 -> User(1, "@one"),
  2 -> User(2, "@two"),
  3 -> User(3, "@three"),
  4 -> User(4, "@four")
)

object Users extends Data[UserId, User] {
  def name = "Users"

  def source[F[_] : Concurrent]: DataSource[F, UserId, User] = new DataSource[F, UserId, User] {
    override def data = Users

    override def CF = Concurrent[F]

    override def fetch(id: UserId): F[Option[User]] =
      latency[F](s"One User $id") >> CF.pure(userDatabase.get(id))

    override def batch(ids: NonEmptyList[UserId]): F[Map[UserId, User]] =
      latency[F](s"Batch Users $ids") >> CF.pure(userDatabase.filterKeys(ids.toList.toSet).toMap)
  }
}

Now that we have a data source we can write a function for fetching users given an id, we just have to pass a UserId as an argument to Fetch.

def getUser[F[_] : Concurrent](id: UserId): Fetch[F, User] =
  Fetch(id, Users.source)

Optional identities

If you want to create a Fetch that doesn’t fail if the identity is not found, you can use Fetch#optional instead of Fetch#apply. Note that instead of a Fetch[F, A] you will get a Fetch[F, Option[A]].

def maybeGetUser[F[_] : Concurrent](id: UserId): Fetch[F, Option[User]] =
  Fetch.optional(id, Users.source)

Data sources that don’t support batching

If your data source doesn’t support batching, you can simply leave the batch method unimplemented. Note that it will use the fetch implementation for requesting identities in parallel.

object Unbatched extends Data[Int, Int]{
  def name = "Unbatched"

  def source[F[_] : Concurrent]: DataSource[F, Int, Int] = new DataSource[F, Int, Int]{
    override def data = Unbatched

    override def CF = Concurrent[F]

    override def fetch(id: Int): F[Option[Int]] =
      CF.pure(Option(id))
  }
}

Batching individuals requests sequentially

The default batch implementation run requests to the data source in parallel, but you can easily override it. We can make batch sequential using NonEmptyList.traverse for fetching individual identities.

object UnbatchedSeq extends Data[Int, Int]{
  def name = "UnbatchedSeq"

  def source[F[_] : Concurrent]: DataSource[F, Int, Int] = new DataSource[F, Int, Int]{
    override def data = UnbatchedSeq

    override def CF = Concurrent[F]

    override def fetch(id: Int): F[Option[Int]] =
      CF.pure(Option(id))

    override def batch(ids: NonEmptyList[Int]): F[Map[Int, Int]] =
      ids.traverse(
        (id) => fetch(id).map(v => (id, v))
      ).map(_.collect { case (i, Some(x)) => (i, x) }.toMap)
  }
}

Data sources that only support batching

If your data source only supports querying it in batches, you can implement fetch in terms of batch.

object OnlyBatched extends Data[Int, Int]{
  def name = "OnlyBatched"

  def source[F[_] : Concurrent]: DataSource[F, Int, Int] = new DataSource[F, Int, Int]{
    override def data = OnlyBatched

    override def CF = Concurrent[F]

    override def fetch(id: Int): F[Option[Int]] =
      batch(NonEmptyList(id, List())).map(_.get(id))

    override def batch(ids: NonEmptyList[Int]): F[Map[Int, Int]] =
      CF.pure(ids.map(x => (x, x)).toList.toMap)
  }
}
Linear Supertypes
Section, Matchers, Explicitly, MatcherWords, Tolerance, AnyFlatSpec, AnyFlatSpecLike, Documenting, Alerting, Notifying, Informing, CanVerb, MustVerb, ShouldVerb, TestRegistration, TestSuite, Suite, Serializable, Assertions, TripleEquals, TripleEqualsSupport, AnyRef, Any
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. UsageSection
  2. Section
  3. Matchers
  4. Explicitly
  5. MatcherWords
  6. Tolerance
  7. AnyFlatSpec
  8. AnyFlatSpecLike
  9. Documenting
  10. Alerting
  11. Notifying
  12. Informing
  13. CanVerb
  14. MustVerb
  15. ShouldVerb
  16. TestRegistration
  17. TestSuite
  18. Suite
  19. Serializable
  20. Assertions
  21. TripleEquals
  22. TripleEqualsSupport
  23. AnyRef
  24. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. Protected

Type Members

  1. final class AWord extends AnyRef
    Definition Classes
    Matchers
  2. final class AnWord extends AnyRef
    Definition Classes
    Matchers
  3. sealed class AnyShouldWrapper[T] extends AnyRef
    Definition Classes
    Matchers
  4. final class BehaviorWord extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  5. class CheckingEqualizer[L] extends AnyRef
    Definition Classes
    TripleEqualsSupport
  6. sealed class Collected extends Serializable
    Attributes
    protected
    Definition Classes
    Matchers
  7. class DecidedByEquality[A] extends Equality[A]
    Definition Classes
    Explicitly
  8. class DecidedWord extends AnyRef
    Definition Classes
    Explicitly
  9. class DeterminedByEquivalence[T] extends Equivalence[T]
    Definition Classes
    Explicitly
  10. class DeterminedWord extends AnyRef
    Definition Classes
    Explicitly
  11. class Equalizer[L] extends AnyRef
    Definition Classes
    TripleEqualsSupport
  12. final class HavePropertyMatcherGenerator extends AnyRef
    Definition Classes
    Matchers
  13. final class IgnoreVerbString extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  14. final class IgnoreVerbStringTaggedAs extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  15. final class IgnoreWord extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  16. final class InAndIgnoreMethods extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  17. final class InAndIgnoreMethodsAfterTaggedAs extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  18. final class ItVerbString extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  19. final class ItVerbStringTaggedAs extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  20. final class ItWord extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  21. final class KeyWord extends AnyRef
    Definition Classes
    Matchers
  22. trait NoArgTest extends () => Outcome with TestData
    Attributes
    protected
    Definition Classes
    TestSuite
  23. final class PlusOrMinusWrapper[T] extends AnyRef
    Definition Classes
    Tolerance
  24. final class RegexWord extends AnyRef
    Definition Classes
    Matchers
  25. final class RegexWrapper extends AnyRef
    Definition Classes
    Matchers
  26. class ResultOfBeWordForAny[T] extends AnyRef
    Definition Classes
    Matchers
  27. sealed class ResultOfBeWordForCollectedAny[T] extends AnyRef
    Definition Classes
    Matchers
  28. final class ResultOfBeWordForCollectedArray[T] extends ResultOfBeWordForCollectedAny[Array[T]]
    Definition Classes
    Matchers
  29. final class ResultOfCollectedAny[T] extends AnyRef
    Definition Classes
    Matchers
  30. final class ResultOfContainWordForCollectedAny[T] extends AnyRef
    Definition Classes
    Matchers
  31. final class ResultOfEndWithWordForCollectedString extends AnyRef
    Definition Classes
    Matchers
  32. final class ResultOfEndWithWordForString extends AnyRef
    Definition Classes
    Matchers
  33. final class ResultOfFullyMatchWordForCollectedString extends AnyRef
    Definition Classes
    Matchers
  34. final class ResultOfFullyMatchWordForString extends AnyRef
    Definition Classes
    Matchers
  35. final class ResultOfHaveWordForCollectedExtent[A] extends AnyRef
    Definition Classes
    Matchers
  36. final class ResultOfHaveWordForExtent[A] extends AnyRef
    Definition Classes
    Matchers
  37. final class ResultOfIncludeWordForCollectedString extends AnyRef
    Definition Classes
    Matchers
  38. final class ResultOfIncludeWordForString extends AnyRef
    Definition Classes
    Matchers
  39. final class ResultOfNotWordForCollectedAny[T] extends AnyRef
    Definition Classes
    Matchers
  40. final class ResultOfStartWithWordForCollectedString extends AnyRef
    Definition Classes
    Matchers
  41. final class ResultOfStartWithWordForString extends AnyRef
    Definition Classes
    Matchers
  42. trait StringCanWrapperForVerb extends AnyRef
    Definition Classes
    CanVerb
  43. trait StringMustWrapperForVerb extends AnyRef
    Definition Classes
    MustVerb
  44. final class StringShouldWrapper extends AnyShouldWrapper[String] with org.scalatest.matchers.should.Matchers.StringShouldWrapperForVerb
    Definition Classes
    Matchers
  45. trait StringShouldWrapperForVerb extends AnyRef
    Definition Classes
    ShouldVerb
  46. class TheAfterWord extends AnyRef
    Definition Classes
    Explicitly
  47. final class TheSameInstanceAsPhrase extends AnyRef
    Definition Classes
    Matchers
  48. final class TheyVerbString extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  49. final class TheyVerbStringTaggedAs extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  50. final class TheyWord extends AnyRef
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  51. final class ValueWord extends AnyRef
    Definition Classes
    Matchers

Value Members

  1. final def !=(arg0: Any): Boolean
    Definition Classes
    AnyRef → Any
  2. def !==[T](right: Spread[T]): TripleEqualsInvocationOnSpread[T]
    Definition Classes
    TripleEqualsSupport
  3. def !==(right: Null): TripleEqualsInvocation[Null]
    Definition Classes
    TripleEqualsSupport
  4. def !==[T](right: T): TripleEqualsInvocation[T]
    Definition Classes
    TripleEqualsSupport
  5. final def ##: Int
    Definition Classes
    AnyRef → Any
  6. def <[T](right: T)(implicit arg0: Ordering[T]): ResultOfLessThanComparison[T]
    Definition Classes
    Matchers
  7. def <=[T](right: T)(implicit arg0: Ordering[T]): ResultOfLessThanOrEqualToComparison[T]
    Definition Classes
    Matchers
  8. final def ==(arg0: Any): Boolean
    Definition Classes
    AnyRef → Any
  9. def ===[T](right: Spread[T]): TripleEqualsInvocationOnSpread[T]
    Definition Classes
    TripleEqualsSupport
  10. def ===(right: Null): TripleEqualsInvocation[Null]
    Definition Classes
    TripleEqualsSupport
  11. def ===[T](right: T): TripleEqualsInvocation[T]
    Definition Classes
    TripleEqualsSupport
  12. def >[T](right: T)(implicit arg0: Ordering[T]): ResultOfGreaterThanComparison[T]
    Definition Classes
    Matchers
  13. def >=[T](right: T)(implicit arg0: Ordering[T]): ResultOfGreaterThanOrEqualToComparison[T]
    Definition Classes
    Matchers
  14. def a[T](implicit arg0: ClassTag[T]): ResultOfATypeInvocation[T]
    Definition Classes
    Matchers
  15. val a: AWord
    Definition Classes
    Matchers
  16. val after: TheAfterWord
    Definition Classes
    Explicitly
  17. def alert: Alerter
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike → Alerting
  18. def all(xs: String)(implicit collecting: Collecting[Char, String], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Char]
    Definition Classes
    Matchers
  19. def all[K, V, JMAP[k, v] <: Map[k, v]](xs: JMAP[K, V])(implicit collecting: Collecting[Entry[K, V], JMAP[K, V]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Entry[K, V]]
    Definition Classes
    Matchers
  20. def all[K, V, MAP[k, v] <: GenMap[k, v]](xs: MAP[K, V])(implicit collecting: Collecting[(K, V), GenTraversable[(K, V)]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[(K, V)]
    Definition Classes
    Matchers
  21. def all[E, C[_]](xs: C[E])(implicit collecting: Collecting[E, C[E]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[E]
    Definition Classes
    Matchers
  22. def allElementsOf[R](elements: GenTraversable[R]): ResultOfAllElementsOfApplication
    Definition Classes
    Matchers
  23. def allOf(firstEle: Any, secondEle: Any, remainingEles: Any*)(implicit pos: Position): ResultOfAllOfApplication
    Definition Classes
    Matchers
  24. def an[T](implicit arg0: ClassTag[T]): ResultOfAnTypeInvocation[T]
    Definition Classes
    Matchers
  25. val an: AnWord
    Definition Classes
    Matchers
  26. final def asInstanceOf[T0]: T0
    Definition Classes
    Any
  27. macro def assert(condition: Boolean, clue: Any)(implicit prettifier: Prettifier, pos: Position): Assertion
    Definition Classes
    Assertions
  28. macro def assert(condition: Boolean)(implicit prettifier: Prettifier, pos: Position): Assertion
    Definition Classes
    Assertions
  29. macro def assertCompiles(code: String)(implicit pos: Position): Assertion
    Definition Classes
    Assertions
  30. macro def assertDoesNotCompile(code: String)(implicit pos: Position): Assertion
    Definition Classes
    Assertions
  31. def assertResult(expected: Any)(actual: Any)(implicit prettifier: Prettifier, pos: Position): Assertion
    Definition Classes
    Assertions
  32. def assertResult(expected: Any, clue: Any)(actual: Any)(implicit prettifier: Prettifier, pos: Position): Assertion
    Definition Classes
    Assertions
  33. def assertThrows[T <: AnyRef](f: => Any)(implicit classTag: ClassTag[T], pos: Position): Assertion
    Definition Classes
    Assertions
  34. macro def assertTypeError(code: String)(implicit pos: Position): Assertion
    Definition Classes
    Assertions
  35. macro def assume(condition: Boolean, clue: Any)(implicit prettifier: Prettifier, pos: Position): Assertion
    Definition Classes
    Assertions
  36. macro def assume(condition: Boolean)(implicit prettifier: Prettifier, pos: Position): Assertion
    Definition Classes
    Assertions
  37. def atLeast(num: Int, xs: String)(implicit collecting: Collecting[Char, String], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Char]
    Definition Classes
    Matchers
  38. def atLeast[K, V, JMAP[k, v] <: Map[k, v]](num: Int, xs: JMAP[K, V])(implicit collecting: Collecting[Entry[K, V], JMAP[K, V]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Entry[K, V]]
    Definition Classes
    Matchers
  39. def atLeast[K, V, MAP[k, v] <: GenMap[k, v]](num: Int, xs: MAP[K, V])(implicit collecting: Collecting[(K, V), GenTraversable[(K, V)]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[(K, V)]
    Definition Classes
    Matchers
  40. def atLeast[E, C[_]](num: Int, xs: C[E])(implicit collecting: Collecting[E, C[E]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[E]
    Definition Classes
    Matchers
  41. def atLeastOneElementOf(elements: GenTraversable[Any]): ResultOfAtLeastOneElementOfApplication
    Definition Classes
    Matchers
  42. def atLeastOneOf(firstEle: Any, secondEle: Any, remainingEles: Any*)(implicit pos: Position): ResultOfAtLeastOneOfApplication
    Definition Classes
    Matchers
  43. def atMost(num: Int, xs: String)(implicit collecting: Collecting[Char, String], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Char]
    Definition Classes
    Matchers
  44. def atMost[K, V, JMAP[k, v] <: Map[k, v]](num: Int, xs: JMAP[K, V])(implicit collecting: Collecting[Entry[K, V], JMAP[K, V]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Entry[K, V]]
    Definition Classes
    Matchers
  45. def atMost[K, V, MAP[k, v] <: GenMap[k, v]](num: Int, xs: MAP[K, V])(implicit collecting: Collecting[(K, V), GenTraversable[(K, V)]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[(K, V)]
    Definition Classes
    Matchers
  46. def atMost[E, C[_]](num: Int, xs: C[E])(implicit collecting: Collecting[E, C[E]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[E]
    Definition Classes
    Matchers
  47. def atMostOneElementOf[R](elements: GenTraversable[R]): ResultOfAtMostOneElementOfApplication
    Definition Classes
    Matchers
  48. def atMostOneOf(firstEle: Any, secondEle: Any, remainingEles: Any*)(implicit pos: Position): ResultOfAtMostOneOfApplication
    Definition Classes
    Matchers
  49. def batching(res0: (User, User)): Assertion

    If we combine two independent requests to the same data source, Fetch will automatically batch them together into a single request.

    Batching

    If we combine two independent requests to the same data source, Fetch will automatically batch them together into a single request. Applicative operations like the product of two fetches help us tell the library that those fetches are independent, and thus can be batched if they use the same data source:

    Both ids (1 and 2) are requested in a single query to the data source when executing the fetch.

  50. val be: BeWord
    Definition Classes
    MatcherWords
  51. val behave: BehaveWord
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  52. val behavior: BehaviorWord
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  53. def between(from: Int, upTo: Int, xs: String)(implicit collecting: Collecting[Char, String], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Char]
    Definition Classes
    Matchers
  54. def between[K, V, JMAP[k, v] <: Map[k, v]](from: Int, upTo: Int, xs: JMAP[K, V])(implicit collecting: Collecting[Entry[K, V], JMAP[K, V]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Entry[K, V]]
    Definition Classes
    Matchers
  55. def between[E, C[_]](from: Int, upTo: Int, xs: C[E])(implicit collecting: Collecting[E, C[E]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[E]
    Definition Classes
    Matchers
  56. def caching(res0: (User, User)): Assertion

    During the execution of a fetch, previously requested results are implicitly cached.

    Caching

    During the execution of a fetch, previously requested results are implicitly cached. This allows us to write fetches in a very modular way, asking for all the data they need as if it was in memory; furthermore, it also avoids re-fetching an identity that may have changed during the course of a fetch execution, which can lead to inconsistencies in the data.

    val fetchCached: Fetch[(User, User)] = for {
    aUser <- getUser(1)
    anotherUser <- getUser(1)
    } yield (aUser, anotherUser)

    As you can see, the User with id 1 is fetched only once in a single round-trip. The next time it was needed we used the cached versions, thus avoiding another request to the user data source.

  57. def cancel(cause: Throwable)(implicit pos: Position): Nothing
    Definition Classes
    Assertions
  58. def cancel(message: String, cause: Throwable)(implicit pos: Position): Nothing
    Definition Classes
    Assertions
  59. def cancel(message: String)(implicit pos: Position): Nothing
    Definition Classes
    Assertions
  60. def cancel()(implicit pos: Position): Nothing
    Definition Classes
    Assertions
  61. def clone(): AnyRef
    Attributes
    protected[lang]
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.CloneNotSupportedException]) @native()
  62. def combiningData(res0: (Post, PostTopic)): Assertion

    Now that we know about some of the optimizations that Fetch can perform to read data efficiently, let's look at how we can combine more than one data source.

    Combining data from multiple sources

    Now that we know about some of the optimizations that Fetch can perform to read data efficiently, let's look at how we can combine more than one data source.

    Imagine that we are rendering a blog and have the following types for posts:

    type PostId = Int
    case class Post(id: PostId, author: UserId, content: String)

    As you can see, every Post has an author, but it refers to the author by its id. We'll implement a data source for retrieving a post given a post id.

    val postDatabase: Map[PostId, Post] = Map(
      1 -> Post(1, 2, "An article"),
      2 -> Post(2, 3, "Another article"),
      3 -> Post(3, 4, "Yet another article")
    )
    
    object Posts extends Data[PostId, Post] {
      def name = "Posts"
    
      def source[F[_] : Concurrent]: DataSource[F, PostId, Post] = new DataSource[F, PostId, Post] {
        override def data = Posts
    
        override def CF = Concurrent[F]
    
        override def fetch(id: PostId): F[Option[Post]] =
          latency[F](s"One Post $id") >> CF.pure(postDatabase.get(id))
    
        override def batch(ids: NonEmptyList[PostId]): F[Map[PostId, Post]] =
          latency[F](s"Batch Posts $ids") >> CF.pure(postDatabase.filterKeys(ids.toList.toSet).toMap)
      }
    }
    
    def getPost[F[_] : Concurrent](id: PostId): Fetch[F, Post] =
      Fetch(id, Posts.source)

    Apart from posts, we are going to add another data source: one for post topics.

    type PostTopic = String

    We'll implement a data source for retrieving a post topic given a post id.

    object PostTopics extends Data[Post, PostTopic] {
      def name = "Post Topics"
    
      def source[F[_] : Concurrent]: DataSource[F, Post, PostTopic] = new DataSource[F, Post, PostTopic] {
        override def data = PostTopics
    
        override def CF = Concurrent[F]
    
        override def fetch(id: Post): F[Option[PostTopic]] = {
          val topic = if (id.id % 2 == 0) "monad" else "applicative"
          latency[F](s"One Post Topic $id") >> CF.pure(Option(topic))
        }
    
        override def batch(ids: NonEmptyList[Post]): F[Map[Post, PostTopic]] = {
          val result = ids.toList.map(id => (id, if (id.id % 2 == 0) "monad" else "applicative")).toMap
          latency[F](s"Batch Post Topics $ids") >> CF.pure(result)
        }
      }
    }
    
    def getPostTopic[F[_] : Concurrent](post: Post): Fetch[F, PostTopic] =
      Fetch(post, PostTopics.source)

    Now that we have multiple sources let's mix them in the same fetch. In the following example, we are fetching a post given its id and then fetching its topic. This data could come from entirely different places, but Fetch makes working with heterogeneous sources of data very easy.

  63. val compile: CompileWord
    Definition Classes
    MatcherWords
  64. val contain: ContainWord
    Definition Classes
    MatcherWords
  65. def convertEquivalenceToAToBConstraint[A, B](equivalenceOfB: Equivalence[B])(implicit ev: <:<[A, B]): CanEqual[A, B]
    Definition Classes
    TripleEquals → TripleEqualsSupport
  66. def convertEquivalenceToBToAConstraint[A, B](equivalenceOfA: Equivalence[A])(implicit ev: <:<[B, A]): CanEqual[A, B]
    Definition Classes
    TripleEquals → TripleEqualsSupport
  67. implicit def convertNumericToPlusOrMinusWrapper[T](pivot: T)(implicit arg0: Numeric[T]): PlusOrMinusWrapper[T]
    Definition Classes
    Tolerance
  68. implicit def convertSymbolToHavePropertyMatcherGenerator(symbol: Symbol)(implicit prettifier: Prettifier, pos: Position): HavePropertyMatcherGenerator
    Definition Classes
    Matchers
  69. implicit def convertToAnyShouldWrapper[T](o: T)(implicit pos: Position, prettifier: Prettifier): AnyShouldWrapper[T]
    Definition Classes
    Matchers
  70. def convertToCheckingEqualizer[T](left: T): CheckingEqualizer[T]
    Definition Classes
    TripleEquals → TripleEqualsSupport
  71. implicit def convertToEqualizer[T](left: T): Equalizer[T]
    Definition Classes
    TripleEquals → TripleEqualsSupport
  72. implicit def convertToInAndIgnoreMethods(resultOfStringPassedToVerb: ResultOfStringPassedToVerb): InAndIgnoreMethods
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  73. implicit def convertToInAndIgnoreMethodsAfterTaggedAs(resultOfTaggedAsInvocation: ResultOfTaggedAsInvocation): InAndIgnoreMethodsAfterTaggedAs
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  74. implicit def convertToRegexWrapper(o: Regex): RegexWrapper
    Definition Classes
    Matchers
  75. implicit def convertToStringCanWrapper(o: String)(implicit position: Position): StringCanWrapperForVerb
    Definition Classes
    CanVerb
  76. implicit def convertToStringMustWrapperForVerb(o: String)(implicit position: Position): StringMustWrapperForVerb
    Definition Classes
    MustVerb
  77. implicit def convertToStringShouldWrapper(o: String)(implicit pos: Position, prettifier: Prettifier): StringShouldWrapper
    Definition Classes
    Matchers
  78. implicit def convertToStringShouldWrapperForVerb(o: String)(implicit position: Position): StringShouldWrapperForVerb
    Definition Classes
    ShouldVerb
  79. def creatingAndRunning(res0: User): Assertion

    Since we’lll use IO from the cats-effect library to execute our fetches, we’ll need a runtime for executing our IO instances.

    Creating a runtime

    Since we’lll use IO from the cats-effect library to execute our fetches, we’ll need a runtime for executing our IO instances. This includes a ContextShift[IO] used for running the IO instances and a Timer[IO] that is used for scheduling, let’s go ahead and create them, we’ll use a java.util.concurrent.ScheduledThreadPoolExecutor with a few threads to run our fetches.

    import cats.effect._
    import java.util.concurrent._
    import scala.concurrent.ExecutionContext
    import scala.concurrent.duration._
    
    val executor = new ScheduledThreadPoolExecutor(4)
    val executionContext: ExecutionContext = ExecutionContext.fromExecutor(executor)
    
    implicit val timer: Timer[IO] = IO.timer(executionContext)
    implicit val cs: ContextShift[IO] = IO.contextShift(executionContext)

    Creating and running a fetch

    We are now ready to create and run fetches. Note the distinction between Fetch creation and execution. When we are creating Fetch values, we are just constructing a recipe of our data dependencies.

    def fetchUser[F[_] : Concurrent]: Fetch[F, User] =
      getUser(1)

    A Fetch is just a value, and in order to be able to get its value we need to run it to an IO first.

    import cats.effect.IO
    
    Fetch.run[IO](fetchUser)

    We can now run the IO and see its result:

  80. val decided: DecidedWord
    Definition Classes
    Explicitly
  81. def deduplication(res0: (User, User)): Assertion

    If two independent requests ask for the same identity, Fetch will detect it and deduplicate the id.

    Deduplication

    If two independent requests ask for the same identity, Fetch will detect it and deduplicate the id. Note that when running the fetch, the identity 1 is only requested once even when it is needed by both fetches.

  82. def defaultEquality[A]: Equality[A]
    Definition Classes
    TripleEqualsSupport
  83. val defined: DefinedWord
    Definition Classes
    MatcherWords
  84. def definedAt[T](right: T): ResultOfDefinedAt[T]
    Definition Classes
    Matchers
  85. val determined: DeterminedWord
    Definition Classes
    Explicitly
  86. val empty: EmptyWord
    Definition Classes
    MatcherWords
  87. val endWith: EndWithWord
    Definition Classes
    MatcherWords
  88. final def eq(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef
  89. def equal(o: Null): Matcher[AnyRef]
    Definition Classes
    Matchers
  90. def equal[T](spread: Spread[T]): Matcher[T]
    Definition Classes
    Matchers
  91. def equal(right: Any): MatcherFactory1[Any, Equality]
    Definition Classes
    MatcherWords
  92. def equals(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef → Any
  93. def every(xs: String)(implicit collecting: Collecting[Char, String], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Char]
    Definition Classes
    Matchers
  94. def every[K, V, JMAP[k, v] <: Map[k, v]](xs: JMAP[K, V])(implicit collecting: Collecting[Entry[K, V], JMAP[K, V]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Entry[K, V]]
    Definition Classes
    Matchers
  95. def every[K, V, MAP[k, v] <: Map[k, v]](xs: MAP[K, V])(implicit collecting: Collecting[(K, V), GenTraversable[(K, V)]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[(K, V)]
    Definition Classes
    Matchers
  96. def every[E, C[_]](xs: C[E])(implicit collecting: Collecting[E, C[E]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[E]
    Definition Classes
    Matchers
  97. def exactly(num: Int, xs: String)(implicit collecting: Collecting[Char, String], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Char]
    Definition Classes
    Matchers
  98. def exactly[K, V, JMAP[k, v] <: Map[k, v]](num: Int, xs: JMAP[K, V])(implicit collecting: Collecting[Entry[K, V], JMAP[K, V]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Entry[K, V]]
    Definition Classes
    Matchers
  99. def exactly[K, V, MAP[k, v] <: GenMap[k, v]](num: Int, xs: MAP[K, V])(implicit collecting: Collecting[(K, V), GenTraversable[(K, V)]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[(K, V)]
    Definition Classes
    Matchers
  100. def exactly[E, C[_]](num: Int, xs: C[E])(implicit collecting: Collecting[E, C[E]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[E]
    Definition Classes
    Matchers
  101. final def execute(testName: String, configMap: ConfigMap, color: Boolean, durations: Boolean, shortstacks: Boolean, fullstacks: Boolean, stats: Boolean): Unit
    Definition Classes
    Suite
  102. val exist: ExistWord
    Definition Classes
    MatcherWords
  103. def expectedTestCount(filter: Filter): Int
    Definition Classes
    Suite
  104. def fail(cause: Throwable)(implicit pos: Position): Nothing
    Definition Classes
    Assertions
  105. def fail(message: String, cause: Throwable)(implicit pos: Position): Nothing
    Definition Classes
    Assertions
  106. def fail(message: String)(implicit pos: Position): Nothing
    Definition Classes
    Assertions
  107. def fail()(implicit pos: Position): Nothing
    Definition Classes
    Assertions
  108. def finalize(): Unit
    Attributes
    protected[lang]
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.Throwable])
  109. val fullyMatch: FullyMatchWord
    Definition Classes
    MatcherWords
  110. final def getClass(): Class[_ <: AnyRef]
    Definition Classes
    AnyRef → Any
    Annotations
    @native()
  111. def hashCode(): Int
    Definition Classes
    AnyRef → Any
    Annotations
    @native()
  112. val have: HaveWord
    Definition Classes
    MatcherWords
  113. val ignore: IgnoreWord
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  114. def inOrder(firstEle: Any, secondEle: Any, remainingEles: Any*)(implicit pos: Position): ResultOfInOrderApplication
    Definition Classes
    Matchers
  115. def inOrderElementsOf[R](elements: GenTraversable[R]): ResultOfInOrderElementsOfApplication
    Definition Classes
    Matchers
  116. def inOrderOnly[T](firstEle: Any, secondEle: Any, remainingEles: Any*)(implicit pos: Position): ResultOfInOrderOnlyApplication
    Definition Classes
    Matchers
  117. val include: IncludeWord
    Definition Classes
    MatcherWords
  118. def info: Informer
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike → Informing
  119. def intercept[T <: AnyRef](f: => Any)(implicit classTag: ClassTag[T], pos: Position): T
    Definition Classes
    Assertions
  120. final def isInstanceOf[T0]: Boolean
    Definition Classes
    Any
  121. val it: ItWord
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  122. val key: KeyWord
    Definition Classes
    Matchers
  123. val length: LengthWord
    Definition Classes
    MatcherWords
  124. def lowPriorityTypeCheckedConstraint[A, B](implicit equivalenceOfB: Equivalence[B], ev: <:<[A, B]): CanEqual[A, B]
    Definition Classes
    TripleEquals → TripleEqualsSupport
  125. def markup: Documenter
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike → Documenting
  126. val matchPattern: MatchPatternWord
    Definition Classes
    MatcherWords
  127. def message(expectedMessage: String): ResultOfMessageWordApplication
    Definition Classes
    Matchers
  128. final def ne(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef
  129. def nestedSuites: IndexedSeq[Suite]
    Definition Classes
    Suite
  130. def no(xs: String)(implicit collecting: Collecting[Char, String], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Char]
    Definition Classes
    Matchers
  131. def no[K, V, JMAP[k, v] <: Map[k, v]](xs: JMAP[K, V])(implicit collecting: Collecting[Entry[K, V], JMAP[K, V]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[Entry[K, V]]
    Definition Classes
    Matchers
  132. def no[E, C[_]](xs: C[E])(implicit collecting: Collecting[E, C[E]], prettifier: Prettifier, pos: Position): ResultOfCollectedAny[E]
    Definition Classes
    Matchers
  133. def noElementsOf(elements: GenTraversable[Any]): ResultOfNoElementsOfApplication
    Definition Classes
    Matchers
  134. def noException(implicit pos: Position): NoExceptionWord
    Definition Classes
    MatcherWords
  135. def noneOf(firstEle: Any, secondEle: Any, remainingEles: Any*)(implicit pos: Position): ResultOfNoneOfApplication
    Definition Classes
    Matchers
  136. val not: NotWord
    Definition Classes
    MatcherWords
  137. def note: Notifier
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike → Notifying
  138. final def notify(): Unit
    Definition Classes
    AnyRef
    Annotations
    @native()
  139. final def notifyAll(): Unit
    Definition Classes
    AnyRef
    Annotations
    @native()
  140. def of[T](implicit ev: ClassTag[T]): ResultOfOfTypeInvocation[T]
    Definition Classes
    Matchers
  141. def oneElementOf(elements: GenTraversable[Any]): ResultOfOneElementOfApplication
    Definition Classes
    Matchers
  142. def oneOf(firstEle: Any, secondEle: Any, remainingEles: Any*)(implicit pos: Position): ResultOfOneOfApplication
    Definition Classes
    Matchers
  143. def only(xs: Any*)(implicit pos: Position): ResultOfOnlyApplication
    Definition Classes
    Matchers
  144. def pending: Assertion with PendingStatement
    Definition Classes
    Assertions
  145. def pendingUntilFixed(f: => Unit)(implicit pos: Position): Assertion with PendingStatement
    Definition Classes
    Assertions
  146. val readable: ReadableWord
    Definition Classes
    MatcherWords
  147. val regex: RegexWord
    Definition Classes
    Matchers
  148. final def registerIgnoredTest(testText: String, testTags: Tag*)(testFun: => Any)(implicit pos: Position): Unit
    Definition Classes
    AnyFlatSpecLike → TestRegistration
  149. final def registerTest(testText: String, testTags: Tag*)(testFun: => Any)(implicit pos: Position): Unit
    Definition Classes
    AnyFlatSpecLike → TestRegistration
  150. def rerunner: Option[String]
    Definition Classes
    Suite
  151. def run(testName: Option[String], args: Args): Status
    Definition Classes
    AnyFlatSpecLike → Suite
  152. def runNestedSuites(args: Args): Status
    Attributes
    protected
    Definition Classes
    Suite
  153. def runTest(testName: String, args: Args): Status
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike → TestSuite → Suite
  154. def runTests(testName: Option[String], args: Args): Status
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike → Suite
  155. def sequence(res0: List[User]): Assertion

    Besides flatMap for sequencing fetches and products for running them concurrently, Fetch provides a number of other combinators.

    Combinators

    Besides flatMap for sequencing fetches and products for running them concurrently, Fetch provides a number of other combinators.

    Sequence

    Whenever we have a list of fetches of the same type and want to run them concurrently, we can use the sequence combinator. It takes a List[Fetch[A]] and gives you back a Fetch[List[A]], batching the fetches to the same data source and running fetches to different sources in parallel. Note that the sequence combinator is more general and works not only on lists but on any type that has a Traverse instance.

    Since sequence uses applicative operations internally, the library is able to perform optimizations across all the sequenced fetches.

    import cats.instances.list._
    import cats.syntax.traverse._
  156. def sequencing(res0: (User, User)): Assertion

    When we have two fetches that depend on each other, we can use flatMap to combine them.

    Sequencing

    When we have two fetches that depend on each other, we can use flatMap to combine them. The most straightforward way is to use a for comprehension.

    When 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 below 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.

  157. implicit val shorthandSharedTestRegistrationFunction: StringVerbBehaveLikeInvocation
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  158. implicit val shorthandTestRegistrationFunction: StringVerbStringInvocation
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  159. val size: SizeWord
    Definition Classes
    MatcherWords
  160. val sorted: SortedWord
    Definition Classes
    MatcherWords
  161. val startWith: StartWithWord
    Definition Classes
    MatcherWords
  162. final val succeed: Assertion
    Definition Classes
    Assertions
  163. def suiteId: String
    Definition Classes
    Suite
  164. def suiteName: String
    Definition Classes
    Suite
  165. final def synchronized[T0](arg0: => T0): T0
    Definition Classes
    AnyRef
  166. def tags: Map[String, Set[String]]
    Definition Classes
    AnyFlatSpecLike → Suite
  167. def testDataFor(testName: String, theConfigMap: ConfigMap): TestData
    Definition Classes
    AnyFlatSpecLike → Suite
  168. def testNames: Set[String]
    Definition Classes
    AnyFlatSpecLike → Suite
  169. def the[T](implicit arg0: ClassTag[T], pos: Position): ResultOfTheTypeInvocation[T]
    Definition Classes
    Matchers
  170. def theSameElementsAs(xs: GenTraversable[_]): ResultOfTheSameElementsAsApplication
    Definition Classes
    Matchers
  171. def theSameElementsInOrderAs(xs: GenTraversable[_]): ResultOfTheSameElementsInOrderAsApplication
    Definition Classes
    Matchers
  172. val theSameInstanceAs: TheSameInstanceAsPhrase
    Definition Classes
    Matchers
  173. val they: TheyWord
    Attributes
    protected
    Definition Classes
    AnyFlatSpecLike
  174. def thrownBy(fun: => Any): ResultOfThrownByApplication
    Definition Classes
    Matchers
  175. def toString(): String
    Definition Classes
    AnyFlatSpec → AnyRef → Any
  176. def traverse(res0: List[User]): Assertion

    Another interesting combinator is traverse, which is the composition of map and sequence.

    Traverse

    Another interesting combinator is traverse, which is the composition of map and sequence.

    All the optimizations made by sequence still apply when using traverse.

  177. val typeCheck: TypeCheckWord
    Definition Classes
    MatcherWords
  178. def typeCheckedConstraint[A, B](implicit equivalenceOfA: Equivalence[A], ev: <:<[B, A]): CanEqual[A, B]
    Definition Classes
    TripleEquals → TripleEqualsSupport
  179. implicit def unconstrainedEquality[A, B](implicit equalityOfA: Equality[A]): CanEqual[A, B]
    Definition Classes
    TripleEquals → TripleEqualsSupport
  180. val value: ValueWord
    Definition Classes
    Matchers
  181. final def wait(): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException])
  182. final def wait(arg0: Long, arg1: Int): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException])
  183. final def wait(arg0: Long): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException]) @native()
  184. def withClue[T](clue: Any)(fun: => T): T
    Definition Classes
    Assertions
  185. def withFixture(test: NoArgTest): Outcome
    Attributes
    protected
    Definition Classes
    TestSuite
  186. val writable: WritableWord
    Definition Classes
    MatcherWords

Deprecated Value Members

  1. def conversionCheckedConstraint[A, B](implicit equivalenceOfA: Equivalence[A], cnv: (B) => A): CanEqual[A, B]
    Definition Classes
    TripleEquals → TripleEqualsSupport
    Annotations
    @deprecated
    Deprecated

    (Since version 3.1.0) The conversionCheckedConstraint method has been deprecated and will be removed in a future version of ScalaTest. It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced.

  2. def convertEquivalenceToAToBConversionConstraint[A, B](equivalenceOfB: Equivalence[B])(implicit ev: (A) => B): CanEqual[A, B]
    Definition Classes
    TripleEquals → TripleEqualsSupport
    Annotations
    @deprecated
    Deprecated

    (Since version 3.1.0) The convertEquivalenceToAToBConversionConstraint method has been deprecated and will be removed in a future version of ScalaTest. It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced.

  3. def convertEquivalenceToBToAConversionConstraint[A, B](equivalenceOfA: Equivalence[A])(implicit ev: (B) => A): CanEqual[A, B]
    Definition Classes
    TripleEquals → TripleEqualsSupport
    Annotations
    @deprecated
    Deprecated

    (Since version 3.1.0) The convertEquivalenceToBToAConversionConstraint method has been deprecated and will be removed in a future version of ScalaTest. It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced.

  4. def lowPriorityConversionCheckedConstraint[A, B](implicit equivalenceOfB: Equivalence[B], cnv: (A) => B): CanEqual[A, B]
    Definition Classes
    TripleEquals → TripleEqualsSupport
    Annotations
    @deprecated
    Deprecated

    (Since version 3.1.0) The lowPriorityConversionCheckedConstraint method has been deprecated and will be removed in a future version of ScalaTest. It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced.

  5. final val styleName: String
    Definition Classes
    AnyFlatSpecLike → Suite
    Annotations
    @deprecated
    Deprecated

    (Since version 3.1.0) The styleName lifecycle method has been deprecated and will be removed in a future version of ScalaTest with no replacement.

Inherited from Section

Inherited from Matchers

Inherited from Explicitly

Inherited from MatcherWords

Inherited from Tolerance

Inherited from AnyFlatSpec

Inherited from AnyFlatSpecLike

Inherited from Documenting

Inherited from Alerting

Inherited from Notifying

Inherited from Informing

Inherited from CanVerb

Inherited from MustVerb

Inherited from ShouldVerb

Inherited from TestRegistration

Inherited from TestSuite

Inherited from Suite

Inherited from Serializable

Inherited from Assertions

Inherited from TripleEquals

Inherited from TripleEqualsSupport

Inherited from AnyRef

Inherited from Any

Ungrouped