https://kotlinlang.org logo
#arrow-contributors
Title
# arrow-contributors
k

kioba

12/20/2019, 5:50 PM
I am currently working on the
Functor 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
zio solution is just this:
Copy code
/*
   * 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))
    }
s

simon.vergauwen

12/20/2019, 5:53 PM
Hey @kioba! Awesome that you want to contribute to this!
Ah I think I see the problem you’re having. If you look at
Ref
,
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.
Copy code
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)

     ...
  }
}
I’m be happy to help with any questions you have along the way! 👍
k

kioba

12/20/2019, 6:08 PM
🤔 that is a fair point, the concern I have is:
Copy code
queue.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
Copy code
object : Queue<F, B> by this@map
not sure if delegating by this@map works tho 😅 What I was thinking maybe to introduce a Queue interface to make sure we never skip any important function?
will look into FS2! Thanks for the link
👍 1
s

simon.vergauwen

12/20/2019, 6:19 PM
Ah I think I see the problem you’re having. If you look at
Ref
,
Semaphore
,
Promise
, or
MVar
you will see they’re all defined as `interface`s that define the contract.
Yes, you need to split
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
k

kioba

01/02/2020, 11:36 PM
@simon.vergauwen I looked into the FS2 Queue but they only define imap only 🤔 . The reason for that is we need to offer any element to the original queue. ZQueue solves this with extending the type parameters so the offer only takes the original types but take can return any mapped type. my current implementation is based on FS2:
Copy code
override 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?
extending the type arguments would allow us to provide the map functions:
Copy code
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 🤔
created a PR for the second solution: https://github.com/arrow-kt/arrow/pull/1904/files
7 Views