How to convert cancellable subscription to Cat's IO

Viewed 165

In one of my projects, I have to use a callback-based asynchronous API. I wanted to introduce cats IO, yet I have some troubles with it:

IO.cancelable { cb =>
  val subscription = createSubscription(
     ...,
     (msg: Message) => cb { Right("SomeResult") }
  )
  IO {subscription.unsubscribe()}
}

It looks quite nice, however I also need to unsubscribe after receiving a message. Unfortunately IO {subscription.unsubscribe()} is evaluated only in the case of cancellation. I can make a quick workaround, but it seems very counter-intuitive :

IO.cancelable { cb =>
  lazy val subscription = createSubscription(
     ...,
     (msg: Message) => cb {
       subscription.unsubscribe() //I can't be really sure if subscription is already initialized
       Right("SomeResult")
     }
  )
  subscribe //I have to ensure evalutaion of lazy val
  IO {subscription.unsubscribe()}
}

I have checked cats-effects doc, sadly I didn't find anything that could help me with this problem. I can think of some bullet-proof solutions for this issue, yet it seems so typical that I can't believe that it wasn't already solved.

Is there any simple way to achieve this resource release behavior using cats?

1 Answers

Try Resources:

import cats.effect.{ IO, Resource }

val subscriptionResource: Resource[IO, Subscription] =
  Resource.make {
    IO { createSubscription(...) }
  } { subscription =>
    IO { subscription.unsubscribe() }
  }

used as:

subscriptionResource.use { subscription =>
  // your IO routine
}

This is a (composable) way of making sure that your resource will be released no matter if there is an error or success. (Though, I don't remember what happens in case of cancellation as it was a trick subject - probably in such case release block wouldn't be called).

Related