@@ -53,10 +53,11 @@ Data Sources take two type parameters:
5353import cats .data .NonEmptyList
5454import cats .effect .ConcurrentEffect
5555
56- trait DataSource [Identity , Result ]{
57- def name : String
58- def fetch [F [_] : ConcurrentEffect ](id : Identity ): F [Option [Result ]]
59- def batch [F [_] : ConcurrentEffect ](ids : NonEmptyList [Identity ]): F [Map [Identity , Result ]]
56+ trait DataSource [F [_], Identity , Result ]{
57+ def data : Data [Identity , Result ]
58+ def CF : ConcurrentEffect [F ]
59+ def fetch (id : Identity ): F [Option [Result ]]
60+ def batch (ids : NonEmptyList [Identity ]): F [Map [Identity , Result ]]
6061}
6162```
6263
@@ -65,31 +66,42 @@ Returning `ConcurrentEffect` instances from the fetch methods allows us to speci
6566We'll implement a dummy data source that can convert integers to strings. For convenience, we define a ` fetchString ` function that lifts identities (` Int ` in our dummy data source) to a ` Fetch ` .
6667
6768``` scala
69+ import cats ._
6870import cats .data .NonEmptyList
6971import cats .effect ._
7072import cats .instances .list ._
73+ import cats .implicits ._
7174import cats .syntax .all ._
7275
7376import fetch ._
7477
75- object ToStringSource extends DataSource [ Int , String ]{
76- override def name = " ToString "
78+ def latency [ F [_] : Effect ]( milis : Long ) : F [ Unit ] =
79+ Effect [ F ].delay( Thread .sleep(milis))
7780
78- override def fetch [F [_] : ConcurrentEffect ](id : Int ): F [Option [String ]] = {
79- Sync [F ].delay(println(s " --> [ ${Thread .currentThread.getId}] One ToString $id" )) >>
80- Sync [F ].delay(println(s " <-- [ ${Thread .currentThread.getId}] One ToString $id" )) >>
81- Sync [F ].pure(Option (id.toString))
82- }
81+ object ToString extends Data [Int , String ] {
82+ def name = " To String"
83+
84+ def source [F [_] : ConcurrentEffect ]: DataSource [F , Int , String ] = new DataSource [F , Int , String ]{
85+ override def data = ToString
86+
87+ override def CF = ConcurrentEffect [F ]
8388
84- override def batch [F [_] : ConcurrentEffect ](ids : NonEmptyList [Int ]): F [Map [Int , String ]] = {
85- Sync [F ].delay(println(s " --> [ ${Thread .currentThread.getId}] Batch ToString $ids" )) >>
86- Sync [F ].delay(println(s " <-- [ ${Thread .currentThread.getId}] Batch ToString $ids" )) >>
87- Sync [F ].pure(ids.toList.map(i => (i, i.toString)).toMap)
89+ override def fetch (id : Int ): F [Option [String ]] = for {
90+ _ <- CF .delay(println(s " --> [ ${Thread .currentThread.getId}] One ToString $id" ))
91+ _ <- latency(100 )
92+ _ <- CF .delay(println(s " <-- [ ${Thread .currentThread.getId}] One ToString $id" ))
93+ } yield Option (id.toString)
94+
95+ override def batch (ids : NonEmptyList [Int ]): F [Map [Int , String ]] = for {
96+ _ <- CF .delay(println(s " --> [ ${Thread .currentThread.getId}] Batch ToString $ids" ))
97+ _ <- latency(100 )
98+ _ <- CF .delay(println(s " <-- [ ${Thread .currentThread.getId}] Batch ToString $ids" ))
99+ } yield ids.toList.map(i => (i, i.toString)).toMap
88100 }
89101}
90102
91103def fetchString [F [_] : ConcurrentEffect ](n : Int ): Fetch [F , String ] =
92- Fetch (n, ToStringSource )
104+ Fetch (n, ToString .source )
93105```
94106
95107## Creating a runtime
@@ -125,8 +137,8 @@ import scala.concurrent.duration._
125137// import scala.concurrent.duration._
126138
127139Fetch .run[IO ](fetchOne).unsafeRunTimed(5 .seconds)
128- // --> [179 ] One ToString 1
129- // <-- [179 ] One ToString 1
140+ // --> [109 ] One ToString 1
141+ // <-- [109 ] One ToString 1
130142// res0: Option[String] = Some(1)
131143```
132144
@@ -145,26 +157,32 @@ When executing the above fetch, note how the three identities get batched and th
145157
146158``` scala
147159Fetch .run[IO ](fetchThree).unsafeRunTimed(5 .seconds)
148- // --> [179 ] Batch ToString NonEmptyList(1, 2, 3)
149- // <-- [179 ] Batch ToString NonEmptyList(1, 2, 3)
160+ // --> [109 ] Batch ToString NonEmptyList(1, 2, 3)
161+ // <-- [109 ] Batch ToString NonEmptyList(1, 2, 3)
150162// res1: Option[(String, String, String)] = Some((1,2,3))
151163```
152164
153165Note that the ` DataSource#batch ` method is not mandatory, it will be implemented in terms of ` DataSource#fetch ` if you don't provide an implementation.
154166
155167``` scala
156- object UnbatchedToStringSource extends DataSource [Int , String ]{
157- override def name = " UnbatchedToString"
168+ object UnbatchedToString extends Data [Int , String ] {
169+ def name = " Unbatched to string"
170+
171+ def source [F [_] : ConcurrentEffect ] = new DataSource [F , Int , String ] {
172+ override def data = UnbatchedToString
173+
174+ override def CF = ConcurrentEffect [F ]
158175
159- override def fetch [F [_] : ConcurrentEffect ](id : Int ): F [Option [String ]] = {
160- Sync [F ].delay(println(s " --> [ ${Thread .currentThread.getId}] One UnbatchedToString $id" )) >>
161- Sync [F ].delay(println(s " <-- [ ${Thread .currentThread.getId}] One UnbatchedToString $id" )) >>
162- Sync [F ].pure(Option (id.toString))
176+ override def fetch (id : Int ): F [Option [String ]] =
177+ CF .delay(println(s " --> [ ${Thread .currentThread.getId}] One UnbatchedToString $id" )) >>
178+ latency(100 ) >>
179+ CF .delay(println(s " <-- [ ${Thread .currentThread.getId}] One UnbatchedToString $id" )) >>
180+ CF .pure(Option (id.toString))
163181 }
164182}
165183
166184def unbatchedString [F [_] : ConcurrentEffect ](n : Int ): Fetch [F , String ] =
167- Fetch (n, UnbatchedToStringSource )
185+ Fetch (n, UnbatchedToString .source )
168186```
169187
170188Let's create a tuple of unbatched string requests.
@@ -178,12 +196,12 @@ When executing the above fetch, note how the three identities get requested in p
178196
179197``` scala
180198Fetch .run[IO ](fetchUnbatchedThree).unsafeRunTimed(5 .seconds)
181- // --> [179 ] One UnbatchedToString 1
182- // --> [181 ] One UnbatchedToString 3
183- // <-- [181 ] One UnbatchedToString 3
184- // --> [182 ] One UnbatchedToString 2
185- // <-- [182 ] One UnbatchedToString 2
186- // <-- [179 ] One UnbatchedToString 1
199+ // --> [109 ] One UnbatchedToString 1
200+ // --> [111 ] One UnbatchedToString 3
201+ // --> [112 ] One UnbatchedToString 2
202+ // <-- [109 ] One UnbatchedToString 1
203+ // <-- [111 ] One UnbatchedToString 3
204+ // <-- [112 ] One UnbatchedToString 2
187205// res2: Option[(String, String, String)] = Some((1,2,3))
188206```
189207
@@ -192,23 +210,30 @@ Fetch.run[IO](fetchUnbatchedThree).unsafeRunTimed(5.seconds)
192210If we combine two independent fetches from different data sources, the fetches can be run in parallel. First, let's add a data source that fetches a string's size.
193211
194212``` scala
195- object LengthSource extends DataSource [String , Int ]{
196- override def name = " Length"
213+ object Length extends Data [String , Int ] {
214+ def name = " Length"
197215
198- override def fetch [F [_] : ConcurrentEffect ](id : String ): F [Option [Int ]] = {
199- Sync [F ].delay(println(s " --> [ ${Thread .currentThread.getId}] One Length $id" )) >>
200- Sync [F ].delay(println(s " <-- [ ${Thread .currentThread.getId}] One Length $id" )) >>
201- Sync [F ].pure(Option (id.size))
202- }
203- override def batch [F [_] : ConcurrentEffect ](ids : NonEmptyList [String ]): F [Map [String , Int ]] = {
204- Sync [F ].delay(println(s " --> [ ${Thread .currentThread.getId}] Batch Length $ids" )) >>
205- Sync [F ].delay(println(s " <-- [ ${Thread .currentThread.getId}] Batch Length $ids" )) >>
206- Sync [F ].pure(ids.toList.map(i => (i, i.size)).toMap)
216+ def source [F [_] : ConcurrentEffect ] = new DataSource [F , String , Int ] {
217+ override def data = Length
218+
219+ override def CF = ConcurrentEffect [F ]
220+
221+ override def fetch (id : String ): F [Option [Int ]] = for {
222+ _ <- CF .delay(println(s " --> [ ${Thread .currentThread.getId}] One Length $id" ))
223+ _ <- latency(100 )
224+ _ <- CF .delay(println(s " <-- [ ${Thread .currentThread.getId}] One Length $id" ))
225+ } yield Option (id.size)
226+
227+ override def batch (ids : NonEmptyList [String ]): F [Map [String , Int ]] = for {
228+ _ <- CF .delay(println(s " --> [ ${Thread .currentThread.getId}] Batch Length $ids" ))
229+ _ <- latency(100 )
230+ _ <- CF .delay(println(s " <-- [ ${Thread .currentThread.getId}] Batch Length $ids" ))
231+ } yield ids.toList.map(i => (i, i.size)).toMap
207232 }
208233}
209234
210235def fetchLength [F [_] : ConcurrentEffect ](s : String ): Fetch [F , Int ] =
211- Fetch (s, LengthSource )
236+ Fetch (s, Length .source )
212237```
213238
214239And now we can easily receive data from the two sources in a single fetch.
@@ -222,10 +247,10 @@ Note how the two independent data fetches run in parallel, minimizing the latenc
222247
223248``` scala
224249Fetch .run[IO ](fetchMulti).unsafeRunTimed(5 .seconds)
225- // --> [181 ] One ToString 1
226- // <-- [181 ] One ToString 1
227- // --> [180 ] One Length one
228- // <-- [180 ] One Length one
250+ // --> [109 ] One ToString 1
251+ // --> [110 ] One Length one
252+ // <-- [109 ] One ToString 1
253+ // <-- [110 ] One Length one
229254// res3: Option[(String, Int)] = Some((1,3))
230255```
231256
@@ -246,8 +271,8 @@ While running it, notice that the data source is only queried once. The next tim
246271
247272``` scala
248273Fetch .run[IO ](fetchTwice).unsafeRunTimed(5 .seconds)
249- // --> [182 ] One ToString 1
250- // <-- [182 ] One ToString 1
274+ // --> [111 ] One ToString 1
275+ // <-- [111 ] One ToString 1
251276// res4: Option[(String, String)] = Some((1,1))
252277```
253278
@@ -258,7 +283,6 @@ Fetch.run[IO](fetchTwice).unsafeRunTimed(5.seconds)
258283
259284For more in-depth information take a look at our [ documentation] ( http://47deg.github.io/fetch/docs.html ) .
260285
261-
262286## Fetch in the wild
263287
264288If you wish to add your library here please consider a PR to include it in the list below.
0 commit comments