I am translating the Haskell code in https://sebfisch.github.io/haskell-regexp/regexp-play.pdf in Scala for self study.
I translated "class Semiring" (page 2) in this way:
trait Semiring[S] {
def zero: S
def one: S
def add(a: S, b: S): S
def mult(a: S, b: S): S
}
and the related class SemiringI (page 7) in this way:
trait SemiringI[S] extends Semiring[S] {
def index: Int => S
}
I had then to provide instances for specific type parameters, so I tried to proceed in what I think is the canonical way, i.e., defining them as implicit vals.
implicit val semiringLeftmost = new Semiring[LeftmostT] {
// ...implementation of zero, one, add, mult...
}
However, I had some problems when I had to define a SemiringI instance for LeftmostT:
implicit val semiringILeftmost = new SemiringI[LeftmostT] {
// ...implementation of zero, one, add, mult (same as for Semiring[LeftmostT])
// ...implementation of index
}
It seemed to me I had to repeat the implementations of the functions already defined in Semiring[LeftmostT], that, of course, would not have been scalable.
I searched the web for answers, but I was not able to find them.
For example, in https://www.slideshare.net/jdegoes/scalaz-8-a-whole-new-game (slide 7) MonoidInt does not reuse the definition of append from Semigroup, as I expected.
At the end, I managed to find a way to proceed, namely:
// traits Semiring[S] and SemiringI[S] defined as above
class semiringLeftmostC extends Semiring[LeftmostT] {
// ...implementation of zero, one, add, mult...
}
implicit val semiringLeftmost = new semiringLeftmostC()
class semiringILeftmostC extends semiringLeftmostC with SemiringI[LeftmostT] {
// ...implementation of index
}
implicit val semiringILeftmost = new semiringILeftmostC()
but I am not sure it is the best one.
Can someone please explain to me whether there are other possibilities to reach the same goals, i.e., reusing code in hierarchies of typeclass instances?
Thanks in advance.
Thanks to this post, and the advent of Scala 3, I was able to write a concise version of the class hierarchy:
trait Semiring[S] {
def zero: S
def one: S
def add(a: S, b: S): S
def mult(a: S, b: S): S
}
trait SemiringI[S] extends Semiring[S] {
def index(i: Int): S
}
given semiringLeftmost: Semiring[LeftmostT] with {
// implementation of Semiring functions
}
given semiringILeftmost: SemiringI[LeftmostT] with {
export semiringLeftmost.*
override def index(i: Int): LeftmostT = ??? // actual implementation not relevant
}
I can infer by the mentioned post, however, that there seems to be no consensus on how to encode typeclass hierarchies in Scala 3. The official documentation examples I found seem to redefine functions in the derived traits (https://dotty.epfl.ch/docs/reference/contextual/type-classes.html).
Related
My motivation is I want an abstract class for sorting functions. Right now that looks like:
abstract class Sorter[A <% Ordered[A]]{
def apply(xs: Seq[A]): Seq[A]
}
But I'm not sure how to make a concrete instance of it, i.e.:
val mySort = new Sorter {
def apply(xs: Seq[A]) = { ... }
}
I suppose my question is two-fold:
Is my abstract class thing even correct?
How do I parameterize mySort?
View bounds are deprecated so if you don't have to use them you could write something simple like:
trait Sorter[A] {
def sort(xs: Seq[A])(implicit ord: Ordering[A]): Seq[A]
}
new Sorter[Int] {
override def sort(xs: Seq[X])(implicit ord: Ordering[X]): Seq[X] = {
xs.sorted
}
}
Sorter is a trait but could well be an abstract class. Parameterizing instance is done right before the brackets as shown in the example.
On a side note, in Scala I don't see the need for an abstract class (or trait) for ordering things.
The collections already define the sorted and sortBy methods. So when you have a collection col of A you can sort it with col.sorted provided you have an implicit Ordering[A] in scope.
It's up to you to figure out if Ordering is what you want or if abstract class and concrete implementation is required.
Keep in mind you can always implement you custom Ordering like:
new Ordering[Int] {
override def compare(x: X, y: X) = ???
}
What I would like to do is this:
trait Addable[T]{
def plus(x: T): T
}
trait AddableWithBounds[T] extends Addable[T] {
abstract override def plus(x: T): T = limitToBounds(super.plus(x))
def limitToBounds(x: T): T = ... //Some bounds checking
}
class Impl(num: Int) extends AddableWithBounds[Impl] {
override def plus(x: Impl) = new Impl(num + x.num)
}
Reading through various posts it would seem that the reason this is not possible is that after class linearization the stacked trait is only looking in classes to its right for implementations of super.plus(x: T)
That is a technical argument though and I'm interested in the question: Is there a fundamental reason why the implementation could not be taken from the base class? Because as you can see in this example it is not possible to implement the plus method before knowing the actual data type that needs to be added but afaic implementing bounds checking in a trait seems reasonable.
Just as an idea: If the problem is that in this case it is unclear which plus is being overridden in the base class maybe a scope identifier like
def Addable[Impl].plus(x: Impl) = new Impl(num + x.num)
would help.
I would avoid overriding and reliance on some super/sub-type invocation. Why not just implement plus and ask sub-types to provide an auxiliary abstract method:
trait Addable[T] {
def plus(x: T): T
}
trait AddableWithBounds[T] extends Addable[T] {
final def plus(x: T): T = limitToBounds(plusImpl(x))
protected def plusImpl(x: T): T
def limitToBounds(x: T): T = ??? //Some bounds checking
}
class Impl(num: Int) extends AnyRef with AddableWithBounds[Impl] {
protected def plusImpl(x: Impl): Impl = ???
}
You're asking: if super.f is abstract (or "incomplete") after mixin, please just pick a concrete f to serve. Maybe you're saying, only if there's exactly one such f, and it's defined in the current template.
One problem is that if the abstract f is called during initialization, and calls the concrete f in the subclass, the initializer for the subclass hasn't run yet.
This is common gotcha for template methods. I think the Josh Bloch book says don't do that.
But the person writing the stackable trait has no way of knowing that a super was transformed in this way.
Ideally I'd like to be able to do the following in Scala:
import Builders._
val myBuilder = builder[TypeToBuild] // Returns instance of TypeToBuildBuilder
val obj = myBuilder.methodOnTypeToBuildBuilder(...).build()
In principle the goal is simply to be able to 'map' TypeToBuild to TypeToBuildBuilder using external mapping definitions (i.e. assume no ability to change these classes) and leverage this in type inferencing.
I got the following working with AnyRef types:
import Builders._
val myBuilder = builder(TypeToBuild)
myBuilder.methodOnTypeToBuildBuilder(...).build()
object Builders {
implicit val typeToBuildBuilderFactory =
new BuilderFactory[TypeToBuild.type, TypeToBuildBuilder]
def builder[T, B](typ: T)(implicit ev: BuilderFactory[T, B]): B = ev.create
}
class BuilderFactory[T, B: ClassTag] {
def create: B = classTag[B].runtimeClass.newInstance().asInstanceOf[B]
}
Note that the type is passed as a function argument rather than a type argument.
I'd be supremely happy just to find out how to get the above working with Any types, rather than just AnyRef types. It seems this limitation comes since Singleton types are only supported for AnyRefs (i.e. my use of TypeToBuild.type).
That being said, an answer that solves the original 'ideal' scenario (using a type argument instead of a function argument) would be fantastic!
EDIT
A possible solution that requires classOf[_] (would really love not needing to use classOf!):
import Builders._
val myBuilder = builder(classOf[TypeToBuild])
myBuilder.methodOnTypeToBuildBuilder(...).build()
object Builders {
implicit val typeToBuildBuilderFactory =
new BuilderFactory[classOf[TypeToBuild], TypeToBuildBuilder]
def builder[T, B](typ: T)(implicit ev: BuilderFactory[T, B]): B = ev.create
}
class BuilderFactory[T, B: ClassTag] {
def create: B = classTag[B].runtimeClass.newInstance().asInstanceOf[B]
}
Being able to just use builder(TypeToBuild) is really just a win in elegance/brevity. Being able to use builder[TypeToBuild] would be cool as perhaps this could one day work (with type inference advancements in Scala):
val obj: TypeToBuild = builder.methodOnTypeToBuildBuilder(...).build();
Here is a complete, working example using classOf: http://ideone.com/94rat3
Yes, Scala supports return types based on the parameters types. An example of this would be methods in the collections API like map that use the CanBuildFrom typeclass to return the desired type.
I'm not sure what you are trying to do with your example code, but maybe you want something like:
trait Builder[-A, +B] {
def create(x: A): B
}
object Builders {
implicit val int2StringBuilder = new Builder[Int, String] {
def create(x: Int) = "a" * x
}
def buildFrom[A, B](x: A)(implicit ev: Builder[A, B]): B = ev.create(x)
}
import Builders._
buildFrom(5)
The magic with newInstance only works for concrete classes that have a constructor that takes no parameters, so it probably isn't generic enough to be useful.
If you're not afraid of implicit conversions, you could do something like this:
import scala.language.implicitConversions
trait BuilderMapping[TypeToBuild, BuilderType] {
def create: BuilderType
}
case class BuilderSpec[TypeToBuild]()
def builder[TypeToBuild] = BuilderSpec[TypeToBuild]
implicit def builderSpecToBuilder[TypeToBuild, BuilderType]
(spec: BuilderSpec[TypeToBuild])
(implicit ev: BuilderMapping[TypeToBuild, BuilderType]) = ev.create
case class Foo(count: Int)
case class FooBuilder() {
def translate(f: Foo) = "a" * f.count
}
implicit val FooToFooBuilder = new BuilderMapping[Foo, FooBuilder] {
def create = FooBuilder()
}
val b = builder[Foo]
println(b.translate(Foo(3)))
The implicit conversions aren't too bad, since they're constrained to these builder-oriented types. The conversion is needed to make b.translate valid.
It looked like wingedsubmariner's answer was most of what you wanted, but you didn't want to specify both TypeToBuild and BuilderType (and you didn't necessarily want to pass a value). To achieve that, we needed to break up that single generic signature into two parts, which is why the BuilderSpec type exists.
It might also be possible to use something like partial generic application (see the answers to a question that I asked earlier), though I can't put the pieces together in my head at the moment.
I'll resort to answering my own question since a Redditor ended up giving me the answer I was looking for and they appear to have chosen not to respond here.
trait Buildable[T] {
type Result
def newBuilder: Result
}
object Buildable {
implicit object ABuildable extends Buildable[A] {
type Result = ABuilder
override def newBuilder = new ABuilder
}
implicit object BBuildable extends Buildable[B] {
type Result = BBuilder
override def newBuilder = new BBuilder
}
}
def builder[T](implicit B: Buildable[T]): B.Result = B.newBuilder
class ABuilder {
def method1() = println("Call from ABuilder")
}
class BBuilder {
def method2() = println("Call from BBuilder")
}
Then you will get:
scala> builder[A].method1()
Call from ABuilder
scala> builder[B].method2()
Call from BBuilder
You can see the reddit post here: http://www.reddit.com/r/scala/comments/2542x8/is_it_possible_to_define_a_function_return_type/
And a full working version here: http://ideone.com/oPI7Az
Suppose one wants to build a novel generic class, Novel[A]. This class will contain lots of useful methods--perhaps it is a type of collection--and therefore you want to subclass it. But you want the methods to return the type of the subclass, not the original type. In Scala 2.8, what is the minimal amount of work one has to do so that methods of that class will return the relevant subclass, not the original? For example,
class Novel[A] /* What goes here? */ {
/* Must you have stuff here? */
def reverse/* What goes here instead of :Novel[A]? */ = //...
def revrev/*?*/ = reverse.reverse
}
class ShortStory[A] extends Novel[A] /* What goes here? */ {
override def reverse: /*?*/ = //...
}
val ss = new ShortStory[String]
val ss2 = ss.revrev // Type had better be ShortStory[String], not Novel[String]
Does this minimal amount change if you want Novel to be covariant?
(The 2.8 collections do this among other things, but they also play with return types in more fancy (and useful) ways--the question is how little framework one can get away with if one only wants this subtypes-always-return-subtypes feature.)
Edit: Assume in the code above that reverse makes a copy. If one does in-place modification and then returns oneself, one can use this.type, but that doesn't work because the copy is not this.
Arjan linked to another question that suggests the following solution:
def reverse: this.type = {
/*creation of new object*/.asInstanceOf[this.type]
}
which basically lies to the type system in order to get what we want. But this isn't really a solution, because now that we've lied to the type system, the compiler can't help us make sure that we really do get a ShortStory back when we think we do. (For example, we wouldn't have to override reverse in the example above to make the compiler happy, but our types wouldn't be what we wanted.)
Edit: I just realized that Rex had a concrete class Novel in his example, not a trait as I've used below. The trait implementation is a bit too simple to be a solution to Rex's question, therefore. It can be done as well using a concrete class (see below), but the only way I could make that work is by some casting, which makes this not really 'compile time type-safe'. This So this does not qualify as a solution.
Perhaps not the prettiest, but a simple example using abstract member types could be implemented as follows:
trait Novel[A] {
type T <: Novel[A]
def reverse : T
def revrev : T#T = reverse.reverse
}
class ShortStory[A](var story: String) extends Novel[A] {
type T = ShortStory[A]
def reverse : T = new ShortStory[A](story reverse)
def myMethod: Unit = println("a short story method")
}
scala> val ss1 = new ShortStory[String]("the story so far")
ss1: ShortStory[String] = ShortStory#5debf305
scala> val ssRev = ss1 reverse
ssRev: ss1.T = ShortStory#5ae9581b
scala> ssRev story
res0: String = raf os yrots eht
scala> val ssRevRev = ss1 revrev
ssRevRev: ss1.T#T = ShortStory#2429de03
scala> ssRevRev story
res1: String = the story so far
scala> ssRevRev myMethod
a short story method
It's certainly minimal, but I doubt whether this would enough to be used as a kind of framework. And of course the types returned not anywhere near as clear as in the Scala collections framework, so perhaps this might be a bit too simple. For the given case, it seems to do the job, however. As remarked above, this does not do the job for the given case, so some other solution is required here.
Yet Another Edit: Something similar can be done using a concrete class as well, though that also not suffices to be type safe:
class Novel[A](var story: String) {
type T <: Novel[A]
def reverse: T = new Novel[A](story reverse).asInstanceOf[T]
def revrev : T#T = reverse.reverse
}
class ShortStory[A](var s: String) extends Novel[A](s) {
type T = ShortStory[A]
override def reverse : T = new ShortStory(story reverse)
def myMethod: Unit = println("a short story method")
}
And the code will work as in the trait example. But it suffers from the same problem as Rex mentioned in his edit as well. The override on ShortStory is not necessary to make this compile. However, it will fail at runtime if you don't do this and call the reverse method on a ShortStory instance.
I haven't thought this through fully, but it type checks:
object invariant {
trait Novel[A] {
type Repr[X] <: Novel[X]
def reverse: Repr[A]
def revrev: Repr[A]#Repr[A]
= reverse.reverse
}
class ShortStory[A] extends Novel[A] {
type Repr[X] = ShortStory[X]
def reverse = this
}
val ss = new ShortStory[String]
val ss2: ShortStory[String] = ss.revrev
}
object covariant {
trait Novel[+A] {
type Repr[X] <: Novel[_ <: X]
def reverse: Repr[_ <: A]
def revrev: Repr[_ <: A]#Repr[_ <: A] = reverse.reverse
}
class ShortStory[+A] extends Novel[A] {
type Repr[X] = ShortStory[X]
def reverse = this
}
val ss = new ShortStory[String]
val ss2: ShortStory[String] = ss.revrev
}
EDIT
The co-variant version can be much nicer:
object covariant2 {
trait Novel[+A] {
type Repr[+X] <: Novel[X]
def reverse: Repr[A]
def revrev: Repr[A]#Repr[A] = reverse.reverse
}
class ShortStory[+A] extends Novel[A] {
type Repr[+X] = ShortStory[X]
def reverse = this
}
val ss = new ShortStory[String]
val ss2: ShortStory[String] = ss.revrev
}
After discussions on the Scala mailing list--many thanks to the people there for setting me on the right track!--I think that this is the closest that one can come to a minimal framework. I leave it here for reference, and I'm using a different example because it highlights what is going on better:
abstract class Peano[A,MyType <: Peano[A,MyType]](a: A, f: A=>A) {
self: MyType =>
def newPeano(a: A, f: A=>A): MyType
def succ: MyType = newPeano(f(a),f)
def count(n: Int): MyType = {
if (n<1) this
else if (n==1) succ
else count(n-1).succ
}
def value = a
}
abstract class Peano2[A,MyType <: Peano2[A,MyType]](a: A, f: A=>A, g: A=>A) extends Peano[A,MyType](a,f) {
self: MyType =>
def newPeano2(a: A, f: A=>A, g: A=>A): MyType
def newPeano(a: A, f: A=>A): MyType = newPeano2(a,f,g)
def pred: MyType = newPeano2(g(a),f,g)
def uncount(n: Int): MyType = {
if (n < 1) this
else if (n==1) pred
else uncount(n-1).pred
}
}
The key here is the addition of the MyType type parameter that is a placeholder for the type of the class that we'll really end up with. Each time we inherit, we have to redefine it as a type parameter, and we have add a constructor method that will create a new object of this type. If the constructor changes, we have to create a new constructor method.
Now when you want to create a class to actually use, you only have to fill in the constructor method with a call to new (and tell the class that it's of its own type):
class Peano2Impl[A](a: A, f: A=>A, g: A=>A) extends Peano2[A,Peano2Impl[A]](a,f,g) {
def newPeano2(a: A, f: A=>A, g: A=>A) = new Peano2Impl[A](a,f,g)
}
and you're off and running:
val p = new Peano2Impl(0L , (x:Long)=>x+1 , (y:Long)=>x-1)
scala> p.succ.value
res0: Long = 1
scala> p.pred.value
res1: Long = -1
scala> p.count(15).uncount(7).value
res2: Long = 8
So, to review, the minimal boilerplate--if you want to include recursive methods, which breaks the other style of answer--is for any methods that return a new copy from outside the class (using new or a factory or whatever) to be left abstract (here, I've boiled everything down to one method that duplicates the constructor), and you have to add the MyType type annotation as shown. Then, at the final step, these new-copy methods have to be instantiated.
This strategy works fine for covariance in A also, except that this particular example doesn't work since f and g are not covariant.
In Scala v 2.7.7
I have a file with
class Something[T] extends Other
object Something extends OtherConstructor[Something]
This throws the error:
class Something takes type parameters
object Something extends OtherConstructor[Something] {
However, I can't do this
object Something[T] extends OtherConstructor[Something[T]]
It throws an error:
error: ';' expected but '[' found.
Is it possible to send type parameters to object? Or should I change and simply use Otherconstructor
You could use:
object Something extends OtherConstructor[Something[_]]
You will of course be restricted by having an existential type with no upper bound in place instead of a concrete type. This solution may not make sense and you might need one object per concrete type T, for those T's which you care about, e.g.
object StringSomething extends OtherConstructor[Something[String]]
But then this has the (possible) disadvantage that StringSomething is not the companion object of Something.
However, my advice would be don't start messing about designing generic APIs (especially self-referential ones like the above) unless you really, really know what you are doing. It will almost certainly end in tears and there are plenty of CORE Java API's which are terrible because of the way generics have been added (the RowSorter API on JTable being one example)
You can solve the general problem of needing object Foo[T] by moving the type parameter to the methods in object Foo:
class Foo[T](t1: T, t2: T)
object Foo {
def apply[T](x: T): Foo[T] = new Foo(x, x)
def apply[T](x: T, y: T): Foo[T] = new Foo(x, y)
}
If you really need one object per T, you can make a class, and have the type-free companion return it from apply.
class Foo[T](t1: T, t2: T)
class FooCompanion[T] {
def apply(x: T): Foo[T] = new Foo(x, x)
def apply(x: T, y: T): Foo[T] = new Foo(x, y)
}
object Foo {
def apply[T] = new FooCompanion[T]
}
object demo extends App {
val x: Foo[Double] = Foo.apply.apply(1.23) // this is what is really happening
val y: Foo[Int] = Foo[Int](123) // with the type both apply calls are automatic
}
Note this will re-construct the Foo[T] companion on each call so you would want to keep it light and stateless.
An explicit solution the the problem above:
class Other
class OtherConstructor[O <: Other] {
def apply(o: O): O = o // constructor 1 in base class
}
class Something[T](value: T) extends Other
class SomethingConstructor[T] extends OtherConstructor[Something[T]] {
def apply(o: T, s: String) = new Something[T](o) // constructor 2 in subclass
}
object Something {
def apply[T] = new SomethingConstructor[T] // the "constructor constructor" method
}
object demoX extends App {
val si = new Something(123)
val sd = new Something(1.23)
val si1: Something[Int] = Something[Int](si) // OtherConstructor.apply
val sd1: Something[Double] = Something[Double](1.23, "hello") // SomethingConstructor[Double].apply
}
An object has to have a concrete type. The Scala object contruct is not a exception to this rule.
A valid definition is
object Something extends OtherConstructor[Something[T]] { }
where T is some concrete type.
Thanks for the answers
object Something extends OtherConstructor[Something[_]]
seems to be compiling (although I have yet to run/test that :-))
#oxbow_lakes, I've followed your advice - of avoiding the type system - till now but I've got to do it!!!
I've been studying existential types, type-erasure and all that but its still not in my grasp :-(