Derives Mirror.ProductOf got error

Hey, I am new to Scala 3 Mirror code. I was trying to learn & develope my framework. But got error when I do:
final case class AppEnv(
logger: Logger[IO],
config: Config,
userService: UserService[IO]
) derives Mirror.ProductOf
Error:
type Product in derives clause of AppEnv has no type parameters
) derives Mirror.ProductOf

I put my whole code in one page, hopefully it would be easier for people to debug it. Thanks.

import cats.effect.*
import cats.syntax.all.*
import scala.deriving.Mirror
import scala.compiletime.{erasedValue, summonInline, constValue, summonAll, summonFrom}

// ---------- Layer definition ----------
final case class Layer[F[], A](resource: Resource[F, A]) {
def map[B](f: A => B): Layer[F, B] = Layer(resource.map(f))
def flatMap[B](f: A => Layer[F, B])(using MonadCancel[F, Throwable]): Layer[F, B] =
Layer(resource.flatMap(a => f(a).resource))
}
object Layer {
def liftF[F[
], A](fa: F[A]): Layer[F, A] = Layer(Resource.eval(fa))
}

// ---------- ZLayerLike abstraction ----------
trait ZLayerLike[F[_], A] {
def layer: Layer[F, A]
}

// ---------- Module pattern ----------
trait Module[F[], A] {
def make: Resource[F, A]
}
object Module {
given toZLayer[F[
], A](using m: Module[F, A]): ZLayerLike[F, A] =
new ZLayerLike[F, A] {
def layer: Layer[F, A] = Layer(m.make)
}
}

// ---------- AutoLayer derivation using Scala 3 Mirror ----------
trait AutoLayer[F[], A] {
def make: Layer[F, A]
}
object AutoLayer {
def apply[F[
], A](using auto: AutoLayer[F, A]): Layer[F, A] = auto.make

inline given derived[F[_], A](using n: MonadCancel[F, Throwable], m: Mirror.ProductOf[A]): AutoLayer[F, A] =
new AutoLayer[F, A] {
def make: Layer[F, A] =
makeFromTuple[F, m.MirroredElemTypes].map(m.fromProduct)
}

private inline def makeFromTuple[F[_], Elems <: Tuple](using n: MonadCancel[F, Throwable]): Layer[F, Tuple] =
reduceLayers[F](summonAllLayers[F, Elems])

private inline def summonAllLayers[F[_], T <: Tuple]: List[Layer[F, ?]] =
inline erasedValue[T] match
case _: EmptyTuple => Nil
case _: (h *: t) =>
val headLayer: Layer[F, h] = summonZLayerOrAuto[F, h]
headLayer.asInstanceOf[Layer[F, ?]] :: summonAllLayers[F, t]

private inline def summonZLayerOrAuto[F[_], A]: Layer[F, A] =
summonFrom {
case z: ZLayerLike[F, A] => z.layer
case a: AutoLayer[F, A] => a.make
}

private def reduceLayers[F[]](layers: List[Layer[F, ?]])(using n: MonadCancel[F, Throwable]): Layer[F, Tuple] =
layers match {
case Nil => Layer(Resource.pure(EmptyTuple.asInstanceOf[Tuple]))
case head :: tail =>
tail.foldLeft(head.map(Tuple1(
))) { (acc, next) =>
acc.flatMap(t1 => next.map(t2 => t1 ++ Tuple1(t2)))
}
}
}

// ---------- Example service modules ----------
trait Logger[F[]] {
def info(msg: String): F[Unit]
}
object Logger {
def makeResource[F[
]: Sync]: Resource[F, Logger[F]] =
Resource.pure(new Logger[F] {
def info(msg: String): F[Unit] = Sync[F].delay(println(s"[info] $msg"))
})

given loggerModule[F[_]: Sync]: Module[F, Logger[F]] with
def make: Resource[F, Logger[F]] = makeResource[F]
}

final case class Config(appName: String)
object Config {
def load[F[_]: Sync]: F[Config] = Sync[F].pure(Config(“MyApp”))

given configModule[F[_]: Sync]: Module[F, Config] with
def make: Resource[F, Config] = Resource.eval(load[F])
}

trait UserService[F[]] {
def doSomething: F[Unit]
}
object UserService {
def makeResource[F[
]: Sync](config: Config): Resource[F, UserService[F]] =
Resource.pure(new UserService[F] {
def doSomething: F[Unit] = Sync[F].delay(println(“User did something”))
})

given userServiceModule[F[_]: Sync](using config: Config): Module[F, UserService[F]] with
def make: Resource[F, UserService[F]] = makeResource(config)
}

// ---------- Composite environment ----------
final case class AppEnv(
logger: Logger[IO],
config: Config,
userService: UserService[IO]
) derives Mirror.ProductOf

given AutoLayer[IO, AppEnv] = AutoLayer.derived

// ---------- Entry point ----------
object Main extends IOApp.Simple {
val appLayer: Layer[IO, AppEnv] = AutoLayer[IO, AppEnv]

override def run: IO[Unit] =
appLayer.resource.use { env =>
for {
_ ← env.logger.info(s"App name: ${env.config.appName}")
_ ← env.userService.doSomething
} yield ()
}
}

Welcome to the Scala community, @weilizhi

It might be helpful if you put your code in Scastie and share the link here.

You can also go into the “Build Settings” and add your dependencies there (looks like you are using ZIO or Cats Effect maybe?)

You can also check Scala Discord to get more detailed help.

2 Likes

All case classes already have a Mirror.ProductOf instance automatically defined for them.
You should not derives it.

1 Like

Thanks, just tried Scastie, doesn’t look working. I couldn’t format the code and it doesn’t recognize cats even after I added cats-mtl and cats-effect libs. I will find it to ask quesiton in discord.

Here’s the link

I did try to remove derives, but then
given AutoLayer[IO, AppEnv] = AutoLayer.derived gives me error:
cannot reduce summonFrom with
patterns : case given z @ _:core.ZLayerLike[cats.effect.IO, logging.Logger[cats.effect.IO]]
case given a @ _:core.AutoLayer[cats.effect.IO, logging.Logger[cats.effect.IO]]
given AutoLayer[IO, AppEnv] = AutoLayer.derived

Right, now you have an issue with your derivation logic.

You would need to debug that, and try to reduce it to a minimal example, and then properly format the code if you want folks to take a look at it.

2 Likes