I am trying to use the typeclass pattern in Scala to mark all the valid API serializable types, so that we can have compile-time safety around what we serialize. Our underlying library accepts an AnyRef which can lead to weird errors when not explicitly declaring the type before serializing it.
We allow sending out a public model, an iterable of public models, an option of public model, or a unit.
trait PublicModel
case class UserModel(name: String) extends PublicModel
sealed class SafeForPublic[-T]
implicit object PublicModelOk extends SafeForPublic[PublicModel]
implicit object IterablePublicModelOk extends SafeForPublic[Iterable[PublicModel]]
implicit object OptionPublicModelOk extends SafeForPublic[Option[PublicModel]]
implicit object UnitOk extends SafeForPublic[Unit]
This method works well for everything except methods where the parameter type is an option. This is because None is an Option[Nothing], so T = Nothing which will tell the compiler to look up an implicit object of type SafeForPublic[Nothing] and it will find both SafeForPublic[PublicModel] as well as SafeForPublic[Iterable[PublicModel]]
def boxed[T : SafeForPublic](t: Option[T]) = println("wooohoo!")
boxed(Some(None)) // works
boxed(Some(1)) // doesn't compile. Int is not a valid serializable model.
boxed(Some({})) // works
boxed(Some(UserModel("ok"))) // works
boxed(Some(Seq(UserModel("ok")))) // works
boxed(None) // doesn't compile, duplicate implicits ><
Any idea how I can trick the compiler to not find duplicate implicits for Nothing. I saw Miles Sabin had a trick using:
sealed trait NotNothing[A]{
type B
}
object NotNothing {
implicit val nothing = new NotNothing[Nothing]{ type B = Any }
implicit def notNothing[A] = new NotNothing[A]{ type B = A }
}
But I couldn't figure out how to use it. Halp?
Ok, thanks to some help from the Scala IRC channel, I figured out that LowPriority implicits was created to solve this issue.
I used this to fix it:
sealed class SafeForPublic[-T]
trait LowPriorityImplicits {
implicit object PublicModelOk extends SafeForPublic[PublicModel]
implicit object IterablePublicModelOk extends SafeForPublic[Iterable[PublicModel]]
implicit object OptionPublicModelOk extends SafeForPublic[Option[PublicModel]]
implicit object UnitOk extends SafeForPublic[Unit]
}
object Implicits extends LowPriorityImplicits {
implicit object NothingOk extends SafeForPublic[Nothing]
}
import Implicits._
def boxed[T : SafeForPublic](t: Option[T]) = println("woohoo!")
boxed(None) // compiles! \o/
Related
I am trying to get hold of a shapeless Generic for a case class with a marker trait, like this:
case class X(a:String)
trait UniversalTrait extends Any {}
object MyApp extends App {
val ok = Generic[X]
val notOk = Generic[X with UniversalTrait]
}
It doesn't compile, with error could not find implicit value for parameter gen: shapeless.Generic[X with UniversalTrait] in the notOk line. Why is that? And can something be done?
Side note: I thought that it might be something to do with from not being able to add the marker trait to the returned instance, so I attempted fixing things by adding this:
object UniversalTrait {
implicit def genGeneric[P1<:Product with UniversalTrait,P2<:Product,L<:HList]
(implicit constraint: P1 =:= P2 with UniversalTrait,
underlying: Generic.Aux[P2,L]): Generic.Aux[P1,L] = new Generic[P1]{
type Repr=L
def to(t: P1): Repr = underlying.to(t)
def from(r: Repr): P1 = underlying.from(r).asInstanceOf[P1]
}
}
However, the error remains.
Deriving works for algebraic data types only.
That is a (sealed) trait and case classes (objects) extending the trait.
case class X(a:String) extends UniversalTrait
sealed trait UniversalTrait extends Any {}
val ok = Generic[X]
I have a problem setting up type classes in an algebraic type hierarchy.
I have the following traits:
trait Field[F]{...}
trait VectorSpace3[V,F] extends Field[F]{...}
Know I want to provide implementations:
trait DoubleIsField extends Field[Double]{
...
}
trait DoubleTurple3IsVectorSpace3 extends VectorSpace3[(Double,Double,Double), Double] with Field[Double]{
...
}
trait MyOtherClassIsVectorSpace3 extends VectorSpace3[MyOtherClass, Double] with Field[Double]{
...
}
//now the implicits
implicit object DoubleIsField extends DoubleIsField
implicit object DoubleTurple3IsVectorSpace3 extends DoubleTurple3IsVectorSpace3 with DoubleIsField
implicit object MyOtherClassIsVectorSpace3 extends MyOtherClassIsVectorSpace3 with DoubleIsField
The last two implicits lead to ambiguity: DoubleIsField is a part of 3 implicit values, code does not compile. How to deal with this issue in scala ?
EDIT:
Error:
ambiguous implicit values:
[error] both object DoubleIsField in object TypeClasses of type
Russoul.lib.common.TypeClasses.DoubleIsField.type
[error] and object DoubleTurple3IsVectorSpace3 in object TypeClasses of type
Russoul.lib.common.TypeClasses.DoubleTurple3IsVectorSpace3.type
[error] match expected type Russoul.lib.common.TypeClasses.Field[...Double]
EDIT2:
def func()(implicit env: Field[Double]): Unit ={
}
func()
Full testing program:
object Test extends App {
trait Field[F]{
}
trait VectorSpace3[V,F] extends Field[F]{
}
trait DoubleIsField extends Field[Double]{
}
trait DoubleTurple3IsVectorSpace3 extends VectorSpace3[(Double,Double,Double), Double] with Field[Double]{
}
//now the implicits
implicit object DoubleIsField extends DoubleIsField
implicit object DoubleTurple3IsVectorSpace3 extends DoubleTurple3IsVectorSpace3 with DoubleIsField
def func()(implicit env: Field[Double]): Unit ={
}
func()
}
(Updated in light of your recent comment...)
The problem is the definition of the following function:
def func()(implicit env: Field[Double]): Unit = {
// ...
}
Your issue is that you have multiple implicit values of this type in the same scope, and so the compiler cannot know which one to provide (they are all implicit values that can be expressed as having the type Field[Double]).
The whole point about implicit argument values is that there be a single value that can be identified by the compiler; if it can't identify one, it can't read your mind and pick the right one, nor would you want it to pick one at random.
The options you have available are as follows:
Forego using implicit argument values and pass values to your function explicitly.
Change the definition of the functions with implicit arguments to be types having unique implicit values.
Make do with a single implicit object definition. (Apologies for missing this off the list initially.)
I have the following relations:
trait Instrument
trait EquityOption extends Instrument { ... }
case class CallEquityOption(...) extends EquityOption
case class PutEquityOption(...) extends EquityOption
trait Priceable[I <: Instrument] { def price(I : Instrument) }
I can use exactly the same implementation of Priceable for the case classes CallEquityOptionand PutEquityOption. By having a match case to differentiation between the Call... and Put.... However, if I try to implement it directly as Priceable[EquityOption] under object EquityOption, the implicit cannot be found since it doesn't exactly match the type.
How can I make it work without needing to duplicate code?
You'll have to prove that you can provide an instance for every subtype of EquityOption.
implicit def allEquityOptions[T <: EquityOption]: Pricable[T] = ???
I'm trying to get some information using Scala's reflect library :
abstract class Model
class Person extends Model
class Car extends Model
abstract class AbstractDao[T <: Model]
object PersonDao extends AbstractDao[Person]
object CarDao extends AbstractDao[Car]
object DataLoader {
val daos = Seq(PersonDao, CarDao)
val modelToString = daos.map(genericImportEntities(_))
val modelToString2 = Seq(genericImportEntities(PersonDao), genericImportEntities(CarDao))
private def genericImportEntities[T <: Model](dao: AbstractDao[T])
(implicit
t2: TypeTag[T]
): String = {
t2.tpe.toString
}
}
If I call modelToString, the output is
List(_1, _1)
With modelToString2, it is
List(Person, Car)
Any idea how can I make modelToString work?
The issue is that type of daos is Seq[AbstractDao[_]]. So when calling daos.map(genericImportEntities(_)), T is an unknown type which the compiler calls _1. Generally, TypeTags are only useful when you know the static types at the point where the compiler should insert them, and in this case you don't.
The easiest way to fix this would be to move TypeTag into AbstractDao:
abstract class AbstractDao[T <: Model](implicit val tag: TypeTag[T])
private def genericImportEntities[T <: Model](dao: AbstractDao[T]) =
dao.tag.tpe.toString
Then the compiler inserts the tags at definition of PersonDao and CarDao and they can be used later in genericImportEntities.
I have the following code:
trait Base[A,B] {
def name: String
}
trait BaseCompanion[A,B] {
def classOfBase: Class[_ <: Base[A,B]] // Can I implement something generic here ?
}
case class First(name: String) extends Base[Int,String]
object First extends BaseCompanion[Int,String] {
override def classOfBase: Class[_ <: Base[Int, String]] = classOf[First] // Can this part be generic ?
}
I don't want to override the classOfBase method in every concrete class that will extend BaseCompanion.This can be achieved by changing BaseCompanion to:
abstract class BaseCompanion[A,B, CLAZZ <: Base[A,B] : ClassTag] {
def classOfBase: Class[CLAZZ] = classTag[CLAZZ].runtimeClass.asInstanceOf[Class[CLAZZ]]
}
object First extends BaseCompanion[Int,String,First]
But I don't really like this solution, is there a way to do this without changing the signature of BaseCompanion and implement something generic inside the it ?
By the way today Companion object of any case class "defines" apply(...) method. Given the example above there will be a method similar to this:
abstract class BaseCompanion[A,B, CLAZZ <: Base[A,B] : ClassTag] {
def classOfBase: Class[CLAZZ] = classTag[CLAZZ].runtimeClass.asInstanceOf[Class[CLAZZ]]
/* No need to implement in the companion of a case class that extends Base */
def apply(name: String): Base[A,B]
}
The return type of this apply method is known to the Companion perhaps there is a way to use this information.
Yes, it can be done with this change:
def classOfBase = this.getClass.getMethods.find(_.getName == "apply").get.
getReturnType.asInstanceOf[Class[_ <: Base[A,B]]]
Note that this assumes there is precisely one apply method. In practice, you should check this assumption.
Assuming that if what you want would work, the following would be possible:
case class First(name: String) extends Base[Int, String]
object First extends BaseCompanion[Int, String]
assert(First.baseClass == classOf[First])
Now if that would work, what would stop you from doing the following?
class Second extends BaseCompanion[Int, String]
val second = new Second
println(second.baseClass)
or
// case class Third not defined
object Third extends BaseCompanion[Int, String]
println(Third.baseClass)
What would that result in? Second and Third are not a companion object with an associated class. Second is a class itself!
The problem is that you cannot force your BaseCompanion trait to only be inherited by something that is a companion object. BaseCompanion therefore cannot use the special relation that companion objects and associated classes have. If you want information about the associated class with a companion object, you have to give BaseCompanion that information manually in the definition of your companion object.
The Scala compiler won't allow you to do this. You can work around this with reflection if you want, but whatever solution remains that accomplishes this has to take into account that you are effectively creating unpredictable runtime behaviour. In my opinion, it's best to just help the compiler figure it out, and supply the proper information at compile time.