How to automatically generate a function to match a private case class family with implicit instances?

I have a private case family of classes that defines some rules that are deserialized from an external source. I also have a class with multiple instances to execute the actual logic, for example:

import scala.util.Try

sealed trait ReaderConfig
case class Substring(begin: Int, end: Int) extends ReaderConfig
case class Regex(expr: String) extends ReaderConfig

trait Read[M[_], RC <: ReaderConfig] {
  def apply(config: RC, raw: String): M[String]
}

object Read {
  implicit val TryReadSubstring: Read[Try, Substring] = (config: Substring, raw: String) => Try {
    raw.substring(config.begin, config.end)
  }
  implicit val TryReadRegex: Read[Try, Regex] = (config: Regex, raw: String) => Try {
    config.expr.r.findFirstIn(raw).get
  }

  trait Helper[RC <: ReaderConfig] {
    def as[M[_]](implicit read: Read[M, RC]): M[String]
  }

  def apply[RC <: ReaderConfig](config: RC)(raw: String) = new Helper[RC] {
    override def as[M[_]](implicit read: Read[M, RC]): M[String] = read.apply(config,raw)
  }
}

Now, using it with specific types, there is no problem finding the right implicit.

@ val ok: Try[String] = Read(Substring(0,1))("abc").as[Try]
ok: Try[String] = Success("a")
@ val Fail: Try[String] = Read(Substring(1000,9001))("abc").as[Try]
Fail: Try[String] = Failure(
  java.lang.StringIndexOutOfBoundsException: String index out of range: 9001
)

When I have a val that has the top attribute as type (for example, when I deserialize it as mentioned above), it cannot compile as expected:

@ val config: ReaderConfig = Substring(0,1)
config: ReaderConfig = Substring(0, 1)
@ val fail2: Try[String] = Read(config)("abc").as[Try]
cmd8.sc:1: could not find implicit value for parameter read: $sess.cmd2.Read[scala.util.Try,$sess.cmd1.ReaderConfig]
val fail2: Try[String] = Read(config)("abc").as[Try]
                                               ^
Compilation Failed

The only solution I came across is to write a function that will match the actual types with the correct instances, for example:

val tryRead: ReaderConfig => String => Try[String] = {rc => raw => rc match {
  case s: Substring => Read[Substring](s)(raw).as[Try]
  case r: Regex => Read[Regex](r)(raw).as[Try]
}}

, .

@ tryRead(config)("abc")

res9: Try[String] = Success("a")

, case s, , , , .

, ? , case .

+4
1

, , typeclass - (ReaderConfig), . ReaderConfig s. , Cats some none Option: 42.some Option[Int], Some[Int].

+2

Source: https://habr.com/ru/post/1685192/


All Articles