cats-stm
cats-stm copied to clipboard
Instantiating Class which requires a TVar
Hello
Having upgraded from 0.8.0 to 0.11.0 I have what is either a question or an issue (I am unsure but raising it as an issue to hopefully at least get an answer).
I have a class that requires a TVar to be passed in at instantiation which is posing an import based issue.
class MyClass(val stuff: TVar[String]) { ... }
Previously (0.8.0) I could have an "import io.github.timwspence.cats.stm.TVar" at the top of my class, this no longer works and from the examples the approach is something akin to
val stm = STM.runtime[IO].unsafeRunSync()
import stm._
but this does not work outside of a class or object? Is there a way to reference the TVar class without having to instatiate the Stm Runtime?
Please advise if this is a legitimate issue, a misuse issue or just a misunderstanding? Karl
Hi Karl, this is a great question!
Firstly thanks for using cats-stm! :) And for trying to upgrade so quickly to 0.11.0 - that was impressively fast!
The answer to this is somewhat long but I'll do my best!
A bit of history
So in 0.8.0
and earlier I took some shortcuts and implemented STM using non-functional primitives and synchronized
. This is actually pretty bad for a cats-effect based library.
Since then, I've re-implemented the entire library based on CE's non-blocking Ref
. So we effectively have
class TVar[F[_], A] {
val state: Ref[F, A]
}
additionally we have some global state
trait STM[F[_]] {
val globalState: Ref[F, STMState]
}
This kind of sucks. So instead we made TVar
a dependent type:
trait STM[F[_]] {
val globalState: Ref[F, STMState]
//Note that TVar no longer has to be parameterized on F[_]
class TVar[A] {
val state: TVar[F, A]
}
}
Structuring applications with 0.11.0
As TVar
is now an inner class, you need to thread the STM runtime from the point where it is instantiated to where it is used to create TVar
s or commit Txn
s.
For methods, this generally means currying:
def foo[F[_], A](stm: STM[F])(tvar: stm.TVar[A] = ???
Class parameters are actually a quite tricky case as you can't have multiple parameter blocks. If TVar
were a type member then you could use the Aux
pattern but as it is an inner class, I'm actually not sure what you can do. I think the solution might just be for me to make TVar
a type member so that the Aux
pattern applies. I'll try to play around with it and let you know how I get on.
TLDR
There is not a way to reference TVar
without its enclosing STM[F]
instance. I believe that this is essential complexity rather than bad design.
In general the solution to this is multiple parameter blocks but in your use-case (class parameters) this is not possible. It definitely should be possible so I'll see what I can do to fix it.
And please do upgrade once I've fixed the issues! I know that 0.8.0
has an easier API to use but its internals really are quite unperformant and will steal threads from your compute pool to do blocking operations.
I hope that answers some of your questions at least. Do let me know if you have more questions!