I have two overloaded method having following signatures -
def fun(x: Seq[String]): Future[Seq[Int]] = ???
def fun(x: Seq[(String, String)]): Future[Seq[Int]] = ???
Due to type erasure, these methods can't be overloaded and hence showing compilation error. I tried using typetags as a workaround -
def fun[t: TypeTag](values: Seq[T]): Future[Seq[Int]] = {
typeOf[T] match {
case t if t =:= typeOf[String] => ???
case t if t =:= typeOf[(String, String)] => ???
case _ => ??? // Should not come here
}
}
There are two problems with this approach that I am facing -
How can I use the values in the Seq ?
How can I avoid Should not come here case ?
Thanks in advance.
Try DummyImplicit approach instead of TypeTag
def fun(x: Seq[String]): Future[Seq[Int]] = ???
def fun(x: Seq[(String, String)])(implicit ev: DummyImplicit): Future[Seq[Int]] = ???
How can I use the values in the Seq ?
Note even though TypeTag enables overcoming type erasure I do not think compiler inserts a cast automatically, so you would have manually call asInstanceOf or equivalent
case t if t =:= typeOf[String] =>
val vs: Seq[String] = values.asInstanceOf[Seq[String]]
???
As a side note, TypeTag requires dependency on scala-reflect.jar (which might be a disadvantage, say, if we care about package size).
For method overloading you can use
DummyImplicit (see #MarioGalic's answer)
def fun(x: Seq[String]): Future[Seq[Int]] = ???
def fun(x: Seq[(String, String)])(implicit ev: DummyImplicit): Future[Seq[Int]] = ???
fun(Seq("a", "b"))
fun(Seq(("a", "b"), ("c", "d")))
type class
trait FunTypeclass[A] {
def fun(x: A): Future[Seq[Int]]
}
object FunTypeclass {
implicit val string: FunTypeclass[Seq[String]] = x => ???
implicit val stringTuple2: FunTypeclass[Seq[(String, String)]] = x => ???
}
def fun[A](x: A)(implicit ftc: FunTypeclass[A]): Future[Seq[Int]] = ftc.fun(x)
fun(Seq("a", "b"))
fun(Seq(("a", "b"), ("c", "d")))
or
magnet
import scala.language.implicitConversions
trait FunMagnet {
def fun(): Future[Seq[Int]]
}
object FunMagnet {
implicit def fromString(x: Seq[String]): FunMagnet = () => ???
implicit def fromStringTuple2(x: Seq[(String, String)]): FunMagnet = () => ???
}
def fun(x: FunMagnet): Future[Seq[Int]] = x.fun()
fun(Seq("a", "b"))
fun(Seq(("a", "b"), ("c", "d")))
Overloading methods based on generics
Common practice for this situation is to use Magnet Pattern
Related
When I try to call the following function:
def sortBy[T, R](seq: Seq[T], by: T => R)(implicit ordering: Ordering[R]): Seq[T] = {
...
}
with this:
case class MyClass(f1: Int, f2: Int)
val o1 = MyClass(1, 2)
val o2 = MyClass(3, 4)
sortBy(Seq(o1, o2), x => x.f1)
I get compilation error "cannot resolve symbol f1"
However when I call it with explicit types it works:
sortBy[MyClass,Int](...)
My question is why scala cannot infer these types automatically?
As one of the ways to solve
def sortBy[T, R](seq: Seq[T], by: T => R)(implicit ordering: Ordering[R]): Seq[T] = ???
case class MyClass(f1: Int, f2: Int)
val o1 = MyClass(1, 2)
val o2 = MyClass(3, 4)
def orderFunc(a: MyClass): Int = a.f1
sortBy(Seq(o1, o2), orderFunc)
The goal is to lazily evaluate a collection of functions stopping on and returning the first happy result. Computation should be sequential. Here is my attempt
def f1(i: Int): Either[String, Int] = {println(s"f1($i)"); Left("boom-f1") }
def f2(i: Int): Either[String, Int] = {println(s"f2($i)"); Left("boom-f2") }
def f3(i: Int): Either[String, Int] = {println(s"f3($i)"); Right(i) }
val in = 42
(f1(in) #:: f2(in) #:: f3(in) #:: Stream.empty) collectFirst { case Right(x) => x } toRight("boom")
which outputs
f1(42)
f2(42)
f3(42)
res0: Either[String,Int] = Right(42)
where we see all three executed, whilst
def f1(i: Int): Either[String, Int] = {println(s"f1($i)"); Right(i) }
def f2(i: Int): Either[String, Int] = {println(s"f2($i)"); Right(i) }
def f3(i: Int): Either[String, Int] = {println(s"f3($i)"); Right(i) }
would output
f1(42)
res0: Either[String,Int] = Right(42)
where we see only one executed.
Does cats provide abstraction for such lazy failure-biased traversal?
You can use the Ior data type which is an inclusive-or relationship between two data types.
import cats.data.Ior
import cats.implicits._
import scala.annotation.tailrec
object Main2 {
def main(args: Array[String]) : Unit = {
def f1(i: Int): Either[String, Int] = {println(s"f1($i)"); Left("boom-f1") }
def f2(i: Int): Either[String, Int] = {println(s"f2($i)"); Left("boom-f2") }
def f3(i: Int): Either[String, Int] = {println(s"f3($i)"); Right(i) }
def traverseLazy(input: Int, list: List[Int => Either[String, Int]]): Ior[List[String], Int] = {
#tailrec
def go(ls: List[Int => Either[String, Int]], acc: List[String]): Ior[List[String], Int] = ls match {
case x :: xs => x(input) match {
case Left(error) => go(xs, error :: acc)
case Right(value) => if (ls.isEmpty) value.rightIor else Ior.both(acc, value)
}
case Nil => acc.leftIor
}
go(list, List.empty)
}
val res = traverseLazy(42, List(f1, f2, f3)).fold(
_.intercalate("\n"),
res => s"succeeded with $res",
(errors, res) => s"completed successfully with res $res but some errors were also found: ${errors.intercalate(", ")}")
println(res)
}
}
Ah I was overcomplicating, orElse already implements such semantics and gives simply
f1(in) orElse f2(in) orElse f3(in) orElse Left("boom")
as it takes by-name parameter.
Given the following from Travis Brown's educational and well-written Type classes and generic derivation:
case class Person(name: String, age: Double)
trait Parser[A] {
def apply(s: String): Option[A]
}
implicit val hnilParser: Parser[HNil] = new Parser[HNil] {
def apply(s: String): Option[HNil] = if(s.isEmpty) Some(HNil) else None
}
implicit def hconsParser[H: Parser, T <: HList: Parser]: Parser[H :: T] = new Parser[H :: T] {
def apply(s: String): Option[H :: T] = s.split(",").toList match {
case cell +: rest => for {
head <- implicitly[Parser[H]].apply(cell)
tail <- implicitly[Parser[T]].apply(rest.mkString(","))
} yield head :: tail
}
}
implicit val stringParser: Parser[String] = new Parser[String] {
def apply(s: String): Option[String] = Some(s)
}
implicit val intParser: Parser[Int] = new Parser[Int] {
def apply(s: String): Option[Int] = Try(s.toInt).toOption
}
implicit val doubleParser: Parser[Double] = new Parser[Double] {
def apply(s: String): Option[Double] = Try(s.toDouble).toOption
}
implicit val booleanParser: Parser[Boolean] = new Parser[Boolean] {
def apply(s: String): Option[Boolean] = Try(s.toBoolean).toOption
}
implicit def caseClassParser[A, R <: HList](implicit gen: Generic[A] { type Repr = R },
reprParser: Parser[R]): Parser[A] =
new Parser[A] {
def apply(s: String): Option[A] = reprParser.apply(s).map(gen.from)
}
object Parser {
def apply[A](s: String)(implicit parser: Parser[A]): Option[A] = parser(s)
}
implicit val stringParser: Parser[String] = new Parser[String] {
def apply(s: String): Option[String] = Some(s)
}
implicit val intParser: Parser[Int] = new Parser[Int] {
def apply(s: String): Option[Int] = Try(s.toInt).toOption
}
implicit val doubleParser: Parser[Double] = new Parser[Double] {
def apply(s: String): Option[Double] = Try(s.toDouble).toOption
}
I was curious to try to get a Parser[X] where X is a case class with a Person argument, i.e. a case class:
case class PersonWrapper(person: Person, x: Int)
Yet I get an error:
scala> Parser[PersonWrapper]("kevin,66,42")
<console>:15: error: diverging implicit expansion for type net.Test.Parser[net.Test.PersonWrapper]
starting with method caseClassParser in object Test
Parser[PersonWrapper]("kevin,66,42")
^
First, why does this divergent implicit error occur?
Secondly, is it possible to use the above code to get a Parser[PersonWrapper]?
Secondly, is it possible to use the above code to get a Parser[PersonWrapper]?
No, just skip to the end of the article:
scala> case class BookBook(b1: Book, b2: Book)
defined class BookBook
scala> Parser[BookBook]("Hamlet,Shakespeare")
res7: Option[BookBook] = None
Our format doesn’t support any kind of nesting (at least we haven’t said anything about nesting, and it wouldn’t be trivial), so we don’t actually know how to parse a string into a BookBook...
The problem is that cell in case cell +: rest will only ever be a string with no commas that is passed to implicitly[Parser[H]].apply(cell). For PersonWrapper, this means that the very first cell will attempt to do this:
implicitly[Parser[PersonWrapper]].apply("kevin")
Which will obviously fail to parse. In order to get nested parsers to work, you would need some way to group the cells together prior to applying a Parser[H] to them.
In Scalaz, is there an easy way to convert an instance of Writer[W, A] (which is an alias for WriterT[Id, W, A]) to WriterT[F, W, A]?
I'm looking for something similar to the optionT function combined with point, but for writers instead:
E.g.
// Similar situation, but using OptionT
val opt: Option[String] = Some("log")
val optT: OptionT[IO, String] = optionT(opt.point[IO])
// Case in hand, using WriterT
val w: Writer[String, Unit] = "log".tell
val wt: WriterT[IO, String, Unit] = ???
// Similar scenario
val w: Writer[String, Int] = 3.set("log")
val wt: WriterT[IO, String, Int] = ???
Some monadic types have lift methods that help with this kind of operation:
import scalaz._, Scalaz._, effect.IO
val stateIO: StateT[IO, Int, Unit] = put(10).lift[IO]
Writer doesn't, but you can use the Hoist instance for WriterT to accomplish the same thing:
type StringWriter[F[_], A] = WriterT[F, String, A]
def fromId[F[_]: Applicative]: Id ~> F = new (Id ~> F) {
def apply[A](a: A) = a.point[F]
}
val w: Writer[String, Unit] = "log".tell
val wt: WriterT[IO, String, Unit] = Hoist[StringWriter].hoist(fromId[IO]).apply(w)
This isn't terribly convenient, but that's something you have to get used to when working with monad transformers.
As an exercise, I am trying to see if I can take a List[Any] and "cast" it into a case class using shapeless.
A very basic example of what I am trying to achieve:
case class Foo(i: Int, j: String)
val foo: Option[Foo] = fromListToCaseClass[Foo]( List(1:Any, "hi":Any) )
Here is how I am shaping my solution (this can be quite off):
def fromListToCaseClass[CC <: Product](a: List[Any]): Option[CC] = a.toHList[???].map( x => Generic[CC].from(x) )
Here is my reasoning:
I know that you can go from a case class to an HList[T] (CC -> HList[T]); where T is the type of the HList. I also know that you can create an HList from a list (list -> Option[HList]) as long as you know the type of the HList. Finally I know that you can go from an HList to a case class (HList -> CC).
CC -> HList[T]
list -> Option[HList[T]] -> Option[CC]
I am wondering if this makes sense or if I am way off here. Can we make this work? Any other suggestions? Thanks!
This can be done very straightforwardly using shapeless's Generic and FromTraversable type classes,
import scala.collection.GenTraversable
import shapeless._, ops.traversable.FromTraversable
class FromListToCaseClass[T] {
def apply[R <: HList](l: GenTraversable[_])
(implicit gen: Generic.Aux[T, R], tl: FromTraversable[R]): Option[T] =
tl(l).map(gen.from)
}
def fromListToCaseClass[T] = new FromListToCaseClass[T]
(There's some accidental complexity here due to Scala's awkwardness when it comes to mixing explicit and inferred type parameters: we want to specify T explicitly, but have R inferred for us).
Sample REPL session ...
scala> case class Foo(i: Int, j: String)
defined class Foo
scala> fromListToCaseClass[Foo](List(23, "foo"))
res0: Option[Foo] = Some(Foo(23,foo))
scala> fromListToCaseClass[Foo](List(23, false))
res1: Option[Foo] = None
You can do it with shapeless the following way:
import shapeless._
trait Creator[A] { def apply(list:List[Any]): Option[A] }
object Creator {
def as[A](list: List[Any])(implicit c: Creator[A]): Option[A] = c(list)
def instance[A](parse: List[Any] => Option[A]): Creator[A] = new Creator[A] {
def apply(list:List[Any]): Option[A] = parse(list)
}
def arbitraryCreate[A] = instance(list => list.headOption.map(_.asInstanceOf[A]))
implicit val stringCreate = arbitraryCreate[String]
implicit val intCreate = arbitraryCreate[Int]
implicit val hnilCreate = instance(s => if (s.isEmpty) Some(HNil) else None)
implicit def hconsCreate[H: Creator, T <: HList: Creator]: Creator[H :: T] =
instance {
case Nil => None
case list => for {
h <- as[H](list)
t <- as[T](list.tail)
} yield h :: t
}
implicit def caseClassCreate[C, R <: HList](
implicit gen: Generic.Aux[C, R],
rc: Creator[R]): Creator[C] =
instance(s => rc(s).map(gen.from))
}
And
val foo:Option[Foo] = Creator.as[Foo](List(1, "hi"))