return class extending generic trait in Scala - scala

Scala code:
trait Converter[S, T] {
def convert(source: S): T
}
class StringDuplicatorConverter extends Converter[Integer, String] {
override def convert(source: Integer): String = {
source.toString + source.toString
}
}
// whatever, the point is to show potentially many 'converters'
// from Integer (for example) to something
class SomeOtherConverter extends Converter[Integer, User] {
override def convert(source: Integer): User = {
User(source)
}
}
trait ConverterProvider {
def getConverter[N]: Converter[Integer, N]
}
class MyClass extends ConverterProvider {
override def getConverter[N]: Converter[Integer, N] = {
new StringDuplicatorConverter()
}
}
gives
Error:(17, 5) type mismatch;
found : StringDuplicatorConverter
required: Converter[Integer,N]
new StringDuplicatorConverter()

It may be that what you really want is for each ConverterProvider to provide a converter to a specific type (otherwise the definition of MyClass doesn't make much sense: it should return different converters for different N, not always StringDuplicatorConverter). If so, the correct definition is
trait ConverterProvider[N] {
def getConverter: Converter[Integer, N]
}
class MyClass extends ConverterProvider[String] {
override def getConverter: Converter[Integer, String] = {
new StringDuplicatorConverter()
}
}

Yes. A call to getConverter[N] is supposed to return something of type Converter[Integer,N] but StringDuplicatorConverter is type Converter[Integer,String]. Since N is not restricted to String, and thus they are different types, this won't compile.
If the compiler were given some guarantee that N is a, or super-type of, String then it would work. This can be done by making the return type covariant ...
trait Converter[S, +T] { ...
... and then defining getConverter, and the override, like this:
def getConverter[N >: String]: Converter[Integer, N]
Now it compiles and appears to work.
val mc = new MyClass
mc.getConverter.convert(7) // res0: String = 77

Related

Why does the use of typed variable make this code work

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)

Type inference for nested types in Scala

I want to write a generic class that takes a nested type. The outer type (I) has to extend Iterable, and the inner type (M) can be anything.
Here is the example I have:
// The outer type here is I and the inner type is M
class GenericDistributor[I <: Iterable[M], M] {
def map(input: I): Unit = {
input.foreach(item => {
//do some stuff
})
}
}
class IntegerGroup(id: Int, items: Set[Int]) extends Iterable[Int] {
override def iterator: Iterator[Int] = items.iterator
}
object IntegerGroupDistributor extends GenericDistributor[IntegerGroup, Int]
val integerGroup = new IntegerGroup(1, Set(1,2,3))
IntegerGroupDistributor.map(integerGroup)
The problem is that I have to explicitly define the inner type M in the GenericDistributor class which I do not want to. Is there a way for Scala to automatically infer the inner type given the outer type?
EDIT
According to the comment of #Arioch. I tried duck types and that seems to fix my problem but still I feel there should be neater way.
class GenericDistributor[I <: {type M; def iterator: Iterator[M]}] {
def map(input: I): Unit = {
val it: Iterator[M] = input.iterator
it.foreach(println)
}
}
class IntegerGroup(id: Int, items: Set[Int]) extends Iterable[Int] {
type M = Int
override def iterator: Iterator[Int] = items.iterator
}
object IntegerGroupDistributor extends GenericDistributor[IntegerGroup]
If you don't need to use any custom methods of type I, your external class need only be parameterized on M. The Iterator[M] does not needed to be added separately since you already have all you need to define it from M.
class GenericDistributor[M] {
type I = Iterable[M]
def map(input: I): Unit = {
input.foreach(item => {
//do some stuff
})
}
}
If you only want a single type parameter there are two options:
(1) State that you do not care about the iterator's type
class GenericDistributor[I <: Iterable[_]]
(2) Use an implicit to store the inner type
class GenericDistributor[I : IterableInfo]
// or (equivalently)
class GenericDistributor[I]()(implicit i: IterableInfo[I])
trait IterableInfo[I] {
type Element
}
object IterableInfo {
implicit def forIterable[I <: Iterable[M], M]: IterableInfo[I] { type Element = M } = ...
}
The last option allows you to shape your code in a lot of different ways. You could add methods to IterableInfo, you could add type members, you could add a restriction of Iterable to the I type parameter.

Add a trait to parametric type in a class

I have a library where an abstract class Base[T] is over a type T supplied by the user. There are many specific Base[T] sub-classes, some are over types T and S, like Specific[T, S], but this is irrelevant. The user might specify any T of course while creating and instance, but I want to treat it as T with a trait AdditionalAbilities or in other words I want to 'gift' the user's type with AdditionalAbilities. How can I do that in Scala? I hope the title is correct for this question.
Example (might not be syntactically correct)
class Specific[T **with trait Additional**]() extends Base[T](){
def doSomething() : T = {
val something = new T()
something.ability(2)
println(something.additional)
something
}
}
trait Additional{
var additional : Integer
def ability(i : Integer) : Unit = {
additional = i
}
}
Would work with any T.
When you define a parametric class you can require the parameter type to descend from a certain type:
trait AdditionalAbilities {
def doStuff(): Unit = println("Hey There")
}
object NoAbility extends AdditionalAbilities {
override def doStuff(): Unit = ()
}
abstract class Base[T] { ... }
class Specific[T <: AdditionalAbilities] extends Base[T] {
def f(t: T): Unit = t.doStuff()
}
Then when you try to instantiate a Specific type:
scala> new Specific[Int] {}
<console>:13: error: type arguments [Int] do not conform to class Specific's type parameter bounds [T <: AdditionalAbilities]
scala> val b = new Specific[NoAbility.type] {}
b: Specific[NoAbility.type] = $anon$1#517cd4b
scala> b.f(NoAbility)
//did nothing
Also, if you want to add a behaviour to an existing concrete class, you can do so at the time of instantiation:
trait CoolAbilities { def doStuff(): Unit = println("Hey there") }
class A { }
scala> val a = new A with CoolAbilities
a: A with CoolAbilities = $anon$1#6ad3381f
scala> a.doStuff()
Hey there
Perhaps implicit classes could help? Implicit classes allow you to add functionality to an existing type without needing to modify the existing type, or be the one instantiating it (so that you could mix in a trait).
The following compiles, and prints: 3
class Specific[T] {
implicit class TAdditional(t: T) {
var additional: Integer = 0
def ability(i: Integer) = {
additional = i
}
}
def doSomething(t: T) = {
doSomethingAdditional(t)
}
private def doSomethingAdditional(t: TAdditional) = {
t.ability(3)
println(t.additional)
}
}
val s = new Specific[Int]
s.doSomething(5)
Note: We need to do something to make sure we are accessing the same instance
of TAdditional, that's why I made the private doSomethingAdditional method that takes a TAdditional as an argument. If we call ability and additional in 'doSomething', separate instances of TAdditional would be created when we try to access #ability and #additional, and '0' would be printed.

Scala Stackable Trait and Self Type Incompatible Type

I have a trait called Mutatable that spits out a modified copy of an implementing class. I also have a trait I'd like to stack on top of it called CostedMutatable that keeps track of the cost of doing so. The method applyMutation returns an Option, as later I'd like to return None in cases where a particular mutation doesn't apply.
A simple version that just works on Ints (and "mutates" them by adding in new numbers) is shown below:
trait Mutatable[M] {
def applyMutation(mut : M) : Option[this.type]
}
trait CostedMutatable[M] extends Mutatable[M]{
var cost : Int = _
def getCostFor(mut : M): Int
abstract override def applyMutation(mut : M) : Option[this.type] = {
cost += getCostFor(mut)
applyMutation(mut)
}
}
object Example extends App {
case class Mutation(x: Int)
class Test(s: Int) extends Mutatable[Mutation] {
val start = s
override def applyMutation(mut: Mutation): Option[Test]
= Some(new Test(s+mut.x))
}
class CostTest(s: Int) extends Test(s) with CostedMutatable[Mutation] {
override def getCostFor(mut: Mutation): Int = 2
}
val testCost = new CostTest(5).cost
}
The problem is, this won't compile. I get the following error on compilation:
Error:(23, 18) overriding method applyMutation in trait Mutatable of type (mut: Example.Mutation)Option[Test.this.type];
method applyMutation has incompatible type
override def applyMutation(mut: Mutation): Option[Test] = Some(new Test(s+mut.x))
^
Aside from the compiler error, one other question comes to mind: am I even approaching this the right way? Should I be using F-bounded types instead? (I'll need each new implementing class to return a new copy of the concrete implementing class from applyMutation.)
Thanks in advance.
this.type is a type the only instances of which are this and Nothing. When a method returns this.type, the only allowed return value is this. In class Test applyMutation doesn't return this, but rather a completely new Test, which isn't an instance of this.type. This is why the code does not type-check.
I think what you are really trying to do is declare that applyMutation returns a value of the same class as this. Doing this does indeed require F-Bounded polymorphism. Here is a rewritten version of your code:
trait CostedMutatable[+A <: CostedMutatable[A, M], M] extends Mutatable[A, M] {
var cost : Int = _
def getCostFor(mut : M): Int
abstract override def applyMutation(mut: M): Option[A] = {
cost += getCostFor(mut)
super.applyMutation(mut)
}
}
object Example extends App {
case class Mutation(x: Int)
class Test(s: Int) extends Mutatable[Test, Mutation] {
val start = s
override def applyMutation(mut: Mutation): Option[Test]
= Some(new Test(s+mut.x))
}
class CostTest(s: Int) extends Test(s) with CostedMutatable[CostTest, Mutation] {
override def getCostFor(mut: Mutation): Int = 2
}
val testCost = new CostTest(5).cost
}

How to specify the return type of a function to be a (arbitrary) monad?

In short, I want to declare a trait like this:
trait Test {
def test(amount: Int): A[Int] // where A must be a Monad
}
so that I can use it without knowing what monad that A is, like:
class Usecase {
def someFun(t: Test) = for { i <- t.test(3) } yield i+1
}
more details...
essentially, I want to do something like this:
class MonadResultA extends SomeUnknownType {
// the base function
def test(s: String): Option[Int] = Some(3)
}
class MonadResultB(a: MonadResultA) extends SomeUnknownType {
// added a layer of Writer on top of base function
def test(s: String): WriterT[Option, String, Int] = WriterT.put(a.test(s))("the log")
}
class Process {
def work(x: SomeUnknownType) {
for {
i <- x.test("key")
} yield i+1
}
}
I wanted to be able to pass any instances of MonadResultA or MonadResultB without making any changes to the function work.
The missing piece is that SomeUnknowType, which I guess should have a test like below to make the work function compiles.
trait SomeUnknowType {
def test(s: String): T[Int] // where T must be some Monad
}
As I've said, I'm still learning this monad thing... if you find my code is not the right way to do it, you're more than welcomed to point it out~
thanks a lot~~
Assuming you have a type class called Monad you can just write
def test[A:Monad](amount: Int): A[Int]
The compiler will require that there is an implicit of type Monad[A] in scope when test is called.
EDIT:
I'm still not sure what you're looking for, but you could package up a monad value with its corresponding type class in a trait like this:
//trait that holds value and monad
trait ValueWithMonad[E] {
type A[+E]
type M <: Monad[A]
val v:A[E]
val m:M
}
object M {
//example implementation of test method
def test(amount:Int):ValueWithMonad[Int] = new ValueWithMonad[Int] {
type A[+E] = Option[E]
type M = Monad[Option]
override val v = Option(amount)
override val m = OptionMonad
}
//test can now be used like this
def t {
val vwm = test(1)
vwm.m.bind(vwm.v, (x:Int) => {
println(x)
vwm.m.ret(x)
})
}
}
trait Monad[A[_]] {
def bind[E,E2](m:A[E], f:E=>A[E2]):A[E2]
def ret[E](e:E):A[E]
}
object OptionMonad extends Monad[Option] {
override def bind[E,E2](m:Option[E], f:E=>Option[E2]) = m.flatMap(f)
override def ret[E](e:E) = Some(e)
}