fs2
fs2 copied to clipboard
`compile.to(ByteVector)` sometimes causes `java.lang.ClassCastException` in scala3
I encountered this in a cross built app and it worked for 2.12/2.13
Here is a scastie repro from @ChristopherDavenport via Discord
I attempted my own repro before I found Chris's, and to(ByteVector) worked in the case I came up with, for example
Here's a scala-cli repro
//> using scala "3.2.0"
//> using lib "org.http4s::http4s-client::0.23.16"
import cats.effect._
import cats.syntax.all._
import org.http4s._
import org.http4s.implicits._
import org.http4s.client._
import fs2._
import scodec.bits._
import org.typelevel.ci._
object CRC32 {
def hash(data: Array[Byte]): Long = {
val c = new java.util.zip.CRC32()
c.update(data)
c.getValue()
}
}
def buffered[F[_]: Concurrent](client: Client[F]): Client[F] = Client[F]{ (req: Request[F]) =>
Resource.eval(req.body.compile.to(ByteVector)).map(c => req.withBodyStream(Stream.chunk(c)).covary[F]).flatMap(client.run)
}
def hashed[F[_]: Concurrent](client: Client[F]): Client[F] = Client[F]{ (req: Request[F]) =>
Resource.eval(req.body.compile.to(ByteVector).map(_.toArray).map(CRC32.hash)).flatMap(hash =>
client.run(req.putHeaders("crc32" -> hash.toString))
)
}
val app = cats.data.Kleisli{(req: Request[IO]) => req.headers.get(CIString("crc32")).traverse_(IO.println) >> IO.pure(Response[IO]())}
val iClient = Client.fromHttpApp(app)
val req = Request[IO]().withEntity("Hello There!")
val client = hashed(buffered(iClient))
import cats.effect.unsafe.implicits.global
@main def main = client.run(req).use(_ => IO.unit).unsafeRunSync()
Turns out Collector was getting inferred as Nothing here https://discord.com/channels/632277896739946517/632310980449402880/1027012526178832404
Not sure if this is still the best place for this issue?
Checkpointing my minimization:
//> using scala "3.2.2-RC1-bin-20221003-5d1497d-NIGHTLY"
//> using lib "co.fs2::fs2-core::3.3.0"
import cats.effect._
import cats.syntax.all._
import fs2._
import scodec.bits._
def buffered[F[_]: Concurrent](body: Stream[F, Byte]): Resource[F, Unit] =
Resource.eval(body.compile.to(ByteVector)).map(c => Chunk.byteVector(c)).void
import cats.effect.unsafe.implicits.global
@main def main = buffered[IO](Stream(1.toByte)).use_.unsafeRunSync()