kioba
12/20/2019, 5:50 PMFunctor instance Queue
ticket. I know this should be a fairly simple task sorry if I ask something obbvious :S ,
I have no previous experience with the Queue
solution so I looked into the mentioned ZIO Queue
(to be precise the trait ZQueue[-RA, +EA, -RB, +EB, -A, +B]
) implementation.
If I understand correctly we currently dont have a ZQueue
equivalent implementation in arrow, our queue is representing a general zio.internal.MutableConcurrentQueue
.
To be able to implement the Functor
we have to make sure the Queue captures the previous queue reference and the f function to be able to deplete the original queue.
TL;DR: we have to create a ZQueue
equivalent in arrow and implement Functor
on it instead of Queue.
I believe it is a slightly more work than a simple typeclass. I would be super happy to implement it, but before that I just want to make sure I am on the right path here 🙂 @simon.vergauwen/*
* Transforms elements dequeued from this queue with a function.
*/
def map[C](f: B => C): ZQueue[RA, EA, RB, EB, A, C] =
new ZQueue[RA, EA, RB, EB, A, C] {
def take: ZIO[RB, EB, C] =
self.take.map(f)
def takeAll: ZIO[RB, EB, List[C]] =
self.takeAll.map(_.map(f))
def takeUpTo(max: Int): ZIO[RB, EB, List[C]] =
self.takeUpTo(max).map(_.map(f))
}
simon.vergauwen
12/20/2019, 5:53 PMRef
, Semaphore
, Promise
, or MVar
you will see they’re all defined as `interface`s that define the contract.
If you have a constraint of fun CF(): Concurrent<F>
in your interface you can implement the above method as following.
fun <B> map(f: (A) -> B): Queue<F, B> = CF().run {
object : Queue<F, B> {
override fun take(): Kind<F, B> =
this@map.take().map(f)
...
}
}
kioba
12/20/2019, 6:08 PMqueue.map { it.toString() }.shutdown()
will not shut down the original queue.
Unless we override all the functions to delegate the call to the original or
object : Queue<F, B> by this@map
simon.vergauwen
12/20/2019, 6:19 PMAh I think I see the problem you’re having. If you look atYes, you need to split,Ref
,Semaphore
, orPromise
you will see they’re all defined as `interface`s that define the contract.MVar
Queue
in interface and implementations as done int he other concurrency primitives. They probably also deserve a Functor
, FunctorK
etc.will not shut down the original queue.Should not be possible. It’s not a new
Queue
, we just wrap the Queue
with Functor#map
kioba
01/02/2020, 11:36 PMoverride fun <A, B> QueueOf<F, A>.imap(f: (A) -> B, g: (B) -> A): Queue<F, B> =
object : Queue<F, B> {
override fun offer(a: B): Kind<F, Unit> = this@imap.fix().offer(g(a))
override fun take(): Kind<F, B> = with(functorF()) {
this@imap.fix().take().map(f)
}
// ....
}
Without knowing the g: (B) -> A
, we can not offer for the original Queue.
Should we extend the type arguments? what would be your suggestion?interface QueueFunctor<F, A> : Functor<QueuePartialOf<F, A>> {
fun functorF(): Functor<F>
override fun <B, C> QueueOf<F, A, B>.map(f: (B) -> C): Queue<F, A, C> =
object : Queue<F, A, C> {
override fun offer(a: A): Kind<F, Unit> = this@map.fix().offer(a)
override fun take(): Kind<F, C> = with(functorF()) {
this@map.fix().take().map(f)
}
}
}
but I am not sure how could I bridge the gap between the FS2 and Zio 🤔