Suppose I have such trait:
trait FooStackable {
def foo: String
def bar: Double
abstract override def logic(x: Double): Unit = {
if (x < 0) println(foo)
else super.logic(bar + x)
}
}
I want to write a test on the method logic for this trait in behaviour driven style: I want to check in test depending on input parameters what method was called and how many times. Apparently it's not possible simply to instantiate an instance of this trait(even when overriding unimplemeted methods), so mocks also will not work.
Does it make sense to write such test scenarios? (Real logic in method logic is more complex) And if yes how stackable traits are tested in isolation?
The code cannot be compiled as written. In particular, there is no 'super' in this trait as it is not derived from anything. I made some changes to work around it:
trait Stackable {
def logic(x: Double): Unit
}
trait FooStackable extends Stackable {
def foo: String
def bar: Double
abstract override def logic(x: Double): Unit = {
println("In FooStackable.logic()")
if (x < 0) println(foo)
else super.logic(bar + x)
}
}
trait TestStackable extends Stackable {
def logic(x: Double): Unit = {
println("In TestStackable.logic()")
}
}
object FooStackableTest extends TestStackable with FooStackable {
def foo: String = "foo"
def bar: Double = 1.234
override def logic(x: Double): Unit = {
println("In FooStackableTest.logic()")
super.logic(x)
}
}
With these changes FooStackable now sits in the middle between TestStackable (which implements logic() in a base class) and FooStackableTest (implements other undefined members of FooStackable and overrides logic()):
scala> FooStackableTest.logic(4.0)
In FooStackableTest.logic()
In FooStackable.logic()
In TestStackable.logic()
scala> FooStackableTest.logic(-4.0)
In FooStackableTest.logic()
In FooStackable.logic()
foo
scala>
Related
I am trying to implement a trait that forces each class that extends it (and is not abstract) to implement certain methods (even if they already exist in super-classes). Concretely it should look like this:
trait Debugable {
override def hashCode(): Int = ???
override def equals(obj: Any): Boolean = ???
override def toString: String = ???
}
That is the trait and this the implementation:
class TestClass {
}
object TestClass{
def main(args: Array[String]): Unit = {
val t = new TestClass
println(t)
}
}
The code above should ideally not compile (since a debugable class does not implement the required methods). In reality this does not only compile, but also throws no run-time exception (it just takes the default implementations of the object class).
Until now nothing managed to generate the expected behaviour. I think macros could help, but I am unsure if macros can express something like:
foreach class
if class.traits.contains(debugable)
return class.methods.contains(toString)
I know that I could let some external script do the check and have it bundled with the gradle compile task, but I am hoping for a solution which can be implemented as part of the project itself (since that would make it independent of the build pipeline used and since it should be simpler and easier to maintain/extend than writing a script crawling the entire source code)
This is close to it (and certainly an improvement over what I have),
but it does not do exactly what I wanted. If I have a "chain" of
classes, then it is enough for the top of the chain to implement the
methods.
Typeclass approach can help with that, for example,
trait Debuggable[T] {
def hashCode(v: T): Int
def equals(v: T, b: Any): Boolean
def toString(v: T): String
}
class Foo
class Bar
class Qux extends Foo
object Debuggable {
implicit val fooDebuggable: Debuggable[Foo] = new Debuggable[Foo] {
def hashCode(v: Foo) = 42
def equals(v: Foo, b: Any) = true
def toString(v: Foo) = "woohoo"
}
implicit val barDebuggable: Debuggable[Bar] = new Debuggable[Bar] {
def hashCode(v: Bar) = 24
def equals(v: Bar, b: Any) = false
def toString(v: Bar) = "boohoo"
}
}
import Debuggable._
def debug[T](v: T)(implicit ev: Debuggable[T]) = ???
debug(new Foo) // OK
debug(new Bar) // OK
debug(new Qux) // Error despite Qux <:< Foo
In my opinion you should just make it abstract.
// Start writing your ScalaFiddle code here
trait Debugable {
def debugHashCode:Int
def debugEquals(obj: Any): Boolean
def debugToString: String
override def hashCode(): Int = debugHashCode
override def equals(obj: Any): Boolean = debugEquals(obj)
override def toString: String = debugToString
}
//this will not compile
class TestClass extends Debugable { }
//this is OK but you need to implement 3 methods later :)
abstract class TestClass2 extends Debugable {}
https://scalafiddle.io/sf/bym3KFM/0
macros should be last thing you try.
Based upon this I wrote the following, which satisfies my need and does override the default implementations:
trait Debuggable_Helper[T]{
def hashCode(v: T): Int
def equals(v: T, b: Any): Boolean
def toString(v: T): String
}
trait Debuggable[T] extends Debuggable_Helper [Debuggable [T]]{
override def hashCode(): Int = hashCode(this)
override def equals(b: Any): Boolean = equals(this, b)
override def toString(): String = toString(this)
}
class Foo extends Debuggable[Foo]{
def hashCode(v: Debuggable[Foo]) = 42
def equals(v: Debuggable[Foo], b: Any) = true
def toString(v: Debuggable[Foo]) = "woohoo"
}
class Qux extends Foo with Debuggable[Qux] //does not compile
object Test{
def main(args: Array[String]): Unit = {
println(new Foo) // OK - prints 'woohoo'
}
}
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)
When I extend traits I can choose which method implementation to use. Like here:
object Main {
def main(args: Array[String]): Unit = {
val c = new C
println(c.a)
println(c.b)
}
trait Parent {
def foo: String
}
trait A extends Parent {
override def foo = "from A"
}
trait B extends Parent {
override def foo = "from B"
}
class C extends A with B {
val b = super[A].foo
val a = super[B].foo
}
}
But if I want to do the same with self-types it's seems like it's not possible:
object Main {
def main(args: Array[String]): Unit = {
val c = new C with A with B
println(c.a)
println(c.b)
}
trait Parent {
def foo: String
}
trait A extends Parent {
override def foo = "from A"
}
trait B extends Parent {
override def foo = "from B"
}
class C {
self: A with B =>
val b = super[A].foo
val a = super[B].foo
}
}
This doesn't compile. Am I right and it's not possible? If I'm right, why is that and is there a workaround for it?
UPDATE:
Why do I needed in a first place? I was playing around with dependency injection using self-types instead of constructor injection. So I had a base trait Converter and child traits FooConverter and BarConverter. And I wanted to write it like that(which doesn't work of course):
object Main {
class Foo
class Bar
trait Converter[A] {
def convert(a: A): String
}
trait FooConverter extends Converter[Foo] {
override def convert(a: Foo): String = ???
}
trait BarConverter extends Converter[Bar] {
override def convert(a: Bar): String = ???
}
class Service {
this: Converter[Foo] with Converter[Bar] =>
def fooBar(f: Foo, b:Bar) = {
convert(f)
convert(b)
}
}
}
I thought it's because of generics, but it turned that it's not. So I was just wondering if it's possible to somehow invoke super method of chosen trait with self-types. Because with simple inheritance it's possible. As for my original problem I can write it like this and it will work:
object Main {
class Foo
class Bar
trait Converter[A] {
def convert(a: A): String
}
trait FooConverter extends Converter[Foo] {
override def convert(a: Foo): String = ???
}
trait BarConverter extends Converter[Bar] {
override def convert(a: Bar): String = ???
}
class Service {
this: FooConverter with BarConverter =>
def fooBar(f: Foo, b:Bar) = {
convert(f)
convert(b)
}
}
}
Probably tighter abstraction, but I'm not sure if it's bad for this kind of situation and if I need such broad abstraction like Converter[A] at all.
Calling super methods from already constructed type is impossible (you can do it only from the inside). In your example, you're trying to call foo on the instance self, which is constructed in runtime, so foo is virtual and could be overridden - compiler doesn't know which actual implementation is going to be called (formal vs real type problem). So technically - it's impossible to do what you want (call virtual method as a static one).
The naive hack is :
trait CC extends A with B {
val b = super[A].foo
val a = super[B].foo
}
class C {
self: CC =>
}
It basically provides encapsulation you want - you might wanna redefine a and b in class C as they're not going to be available (in type C itself) till you mix C with CC.
Note that in every example you provide (including my naive solution) - resulting val c has access to foo anyway and which exact foo is going to be called depends on how do you mix A and B (A with B or B with A). So, the only encapsulation you get is that type C itself isn't going to have foo method. This means that self-type gives you kind of a way to temporary close (make private) a method in "subclass" without violating LSP - but it's not the only way (see below).
Besides all of that, cake-injection that you're trying to implement is considered impractical by some authors. You might want to have a look at Thin Cake Pattern - as a remark, I successfully used something like this in real project (in combination with constructor injection).
I would implement your converter services this way:
class Foo
class Bar
trait Converter[A] {
def convert(a: A): String
}
object FooConverter1 extends Converter[Foo] {
override def convert(a: Foo): String = ???
}
object BarConverter1 extends Converter[Bar] {
override def convert(a: Bar): String = ???
}
trait FooBarConvertService {
def fooConverter: Converter[Foo]
def barConverter: Converter[Bar]
def fooBar(f: Foo, b: Bar) = {
fooConverter(f)
barConverter(b)
}
}
trait Converters {
def fooConverter: Converter[Foo] = FooConverter1
def barConverter: Converter[Bar] = BarConverter1
}
object App extends FooBarConvertService with Converters with ...
This allows you to change/mock converter implementation when putting it all together.
I'd also notice that Converter[Bar] is nothing else but Function1[Bar, String] or just Bar => String, so actually you don't need separate interface for that:
sealed trait FooBar //introduced it just to make types stronger, you can omit it if you prefer
class Foo extends FooBar
class Bar extends FooBar
trait FooBarConvertService {
type Converter[T <: FooBar] = T => String
def fooConverter: Converter[Foo]
def barConverter: Converter[Bar]
def fooBar(f: Foo, b: Bar) = {
fooConverter(f)
barConverter(b)
}
}
trait FooConverterProvider {
def fooConverter: Foo => String = ???
}
trait BarConverterProvider {
def barConverter: Bar => String = ???
}
object App
extends FooBarConvertService
with FooConverterProvider
with BarConverterProvider
You can also use def fooConverter(f: Foo): String = ??? instead def fooConverter: Foo => String = ???.
Talking about encapsulation - it's more weak here as you can access transitive dependencies, so if you really need it - use private[package] modifier.
Converters module:
package converters
trait FooBarConvertService {
type Converter[T <: FooBar] = T => String
private[converters] def fooConverter: Converter[Foo]
private[converters] def barConverter: Converter[Bar]
def fooBar(f: Foo, b: Bar) = {
fooConverter(f)
barConverter(b)
}
}
trait FooConverterProvider {
private[converters] def fooConverter: Foo => String = ???
}
trait BarConverterProvider {
private[converters] def barConverter: Bar => String = ???
}
Core module:
package client
import converters._
object App
extends FooBarConvertService
with FooConverterProvider
with BarConverterProvider
You can use objects object converters {...}; object client {...} instead of packages if you prefer.
This encapsulation is even stronger than self-type based one, as you can't access fooConverter/barConverter from the App object (in your example foo is still accessable from val c = new C with A with B):
client.App.fooBar(new Foo, new Bar) //OK
client.App.fooConverter
<console>:13: error: method fooConverter in trait FooConverterProvider cannot be accessed in object client.App
client.App.fooConverter
^
Keep in mind that self types are meant to allow you to require that any client code that uses the trait you are mixing in must also mix in another trait. In other words it is a way of declaring dependencies. But it is not classical inheritance. So when you say class C { self: A with B => } A and B actually are not there at the time. You have just defined that the client code has to mix in A and B in order to then mix in C.
But for your specific use case, it seems like you can accomplish the same goal with something like this code. In other words first create a third trait and then extend it into a specific class.
object DoubleSelfType extends App {
val c = new DoubleFoo
println(c.a)
println(c.b)
trait Parent {
def foo: String
}
trait A extends Parent {
override def foo = "from A"
}
trait B extends Parent {
override def foo = "from B"
}
trait C {
self: A with B =>
val a = ""
val b = ""
}
class DoubleFoo extends C with A with B {
override val b = super[A].foo
override val a = super[B].foo
}
}
I got the following issue when trying to use typeclasses throughout my project.
trait FooAble[T] { def fa(t: T): List[T] }
object Foo { def apply[T](t: T) = implicitly[FooAble[T]].fa(t) }
trait BarAble[T] { def fb(t: T): Double }
object Bar { def apply[T](t: T) = implicitly[BarAble[T]].fb(t) }
And would like to be able to do the following:
// xs contains elements of type A and B which are subclasses of the trait Something
def f(xs: List[Something]) = {
val something = xs.map(Foo)
val somethingElse = xs.map(Bar)
}
However, this would not work as we don't know if Something implements A[]and B[], no implicit implementation found. What do I need to do so that the elements of the list xs implement the typeclasses FooAble and BarAble?
I think this question: What are type classes in Scala useful for? will help you to understand the proper use (& usefulness) of type classes.
Am just extending the answer by Kevin Wright in the above link for your use case (if I understand your need correctly!):
trait Addable[T] {
def zero: T
def append(a: T, b: T): T
}
trait Productable[T] {
def zero: T
def product(a: T, b: T): T
}
implicit object IntIsAddable extends Addable[Int] {
def zero = 0
def append(a: Int, b: Int) = a + b
}
implicit object IntIsProductable extends Productable[Int] {
def zero = 1
def product(a: Int, b: Int) = a*b
}
def sumAndProduct[T](xs: List[T])(implicit addable: Addable[T], productable: Productable[T]) =
(xs.foldLeft(addable.zero)(addable.append), xs.foldLeft(productable.zero)(productable.product))
So akin to above, in your use case, you need to provide implicit objects which implement your type classes FooAble & BarAble and your method signature for function f becomes:
def f[Something](xs: List[Something])(implicit fooable: FooAble[Something], barable: BarAble[Something])
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)
}