calico
calico copied to clipboard
`SignallingRef` has to be upcast to `Signal` for syntax
This is annoying.
This should be fixed by https://github.com/armanbilge/calico/pull/183 shouldn't it?
Unfortunately not, this is really an FS2 issue.
//> using lib "co.fs2::fs2-core::3.6.1"
import cats.effect.IO
import cats.syntax.all.*
import fs2.concurrent.{Signal, SignallingRef}
def sigRef: SignallingRef[IO, String] = ???
@main def main =
(sigRef: Signal[IO, String]).void
sigRef.void
[error] ./signal.scala:13:3: value void is not a member of fs2.concurrent.SignallingRef[cats.effect.IO, String].
[error] An extension method was tried, but could not be fully constructed:
[error]
[error] cats.syntax.all.toFunctorOps[
[error] ([A] =>> fs2.concurrent.SignallingRef[cats.effect.IO, A])
[error] , String](sigRef)(cats.Invariant.catsApplicativeForArrow[F, A])
[error] sigRef.void
[error] ^^^^^^^^^^^
Error compiling project (Scala 3.2.2, JVM)
Compilation failed
But, I just had an idea how to fix it :)
But, I just had an idea how to fix it :)
Erm, spoke too soon :) I thought the problem is in FS2, but it's not really. You can't have a Functor
for SignallingRef
, so the implicit search gives up quickly and with good reason.
So it turns out, this is something that would need to be fixed in Cats 😂 the question is, if you have Foo[A] <: Bar[A]
, and Bar
implements Functor
but Foo
does not, should you be able to use functor syntax on Foo
? All the methods would return Bar
in that case.
I have a hard time imagining such a major change like that being made in Cats.
That said, maybe the fix is to add an .asSignal
method to SignallingRef
in FS2 /shrug
One more datapoint: experimented with the extension
-based encoding in Scala 3, ~~and it doesn't work there either. That's even harder to fix than the implicit class
syntax used in Scala 2. So I don't really think we can "fix" this, we can just make the upcast more convenient.~~
Edit: Ha wait, totally screwed that up. It does work! So that's very interesting 🤔
trait Functor[F[_]]:
extension [A](fa: F[A]) def map[B](f: A => B): F[B]
trait Foo[A]
object Foo:
given Functor[Foo] = ???
trait Bar[A] extends Foo[A]
def foo: Foo[String] = ???
def bar: Bar[String] = ???
@main def main =
foo.map(_.length)
bar.map(_.length)