I would like to treat every result differently without using match.
I wanted to make multiple method implementations but it only works for classes, using a case class instead would be too problematic.
The following approach does not work:
sealed trait Result
object Result {
final case object Result1 extends Result
final case object Result2 extends Result
final case object Result3 extends Result
}
def doSth(result: Result)=
consumeResult(result)
private def consumeResult(result: Result.Result1) = ???
private def consumeResult(result: Result.Result2) = ???
private def consumeResult(result: Result.Result3) = ???
The immediate fix is to use the object type like this:
private def consumeResult(result: Result.Result1.type) = ???
etc..
However the broader code is not going to work:
def doSth(result: Result) =
consumeResult(result)
Function overloading happens at compile time, not run time. By the time you call consumeResult the run-time type is lost. The compile-time type is Result so it won't match any of your overloaded versions of consumeResult.
A typeclass might be the best approach:
trait Consume[T <: Result] {
def consume()
}
object Consume {
implicit object consume1 extends Consume[Result.Result1.type] {
def consume() = {
println("Consume1")
}
}
implicit object consume2 extends Consume[Result.Result2.type] {
def consume() = {
println("Consume2")
}
}
}
def doSth[T <: Result](result: T)(implicit ev: Consume[T]) =
ev.consume()
doSth(Result.Result1) // Prints "Consume1"
doSth(Result.Result2) // Prints "Consume2"
doSth(Result.Result3) // Does not compile, no matching typeclass
Related
I have a situation where I'd like to implement a given trait (CanBeString in the example below). I would like to have the option either to implement that trait using a newly created case class (NewImplementation in the example below), or to implement it by adding functionality to some pre-existing type (just Int in the example below), by using a type class. This is probably best illustrated by the below:
package example
// typeclass
trait ConvertsToString[A] {
def asString(value: A): String
}
// the trait I would like the typeclass to implement
trait CanBeString {
def asString: String
}
// this implementation approach taken from the scala with cats book
object ConvertsToStringInstances {
implicit val intConvertsToString: ConvertsToString[Int] =
new ConvertsToString[Int] {
def asString(value: Int): String = s"${value}"
}
}
object ConvertsToStringSyntax {
implicit class ConvertsToStringOps[A](value: A) {
def asString(implicit c: ConvertsToString[A]): String = c.asString(value)
}
}
object Test {
import ConvertsToStringInstances._
import ConvertsToStringSyntax._
def testAsFunc(c: CanBeString): String = c.asString
case class NewImplementation (f: Double) extends CanBeString {
def asString = s"{f}"
}
println(testAsFunc(NewImplementation(1.002))) // this works fine!
println(testAsFunc(1)) // this sadly does not.
}
Is anything like this possible? I'm only recently discovering the topic of typeclasses so I'm aware that what I'm asking for here may be possible but just unwise - if so please chime in and let me know what a better idiom might be.
Thanks in advance, and also afterwards!
For example you can have two overloaded versions of testAsFunc (OOP-style and typeclass-style)
object Test {
...
def testAsFunc(c: CanBeString): String = c.asString
def testAsFunc[C: ConvertsToString](c: C): String = c.asString
println(testAsFunc(NewImplementation(1.002))) // {f}
println(testAsFunc(1)) // 1
}
Or if you prefer to have the only testAsFunc then you can add instances of the type class for subtypes of the trait to be implemented
object ConvertsToStringInstances {
implicit val intConvertsToString: ConvertsToString[Int] = ...
implicit def canBeStringSubtypeConvertsToString[A <: CanBeString]: ConvertsToString[A] =
new ConvertsToString[A] {
override def asString(value: A): String = value.asString
}
}
object Test {
...
def testAsFunc[C: ConvertsToString](c: C): String = c.asString
println(testAsFunc(NewImplementation(1.002))) // {f}
println(testAsFunc(1)) // 1
}
Please notice that if for a c there are both OOP-ish c.asString and extension-method c.asString then only the first is actually called.
I am going through some exercises I have invented on case classes and typeclasses. One of the problems I have faced is the following:
object Example extends App {
sealed trait Serializer[T] {
def serialize(seq: List[T]): String
}
implicit object StringSerializer extends Serializer[String] {
def serialize(seq: List[String]): String = seq.toString()
}
implicit object IntSerializer extends Serializer[Int] {
def serialize(seq: List[Int]): String = seq.toString()
}
case class Marker[T: Serializer](lst: Option[List[T]] = None)
Marker() // ambiguous implicit values: here...
}
Now this gives an error about ambiguous implicit values. I think this is related to a question I have asked before (albeit a different error message):
Type erasure in a nested list with a given context bound
Am I correct in that it is the same process at work here, even though the error message is different?
Compiler can't infer T. Try to specify T explicitly
Marker[String]() // compiles
Marker[Int]() // compiles
When you provide lst it can infer T itself
Marker(Some(List(1, 2)))
Marker(Some(List("a", "b")))
For the same reason
Marker(Option.empty[List[Int]])
Marker(Option.empty[List[String]])
Marker[Int](None)
Marker[String](None)
Marker(None: Option[List[Int]])
Marker(None: Option[List[String]])
compile while Marker(None) doesn't.
Alternatively you can prioritize your implicits
trait LowPrioritySerializer {
implicit object StringSerializer extends Serializer[String] {
def serialize(seq: List[String]): String = seq.toString()
}
}
object Serializer extends LowPrioritySerializer {
implicit object IntSerializer extends Serializer[Int] {
def serialize(seq: List[Int]): String = seq.toString()
}
}
Then IntSerializer will be tried firstly and StringSerializer will be tried secondly if IntSerializer didn't work (if type is different).
Let's say we have the following traits:
trait MyValue
object MyValue {
case class MyBoolean(record: Boolean) extends MyValue
case class MyLong(record: Long) extends MyValue
}
trait MyValueExtractor[T] {
def apply(record: T): Option[MyValue]
}
trait MyThing[T] {
def name: String
def myValueExtractor: MyValueExtractor[T]
def myValue(record: T): Option[MyValue] = myValueExtractor(record)
}
What I want is something like this but without the second type parameter.
Note: I can't actually update the MyThing trait; I'm just using this as an illustration of the intended functionality.
trait MyThing[T, U] {
def name: String
def myValueExtractor: MyValueExtractor[T]
def myValue(record: T): Option[MyValue] = myValueExtractor(record)
def myRelatedValue(record: T): Option[U]
}
I'm wondering if I could use the type class pattern to help solve this (i.e., import some rich class that implicitly gives me a myRelatedValue method)?
Here's the rub. Every time T (above) is MyValue.MyBoolean, U must be a String. Every time T is MyValue.MyLong, U must be a Double. In other words, there's a sort of underlying mapping between T and U.
Is there a good way to do this using type class?
Sure. You just need to define some Mapping typeclass with implementations for your desired pairs of types. Then MyThing can have a method that takes an implicit typeclass instance and simply invokes its method.
Here's the code (I removed the unneeded details)
// types
case class MyBoolean(record: Boolean)
case class MyLong(record: Long)
// trait which uses the Mapping typeclass
trait MyThing[T] {
def myRelatedValue[U](record: T)(implicit ev: Mapping[T, U]): Option[U] = ev.relatedValue(record)
}
// typeclass itself
trait Mapping[T, U] {
def relatedValue(record: T): Option[U]
}
object Mapping {
implicit val boolStringMapping = new Mapping[MyBoolean, String] {
def relatedValue(record: MyBoolean) = Some(record.record.toString)
}
implicit val longDoubleMapping = new Mapping[MyLong, Double] {
def relatedValue(record: MyLong) = Some(record.record)
}
}
// usage
val myBoolThing = new MyThing[MyBoolean] {}
val myLongThing = new MyThing[MyLong] {}
val myStringThing = new MyThing[String] {}
myBoolThing.myRelatedValue(MyBoolean(true)) // Some(true)
myLongThing.myRelatedValue(MyLong(42L)) // Some(42.0)
myStringThing.myRelatedValue("someString") // error: could not find implicit value
Note that e.g. myBoolThing.myRelatedValue(MyBoolean(true)) will yield a type Option[U]. However, since myRelatedValue is parameterized, you can help the compiler and invoke it as myBoolThing.myRelatedValue[String](MyBoolean(true)), in which case you will obtain an Option[String]. If you try something other than String for MyBoolean, you will get an error.
Some setup before my question:
/* roughly equivalent to a union type */
sealed trait NewType
object NewType {
final case class Boolean(record: Boolean) extends NewType
final case class Long(record: Long) extends NewType
final case class String(record: String) extends NewType
}
/* Try to convert a record of type T to a NewType */
sealed trait NewTypeConverter[T] { def apply(record: T): Option[NewType] }
object NewTypeConverter {
trait BooleanConverter[T] extends NewTypeConverter[T] {
override def apply(record: T): Option[NewType.Boolean]
}
trait LongConverter[T] extends NewTypeConverter[T] {
override def apply(record: T): Option[NewType.Long]
}
trait StringConverter[T] extends NewTypeConverter[T] {
override def apply(record: T): Option[NewType.String]
}
}
I want to define a trait Data like the following:
trait Data[T] {
def name: String
def converter: NewTypeConverter[_]
final def value(record: T): Option[NewType] = ??? // calls converter
}
How can I implement this final def value(record: T): Option[NewType]?
A few things to note:
The return type from the apply method of converter must be the same return type as value. So, if you happen to have a BooleanConverter, then value must return an Option[NewValue.Boolean].
Input type T to the Data trait does not have to be the same as the input type _ to the converter. If they happen to be the same type, then the implementation could just be final def value(record: T): Option[NewType] = converter(record). The trickier case is in when the input types differ. Let's say the input type to Data was a String, but the input type to converter was a Long. How would that be handled?
It looks like you are 90% through implementing the Type Class pattern, so I'll try to solve your issue by completing it. Here is a nice reading about it. In short, what you miss is a signature that states that, if one (and only one) implementation of the converter can be found in the implicit scope, use it to run the conversion (or anything else defined by the trait).
The signature is the following:
final def value(record: T)(implicit c: NewTypeConverter[T]): Option[NewType]
Given such a strict signature also makes the implementation quite straightforward:
final def value(record: T)(implicit c: NewTypeConverter[T]): Option[NewType] =
c(record) // literally only applies `c`, the converter
Now, whenever you have your converter instance in the implicit scope, for example the following:
implicit val converter: NewTypeConverter[Boolean] =
new StringConverter[Boolean] {
override def apply(record: Boolean): Option[NewType.String] =
if (record) Some(NewType.String("TRUE"))
else Some(NewType.String("FALSE"))
}
you can instance your trait (simplified in the example):
trait Data[T] {
def name: String
final def value(record: T)(implicit c: NewTypeConverter[T]): Option[NewType] =
c(record)
}
final case class BooleanData(name: String) extends Data[Boolean]
val bool = BooleanData(name = "foo")
And use it:
println(bool.value(true)) // prints Some(String(TRUE))
println(bool.value(false)) // prints Some(String(FALSE))
If you try to invoke the value method from a place where you have no access to the implicit instance you'll get an error:
error: could not find implicit value for parameter converter: NewTypeConverter[Boolean]
Bonus
Providing evidence for a known capability of an object via implicits is so common that Scala has some syntactic sugar you can use if you need to provide such evidence (for example, you have a method that calls your value method) but you do not have to use it directly. It's expressed as follows, with the : right after the generic type:
def methodThatCallsValue[T: Data](convertee: T): Option[NewType] =
data.value(convertee)
it's called context bound and is equivalent to the following (which was done explicitly in the example):
def methodThatCallsValue(convertee: T)(implicit $ev: Data[T]): Option[NewType] =
data.value(convertee)
Just when I thought I understood the basics of Scala's type system... :/
I'm trying to implement a class that reads the contents of a file and outputs a set of records. A record might be a single line, but it could also be a block of bytes, or anything. So what I'm after is a structure that allows the type of Reader to imply the type of the Record, which in turn will imply the correct Parser to use.
This structure works as long as MainApp.records(f) only returns one type of Reader. As soon as it can return more, I get this error:
could not find implicit value for parameter parser
I think the problem lies with the typed trait definitions at the top, but I cannot figure out how to fix the issue...
// Core traits
trait Record[T]
trait Reader[T] extends Iterable[Record[T]]
trait Parser[T] {
def parse(r: Record[T]): Option[Int]
}
// Concrete implementations
class LineRecord[T] extends Record[T]
class FileReader[T](f:File) extends Reader[T] {
val lines = Source.fromFile(f).getLines()
def iterator: Iterator[LineRecord[T]] =
new Iterator[LineRecord[T]] {
def next() = new LineRecord[T]
def hasNext = lines.hasNext
}
}
trait TypeA
object TypeA {
implicit object TypeAParser extends Parser[TypeA] {
def parse(r: Record[TypeA]): Option[Int] = ???
}
}
trait TypeB
object TypeB {
implicit object TypeBParser extends Parser[TypeB] {
def parse(r: Record[TypeB]): Option[Int] = ???
}
}
// The "app"
object MainApp {
def process(f: File) =
records(f) foreach { r => parse(r) }
def records(f: File) = {
if(true)
new FileReader[TypeA](f)
else
new FileReader[TypeB](f)
}
def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
parser.parse(r)
}
First off you must import the implicit object in order to use them:
import TypeA._
import TypeB._
That's not enough though. It seems like you're trying to apply implicits dynamically. That's not possible; they have to be found compile time.
If you import the objects as above and change the records so that the compiler finds the correct generic it will run fine:
def records(f: File) = new FileReader[TypeA](f)
But then it may not be what you were looking for ;)
The problem is that the return type of your records method is basically FileReader[_] (since it can return either FileReader[TypeA] or FileReader[TypeB]), and you don't provide an implicit argument of type Parser[Any]. If you remove the if-expression the return type is inferred to FileReader[TypeA], which works fine. I'm not sure what you're trying to do, but obviously the compiler can't select implicit argument based upon a type that is only known at runtime.
1) Using type with implicit inside as type parameter - doesn't bind this implicit to the host type, to do this change objects to the traits and mix them instead of generalizing (type-parametrizing):
def records(f: File) = {
if(true)
new FileReader(f) with TypeA
else
new FileReader(f) with TypeB
}
2) The parser should be in scope of function that calls parse. So you may try smthg like that:
def process(f: File) = {
val reader = records(f);
import reader._
reader foreach { r => parse(r) }
}
PlanB) Simpler alternative is to define type-parameter specific implicit methods inside the AppMain (or some trait mixed in), but it will work only if TypeA/TypeB is known on compile time, so records method can return concrete type:
implicit class TypeAParser(r: Record[TypeA]) {
def parse: Option[Int] = ???
}
implicit class TypeBParser(r: Record[TypeB]) {
def parse: Option[Int] = ???
}
def process[T <: TypeAorB](f: File) =
records[T](f).foreach(_.parse)
def recordsA[T <: TypeAorB](f: File) = new FileReader[T](f)
Here is, I think, the full set of modifications you need to do to get where I think you want to go.
import scala.io.Source
import java.io.File
import reflect.runtime.universe._
// Core traits
trait Record[+T]
trait Reader[+T] extends Iterable[Record[T]]
trait Parser[-T] {
def parse(r: Record[T]): Option[Int]
}
// Concrete implementations [unmodified]
class LineRecord[T] extends Record[T]
class FileReader[T](f:File) extends Reader[T] {
val lines = Source.fromFile(f).getLines()
def iterator: Iterator[LineRecord[T]] =
new Iterator[LineRecord[T]] {
def next() = new LineRecord[T]
def hasNext = lines.hasNext
}
}
sealed trait Alternatives
case class TypeA() extends Alternatives
object TypeA {
implicit object TypeAParser extends Parser[TypeA] {
def parse(r: Record[TypeA]): Option[Int] = ???
}
}
case class TypeB() extends Alternatives
object TypeB {
implicit object TypeBParser extends Parser[TypeB] {
def parse(r: Record[TypeB]): Option[Int] = ???
}
}
class ParseAlternator(parserA: Parser[TypeA], parserB: Parser[TypeB]) extends Parser[Alternatives] {
def parse(r: Record[Alternatives]): Option[Int] = r match {
case x: Record[TypeA #unchecked] if typeOf[Alternatives] =:= typeOf[TypeA] => parserA.parse(x)
case x: Record[TypeB #unchecked] if typeOf[Alternatives] =:= typeOf[TypeB] => parserB.parse(x)
}
}
object ParseAlternator {
implicit def parseAlternator(implicit parserA: Parser[TypeA], parserB: Parser[TypeB]): Parser[Alternatives] = new ParseAlternator(parserA, parserB)
}
// The "app"
object MainApp {
import ParseAlternator._
def process(f: File) =
records(f) foreach { r => parse(r) }
def records(f: File): Reader[Alternatives] = {
if(true)
new FileReader[TypeA](f)
else
new FileReader[TypeB](f)
}
def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
parser.parse(r)
}
The gist of it is: all of this would be completely classsical if only your parse instance did not have to pattern-match on a generic type but dealt directly with an Alternative instead.
It's this limitation (inherited from the JVM) that scala can't properly pattern-match on an object of a parametric type that requires the reflection & typeOf usage. Without it, you would just have type alternatives for your content (TypeA, TypeB), which you would add to a sealed trait, and which you would dispatch on, in an implicit that produces a Parser for their supertype.
Of course this isn't the only solution, it's just what I think is the meeting point of what's closest to what you're trying to do, with what's most idiomatic.