I just found the type class concept of Scala and like it actually very much. The problem I have it that all examples I found put the type classes (objects) under one object (Like here: http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html)
Maybe this is wrong but I am a friend of having one file per class. So what I like to do is to put the type classes into several files which would mean that each implementation needs to be put into a seperate object. This now leads to the "problem" that I need to import every single implementation manually. To clarify better here a small example:
package sth
import sth.like.Like
import sth.like.StringLike._
import sth.like.IntLike._
object Application extends App {
def t[T](arg: T)(implicit l: Like[T]) = {
l.print(arg)
}
t(1)
t("Blub")
}
-
package sth.like
trait Like[T] {
def print(arg: T)
}
-
package sth.like
object IntLike {
implicit object LikeIntLike extends Like[Int] {
def print(arg: Int): Unit = println(s"INT $arg")
}
}
-
package sth.like
object StringLike {
implicit object LikeStringLike extends Like[String] {
override def print(arg: String): Unit = println(s"STRING: $arg")
}
}
This works so far. I know, the wildcard import in Application is not necessary but that is not the point. As you can see I need to import both StringLike and IntLike in Application because otherwise these are not available for the Application object.
So is it possible to do this in a generic way or is it completely bad practise to do it this way?
You could put your implicits inside traits, not objects
trait IntLike {
implicit object LikeIntLike extends Like[Int] {
def print(arg: Int): Unit = println(s"INT $arg")
}
}
And then have object with the same name, extending this trait, which would give you what you had earlier, you can import each implicit individually.
object IntLike extends IntLike
You can do the same for all other instances of your typeclass:
trait StringLike {
implicit object LikeStringLike extends Like[String] {
override def print(arg: String): Unit = println(s"STRING: $arg")
}
}
object StringLike extends StringLike
And in another file, you can combine all traits together like this:
object Implicits extends StringLike with IntLike
and import just the Implicits object if you want all your implicits in scope.
package sth
import sth.like.Like
import sth.like.Implicits._
object Application extends App {
def t[T](arg: T)(implicit l: Like[T]) = {
l.print(arg)
}
t(1)
t("Blub")
}
Optionally, you can mixin traits you need
object Application extends App with StringLike with IntLike { ... }
or even do
trait Implicits extends StringLike with IntLike
object Implicits extends Implicits
object Application extends App with Implicits { ... }
thank you very much for your answer. But I also would need to add new type classes manually to the Implicits, wouldn't I?
Couldn't I use then package objects for simplicity reasons like here:
http://naildrivin5.com/scalatour/wiki_pages/PackageObjects/
?
I mean this part:
package opower {
package object controller {
type Secured = org.springframework.security.access.annotation.Secured
type Controller = org.springframework.stereotype.Controller
...
}
}
Since it seems that there is no dynamic way I could just put all type classes into the controller object and could this then import.
Or do I missunderstand the package object?
Related
I have a JSON protocol written in spray
trait MyJsonProtocol {
//some logic
}
object MyJsonProtocol extends MyJsonProtocol {
}
Now which is better ?? Importing this companion object or extending the trait ?
If you are creating some JsonFormat instances for spray, then you can just create an object directly and import that. This means that you only have a single instance of your implicit vals and objects.
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object MyTypeJsonFormat extends RootJsonFormat[MyType] {
def write(v: MyType): JsValue = ...
def read(value: JsValue): MyType = ...
}
implicit val myClassFormat = jsonFormat5(MyClass)
}
class OtherClass {
import MyJsonProtocol._
...
}
It depends on the scenario, Because a companion class or object can access the private members of its companion. Use a companion object for methods and values which are not specific to instances of the companion class. If you just want multiple inheritance but allow code reusability then trait is fine.
Hope it helps.
This depends on your logic. If you define some implicits, importing an object and extending a trait are different. If you import you define implicits of the same priority as local ones. If you extend you create low-priority implicits in comparison with local ones.
I have a package foo which contains class FStream. The package object of foo defines a few implicit value classes that provide extender methods for FStream. I would like to move these value classes out of the package object and into their own individual files, but I also want them to always be available when I use FStream (or preferably, when I use anything from foo package. Is it possible to accomplish this? I tried putting implicit value classes into other objects, but I can't extend from objects. Tried putting them in classes or traits, but implicit value classes can only be defined in other objects.
foo/FStream.scala
package foo
class FStream {
def makeFoo(): Unit = ???
}
foo/package.scala
package foo
package object foo {
// I want to move these definitions into separate files:
implicit class SuperFoo(val stream: FStream) extends AnyVal {
def makeSuperFoo(): Unit = ???
}
implicit class HyperFoo(val stream: FStream) extends AnyVal {
def makeHyperFoo(): Unit = ???
}
}
bar/usage.scala
package bar
import foo._ // something nice and short that doesn't reference individual value classes
val x: FStream = ???
x.makeSuperFoo() // should work
x.makeHyperFoo() // should work
I recommend you to read the mandatory tutorial first.
My solution is to use FStream's companion object. So you can just import FStream and get all the functionality. This also uses trait to separate files.
foo/FStream.scala
package foo
class FStream {
def makeFoo(): Unit = ???
}
// companion provides implicit
object FStream extends FStreamOp
foo/FStreamOp.scala
package foo
// value class may not be a member of another class
class SuperFoo(val stream: FStream) extends AnyVal {
def makeSuperFoo(): Unit = ???
}
class HyperFoo(val stream: FStream) extends AnyVal {
def makeHyperFoo(): Unit = ???
}
trait FStreamOp {
// you need to provide separate implicit conversion
implicit def makeSuper(stream: FStream) = new SuperFoo(stream)
implicit def makeHyper(stream: FStream) = new HyperFoo(stream)
}
usage.scala
import foo.FStream
object Main {
def main(args: Array[String]): Unit = {
val x: FStream = ???
x.makeSuperFoo() // should work
x.makeHyperFoo() // should work
}
}
Say I have the following two typeclasses:
object Foo {
sealed trait FooClass[A]
implicit object FooString extends FooClass[String]
implicit object FooInt extends FooClass[Int]
}
object Bar {
trait BarClass[A] //leave this one unsealed as we're attempting to prove that Foo is a subset of Bar, not the other way around.
implicit object BarString extends BarClass[String]
implicit object BarInt extends BarClass[Int]
implicit object BarDouble extends BarClass[Double]
}
is it possible to provide a hint to the compiler that Foo is a subset of Bar somehow, such that the following would compile, without a change to the type signatures of either method?
class Outer(callee : Inner) {
import Foo._
def call[A : FooClass](a) : Unit = callee.call(a);
}
class Inner
import Bar._
def call[B : BarClass](b) : Unit = ();
}
val test = new Outer(new Inner)
test.call(123)
test.call("Hello World")
My main objective is to allow the Outer class to be entirely unaware of the existence of the BarClass typeclass - using the Inner class to abstract over it entirely. Is this possible? For those who are interested, there's some context over in this git repository - the commit descriptions have some more detailed exposition of the problem I'm trying to solve).
I'm trying to provide different sets of implementations for a list of type classes, where importing different package objects would give the end user a different version of a TypeClass implementation into scope, with the swap being completely invisible.
trait TypeClass[T] {
def name: String
}
trait DefaultTypeClasses {
implicit val String: TypeClass[String]
implicit val Int: TypeClass[Int]
}
trait FirstImplementor extends DefaultTypeClasses {
implicit object String extends TypeClass[String] {
def name = "test"
}
implicit object Int extends TypeClass[Int] {
def name = "int"
}
}
object FirstImplementor extends FirstImplementor
object Test {
import FirstImplementor._
def doSomething[T : TypeClass](value: T): Unit = {
println(implicitly[TypeClass[T]].name)
}
doSomething("test")
}
The above works as expected, but if do:
trait DefaultDefinitions extends DefaultTypeClasses {
}
package object something extends DefaultDefinitions with FirstImplementor {}
And then I import the same package object into the scope of the Test object, like this:
import com.blabla.something._ // should bring all type class definitions from FirstImplementor into scope.
object Test {
def doSomething[T : TypeClass](value: T): Unit = {
println(implicitly[TypeClass[T]].name)
}
doSomething("test")// Cannot find implicit value for TypeClass[String]
doSomething[String]("test")(String) // if passed explicitly it compiles as expected, no more imports necessary.
}
For whatever reason, the materialised type class is available in explicit scope by name, but not in implicit scope. Sounds like my knowledge of the SLS is experiencing a gap, could anyone please clarify?
But if you create Implicits object in package, it works fine. However I have no idea if original error is a bug in scalac or correct behavior
package object something {
object Implicits extends DefaultDefinitions with FirstImplementor
}
import something.Implicits._
I'm trying to import a bunch of methods from one class to another without extending it. I've made it work but why one approach works and the other doesn't is beyond me.
Stripped down, here is what I'm trying to do
class A {def x() {println("x")}}
object A
class B {
import A._
def y() {x()}
}
And the compiler tells me "not found: value x"
But it works if I do either this
class C extends A
class B {
import C._
or if I do this
object C extends A
class B {
import C._
Can someone explain why this is the case?
The reason why your code example class C extends A is not working is that you import class members which can only exist in the class they are defined.
Whereas when you write object C extends A you will create a singleton (in Scala called object like the keyword) which represents an instance and allows you to import its members.
So, to make members of other classes visible you always have to extend them, either by an object or by another class/trait. It is not enough to declare a companion object of a class because it does not hold an instance of its companion class.
There also the possibility of using implicits.
You need to have a way to get from a instance of B to the desired instance of
A. In the example below I use a member value, but it should be possible to make
it a function as well.
The trait exposed exposes the this in B to the implicits declared in A.
The trait PathTo can be used to expose a path to the desired instance of A.
class A {
def a1(){ println("a1") };
def a2(){ println("a2") };
def a3(){ println("a3") };
}
object A{
def a1()(implicit a:A){a.a1};
def a2()(implicit a:A){a.a2};
def a3()(implicit a:A){a.a3};
//makes it possible to use a1() instead of a1()(this.a)
implicit def insertPathToA(implicit path:PathTo[A]):A=path.pathTo;
// Makes it possible to write this.a2() instead of this.a.a2();
implicit def convertPathToA(path:PathTo[A]):A=path.pathTo;
};
trait exposed[U]{
implicit def self:U=this.asInstanceOf[U];
}
trait PathTo[U]{
implicit def pathTo:U;
}
class B(val a:A) extends exposed[B] with PathTo[A] {
// imports the magic
import A._
override def pathTo:A=a;
def y() {
a1() ;
this.a2();
};
}