In Scala, I want to generate some aliases for basic types, and then implement conversions through a type class. This is both useful for me, and an opportunity to understand type classes. The code is the following:
type Index = Int
val Index = Int
type Integer = Int
val Integer = Int
type Real = Double
val Real = Double // to have companion object of Double also be the companion object of Real
trait Convertible[A] {
def toIndex(a: A): Index
def toInteger(a: A): Integer
def toReal(a: A): Real
}
implicit val ConvertibleIndex: Convertible[Index] = new Convertible[Index] {
def toIndex(i: Index) = i
def toInteger(i: Index) = i
def toReal(i: Index) = i.toDouble
}
implicit val ConvertibleInteger: Convertible[Integer] = new Convertible[Integer] {
def toIndex(i: Integer) = i
def toInteger(i: Integer) = i
def toReal(i: Integer) = i.toDouble
}
implicit val ConvertibleReal: Convertible[Real] = new Convertible[Real] {
def toIndex(r: Real) = r.toInt
def toInteger(r: Real) = r.toInt
def toReal(r: Real) = r
}
implicit val ConvertibleString: Convertible[String] = new Convertible[String] {
def toIndex(s: String) = s.toInt
def toInteger(s: String) = s.toInt
def toReal(s: String) = s.toDouble
}
implicit class ConvertibleSyntax[A](a: A)(implicit val c: Convertible[A]) {
def toIndex = c.toIndex(a)
def toInteger = c.toInteger(a)
def toReal = c.toReal(a)
}
Consider now:
val a = 3.toReal
val b = 3.0.toReal
val c = "3".toReal
The statement for a does not compile, with the compilation error: method toReal is not a member of Int. But, for the b and c statements, the implicit conversion to ConvertibleSyntax is properly done.
Why is the implicit conversion not working on Int, but is working on Double and String ?
Because you define ambiguous implicits for Index and Integer (both Int).
Which one should be chosen by compiler?
I think you might be a little confused about how Scala does implicit conversions. (A common mistake, as implicit is a little overused.)
I think that what you want, first of all, is an implicit conversion function - or even an implicit class. Here's how you could do this using the latter:
Note: Int, Index and Integer are treated identically, so are Real and Double, confusing matters somewhat, so I've pared this down to something that will work. Also, Convertible does not need to be generic as its conversion functions need no arguments. Finally, you shouldn't have both type and val declarations for your types.
type Index = Int
type Integer = Int
type Real = Double
trait Convertible {
def toIndex: Index
def toInteger: Integer
def toReal: Real
}
// Implicit classes cannot be defined in top-level scope, so they belong to an object.
object Implicits {
implicit class ConvertibleInt(i: Int)
extends Convertible {
override def toIndex = i
override def toInteger = i
override def toReal = i.toDouble
}
implicit class ConvertibleDouble(d: Double)
extends Convertible {
override def toIndex = d.toInt
override def toInteger = d.toInt
override def toReal = d
}
implicit class ConvertibleString(s: String)
extends Convertible {
override def toIndex = s.toInt
override def toInteger = s.toInt
override def toReal = s.toDouble
}
}
Now try this:
import Implicits._
val a = 3.toReal
val b = 3.0.toReal
val c = "3".toReal
What's happening here? Well, the implicit class declarations define classes that decorate the sole constructor argument with additional functions. If the compiler sees that you're trying to call a method on a type that doesn't have that method, it will look to see if there's an implicit conversion, in scope, to a type that does. If so, it is used and the function is called; if not, you get a compiler error. (The import statement is used to bring the classes into your current scope.)
So, for example, when the compiler sees "3".toReal it firstly determines that "3" is a String. Since this type doesn't have a .toReal member, it tries to find a conversion from a String to a type that does have such a member. It finds the ConvertibleString implicit class that takes a String argument and provides a .toReal method. Yay! So the compiler creates an instance of this class by passing "3" to ConvertibleString's constructor, then calls .toReal on the result.
On the other hand, when implicit is used with a value, it tells the compiler that the value is a default for any matching implicit arguments of the same type that are not provided. NEVER USE implicit WITH A PRIMITIVE OR COMMON LIBRARY TYPE!
For example:
final case class Example(i: Int)
// Default.
implicit val nameCanBeAnythingAtAll = Example(5)
// Function with implicit argument.
def someFunc(implicit x: Example): Unit = println(s"Value is $x")
Now, if you write something like this:
someFunc
the output will be Value is Example(5).
implicit values and arguments are an advanced topic, and I wouldn't worry about how they're used right now.
Related
Say I have a trait like this
trait X {
def foo(param: String): Int
}
and I want to override it with a function value, I have to do this:
class XImpl extends X {
private val fooF: String => Int = ???
override def foo(param: String): Int = fooF(param)
}
because the following does not work
class XImpl extends X {
override val foo: String => Int = ???
}
/*
error: class XImpl needs to be abstract. Missing implementation for:
def foo(param: String): Int // inherited from trait X
^
error: value foo overrides nothing.
Note: the super classes of class XImpl contain the following, non final members named foo:
def foo(param: String): Int
*/
Is it possible to directly implement/override the trait method with the function value in some way and avoid the forwarder?
The simple answer is that you cannot override a method with a function value because they are not the same type.
A method must be called on a particular instance of a class (even if the method itself does not use any fields in that class).
A function value stands alone and can be called without any additional information.
A method can be converted a function value using eta expansion x.foo _. This creates a function value which has the class value embedded within it.
val x: X = ???
val etaFoo: String => Int = x.foo _
In this case Scala will actually do the eta expansion for you so the _ is optional, but it is required in more complex uses and is good practice to keep it there so show that eta expansion is happening.
To convert a function value to a method you need to define a method as shown in the question.
Eta expansion works with methods with multiple parameters:
trait X2 {
def foo2(p1: String, p2: Int): String
}
val x2: X2 = ???
val etaFoo2: (String, Int) => String = x2.foo2 _
I'm trying to remove some of the boilerplate in an API I am writing.
Roughly speaking, my API currently looks like this:
def toEither[E <: WrapperBase](priority: Int)(implicit factory: (String, Int) => E): Either[E, T] = {
val either: Either[String, T] = generateEither()
either.left.map(s => factory(s, priority))
}
Which means that the user has to generate an implicit factory for every E used. I am looking to replace this with a macro that gives a nice compile error if the user provided type doesn't have the correct ctor parameters.
I have the following:
object GenericFactory {
def create[T](ctorParams: Any*): T = macro createMacro[T]
def createMacro[T](c: blackbox.Context)(ctorParams: c.Expr[Any]*)(implicit wtt: WeakTypeType[T]): c.Expr[T] = {
import c.universe._
c.Expr[T](q"new $wtt(..$ctorParams)")
}
}
If I provide a real type to this GenericFactory.create[String]("hey") I have no issues, but if I provide a generic type: GenericFactory.create[E]("hey") then I get the following compile error: class type required by E found.
Where have I gone wrong? Or if what I want is NOT possible, is there anything else I can do to reduce the effort for the user?
Sorry but I don't think you can make it work. The problem is that Scala (as Java) uses types erasure. It means that there is only one type for all generics kinds (possibly except for value-type specializations which is not important now). It means that the macro is expanded only once for all E rather then one time for each E specialization provided by the user. And there is no way to express a restriction that some generic type E must have a constructor with a given signature (and if there were - you wouldn't need you macro in the first place). So obviously it can not work because the compiler can't generate a constructor call for a generic type E. So what the compiler says is that for generating a constructor call it needs a real class rather than generic E.
To put it otherwise, macro is not a magic tool. Using macro is just a way to re-write a piece of code early in the compiler processing but then it will be processed by the compiler in a usual way. And what your macro does is rewrites
GenericFactory.create[E]("hey")
with something like
new E("hey")
If you just write that in your code, you'll get the same error (and probably will not be surprised).
I don't think you can avoid using your implicit factory. You probably could modify your macro to generate those implicit factories for valid types but I don't think you can improve the code further.
Update: implicit factory and macro
If you have just one place where you need one type of constructors I think the best you can do (or rather the best I can do ☺) is following:
Sidenote the whole idea comes from "Implicit macros" article
You define StringIntCtor[T] typeclass trait and a macro that would generate it:
import scala.language.experimental.macros
import scala.reflect.macros._
trait StringIntCtor[T] {
def create(s: String, i: Int): T
}
object StringIntCtor {
implicit def implicitCtor[T]: StringIntCtor[T] = macro createMacro[T]
def createMacro[T](c: blackbox.Context)(implicit wtt: c.WeakTypeTag[T]): c.Expr[StringIntCtor[T]] = {
import c.universe._
val targetTypes = List(typeOf[String], typeOf[Int])
def testCtor(ctor: MethodSymbol): Boolean = {
if (ctor.paramLists.size != 1)
false
else {
val types = ctor.paramLists(0).map(sym => sym.typeSignature)
(targetTypes.size == types.size) && targetTypes.zip(types).forall(tp => tp._1 =:= tp._2)
}
}
val ctors = wtt.tpe.decl(c.universe.TermName("<init>"))
if (!ctors.asTerm.alternatives.exists(sym => testCtor(sym.asMethod))) {
c.abort(c.enclosingPosition, s"Type ${wtt.tpe} has no constructor with signature <init>${targetTypes.mkString("(", ", ", ")")}")
}
// Note that using fully qualified names for all types except imported by default are important here
val res = c.Expr[StringIntCtor[T]](
q"""
(new so.macros.StringIntCtor[$wtt] {
override def create(s:String, i: Int): $wtt = new $wtt(s, i)
})
""")
//println(res) // log the macro
res
}
}
You use that trait as
class WrapperBase(val s: String, val i: Int)
case class WrapperChildGood(override val s: String, override val i: Int, val float: Float) extends WrapperBase(s, i) {
def this(s: String, i: Int) = this(s, i, 0f)
}
case class WrapperChildBad(override val s: String, override val i: Int, val float: Float) extends WrapperBase(s, i) {
}
object EitherHelper {
type T = String
import scala.util._
val rnd = new Random(1)
def generateEither(): Either[String, T] = {
if (rnd.nextBoolean()) {
Left("left")
}
else {
Right("right")
}
}
def toEither[E <: WrapperBase](priority: Int)(implicit factory: StringIntCtor[E]): Either[E, T] = {
val either: Either[String, T] = generateEither()
either.left.map(s => factory.create(s, priority))
}
}
So now you can do:
val x1 = EitherHelper.toEither[WrapperChildGood](1)
println(s"x1 = $x1")
val x2 = EitherHelper.toEither[WrapperChildGood](2)
println(s"x2 = $x2")
//val bad = EitherHelper.toEither[WrapperChildBad](3) // compilation error generated by c.abort
and it will print
x1 = Left(WrapperChildGood(left,1,0.0))
x2 = Right(right)
If you have many different places where you want to ensure different constructors exists, you'll need to make the macro much more complicated to generate constructor calls with arbitrary signatures passed from the outside.
This works
trait SomeTrait {
type T
def write2( s: String): T
}
case class C() extends SomeTrait {
type T = String
override def write2(s:String): T = s }
But this does not
trait SomeTrait {
def write2[T]( s: String): T
}
case class C() extends SomeTrait {
override def write2(s: String): String =s }
To my reasoning, they ..seem similar. Why specifically does the compiler give a "method does not override anything" error? IS there a way of making this work?
If you use this definition:
trait T {
def write2[T]( s: String): T
}
Consider some client using this trait. For example:
def doSomething(t: T): Unit = {
val s: String = t.write2[String]("hello")
val n: Int = t.write2[Int]("world")
val d: Double = t.write2[Double]("!")
println(s + n.toString + d.toString)
}
I don't know what the values of s, n, and d would be, but in theory that would be a perfectly valid usage of the trait, from the compiler's perspective. So in order to truly override that write2[T] method, you would have to provide valid behavior for all possible types T.
Compare that to:
trait T2 {
type T
def write2( s: String): T
}
Or even:
trait T3[T] {
def write2(s: String): T
}
Then when callers use it:
def doSomething(t: T2): Unit = {
val x = t.write2("hello") // always returns type t.T
}
def doSomething[T](t: T3[T]): Unit = {
val x = t.write2("hello") // always returns type T
}
There's only one possible type that can be returned, once you have a specific instance of that trait. So to override that method, you need only override the behavior for that one type.
Your trait definition in the second snippet does not mean that implementations of it must have a method write2 for some type T, but that it must have a method write2 which takes a type parameter T.
That means that, if you have a value v: SomeTrait, you should be able to do
val stringed: String = v.method2[String]("foo")
val inted: Int = v.method2[Int]("foo")
This compiles:
trait T {
def write2[T]( s: String): T
}
case class C() extends T {
override def write2[T](s: String): T =s.asInstanceOf[T]
}
write2 returns a T, not a String (hence the reason why your second override does not work and the necessity in the code above for the ugly cast)
I'd like to construct a type like LimitedString[Limit] where Limit is a type representation of the maximum length of the string.
It would work along the lines of
class LimitedString[Limit] [private](val s: String)
object LimitedString {
private def getLimit[Limit]: Int = ??? // turn `Limit` type into a value
def truncate[Limit](s: String) = new LimitedString[Limit](s take getLimit)
def get[Limit](s: String) =
if(s.length < getLimit) Some(new LimitedString[Limit](s))
else None
}
type Str100 = LimitedString[100] // obviously this won't work
def someLibraryMethod(s: Str100) = { ... }
What I can't figure out is how to actually type (as in keyboard) the type (as in compilation) for Limit.
I started looking into Shapeless's singleton types and found that you can say
100.narrow
// res1: Int(100) = 100
But if I try to use Int(100) as the type, I get errors.
val x: Int(100) = 100
// error: ';' expected but '(' found.
Additionally, how would I implement something like def getLimit[Limit]: Int?
I took #TravisBrown's suggestion to look into Shapless's Witness, and came up with this:
class LimitedString[Limit <: Int] private[util](val s: String) extends AnyVal {
override def toString = s
}
class LimitedStringCompanion[Limit <: Int : Witness.Aux]{
def limit: Int = implicitly[Witness.Aux[Limit]].value
def unapply(s: String): Option[LimitedString[Limit]] = {
if(s.length > limit) None else Some(new LimitedString(s))
}
def truncate(s: String): LimitedString[Limit] = new LimitedString(s take limit)
}
Usage:
import shapeless._
object MyLibraryThing {
type Name = LimitedString[Witness.`50`.T]
object Name extends LimitedStringCompanion[Witness.`50`.T]
def rename(id: Int, name: Name) = { ... }
}
The key things that make it work:
Witness.Aux is a typeclass which you can use to get the singleton value back out of the type
The singleton type Witness.`50`.T is actually a subtype of Int
Type aliases make it more convenient to interact with
I have code that boils down to a Factory initializing an object and then using that object again to do additional operations:
trait Factory[T] {
def initialize(): T;
def finish(t: T): Unit;
}
As I understand this, the result of initialize should always be suitable to be passed to finish for any one Factory instance, regardless of T.
The factory itself is called at a point where it isn't known what T is:
object Minimal {
object StringFactory extends Factory[String] {}
val factories = Map[Int, Factory[_]](0 -> StringFactory)
val factory = factories(0)
// (1)
val obj = factory.initialize()
factory.finish(obj)
// (2)
def wrapper[T](factory: Factory[T]): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}
While variant (2) works, variant (1) doesn't:
type mismatch; found : Minimal.obj.type (with underlying type Any) required: _$6
but I can't figure out how to fix this. Is it even possible?
What does the compiler get by calling the wrapper method that it can't figure out itself? From my point of view, obj's type should be _$6, as the compiler seems to name that capture of _. How can I make the compiler realize that without having to introduce a whole new method for it?
Existential type looses its existentiality and becomes upper-bound after assigning its instance to val itself, so any way without such assigning will work, including:
scala> trait Factory[T] { type TT = T; def initialize(): TT; def finish(t: TT): Unit;}
defined trait Factory
scala> val factory: Factory[_] = new Factory[Int] {def initialize = 5; def finish(t: Int) {}}
factory: Factory[_] = $anon$1#31d0ca61
scala> factory.finish(factory.initialize())
This will not work:
scala> val obj = factory.initialize()
obj: Any = 5
scala> factory.finish(obj)
<console>:11: error: type mismatch;
found : Any
required: factory.TT
(which expands to) _$1
factory.finish(obj)
^
And this because scala will not see their types as equal (unless both are one same type member) as existentiality means that intialize() may return any subclass of Any, when finish() may accept any (but not always same) subclass of Any:
scala> trait Factory[T] { def initialize(): T; def finish(t: T): Unit;}
defined trait Factory
scala> val factory: Factory[_] = new Factory[Int] {def initialize = 5; def finish(t: Int) {}}
factory: Factory[_] = $anon$1#6e5da49
scala> factory.finish(factory.initialize())
<console>:10: error: type mismatch;
found : (some other)_$1(in value factory)
required: _$1(in value factory)
factory.finish(factory.initialize())
^
So the only way to bind input and output here is sharing type member between them.
One solution would be to entirely replace the type parameter with an abstract type:
trait Factory {
type T
def initialize(): T;
def finish(t: T): Unit;
}
object Minimal {
object StringFactory extends Factory {
type T = String
def initialize(): T = ???
def finish(t: T): Unit = ???
}
val factories = Map[Int, Factory](0 -> StringFactory)
val factory: Factory = factories(0)
// (1)
val obj: factory.T = factory.initialize()
// Or simply (relying on inference): val obj = factory.initialize()
factory.finish(obj)
// (2)
def wrapper(factory: Factory): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}
Based on Régis' answer, I found out that the compiler infers obj: Factory.T. From there, it was a small step to combine this with dk14's suggestion to use type TT = T. The result is this, buth generic and statically typechecked, without introducing a wrapper method. Kudos to both!
To literally answer the original question
From my point of view, obj's type should be _$6, as the compiler seems to name that capture of _. How can I make the compiler realize that without having to introduce a whole new method for it?
by giving _$6 the explicit name TT. Of course, the methods then need to use that name as well.
trait Factory[T] {
type TT = T
def initialize(): TT;
def finish(t: TT): Unit;
}
object Minimal {
object StringFactory extends Factory[String] {
def initialize(): TT = ""
def finish(t: TT): Unit = {}
}
val factories = Map[Int, Factory[_]](0 -> StringFactory)
val factory = factories(0)
// (1)
val obj: factory.TT = factory.initialize()
factory.finish(obj)
// (2)
def wrapper[T](factory: Factory[T]): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}