Overload constructor for Scala's Case Classes? - scala

In Scala 2.8 is there a way to overload constructors of a case class?
If yes, please put a snippet to explain, if not, please explain why?

Overloading constructors isn't special for case classes:
case class Foo(bar: Int, baz: Int) {
def this(bar: Int) = this(bar, 0)
}
new Foo(1, 2)
new Foo(1)
However, you may like to also overload the apply method in the companion object, which is called when you omit new.
object Foo {
def apply(bar: Int) = new Foo(bar)
}
Foo(1, 2)
Foo(1)
In Scala 2.8, named and default parameters can often be used instead of overloading.
case class Baz(bar: Int, baz: Int = 0)
new Baz(1)
Baz(1)

You can define an overloaded constructor the usual way, but to invoke it you have to use the "new" keyword.
scala> case class A(i: Int) { def this(s: String) = this(s.toInt) }
defined class A
scala> A(1)
res0: A = A(1)
scala> A("2")
<console>:8: error: type mismatch;
found : java.lang.String("2")
required: Int
A("2")
^
scala> new A("2")
res2: A = A(2)

Related

Scala: implicitly to implicit class

Given:
implicit class Foo(val i: Int) {
def addValue(v: Int): Int = i + v
}
is it possible apply to it any implicitly?
I get an error here:
<console>:14: error: could not find implicit value for parameter e: Foo
implicitly[Foo]
An implicit class Foo(val i: Int) means that there is an implicit conversion from Int to Foo. So implicitly[Int => Foo] should work.
Think about it like this: if you could summon a Foo with implicitly[Foo], which Foo would you expect to get? A Foo(0)? A Foo(1)? A Foo(2)?
For further details,
implcitly key word can be explained as following
implitly[T] means return implicit value of type T in the context
Which means, to get Foo implicitly you need to create an implicit value in the scope
For example,
implicit class Foo(val i: Int) {
def addValue(v: Int): Int = i + v
}
implicit val foo:Foo = Foo(1)
val fooImplicitly = implicitly[Foo] // Foo(1)
Also, note that Foo itself is only a class,
But by putting implicit key word in front of class definition,
Compiler creates an implicit function of type Int => Foo

Tupled method for case class having a type parameter

When having a case class with a type parameter, I don't see how I can call the tupled method. It seems to be find with apply and unapply.
scala> case class Foo[T](x:T, y:T)
defined class Foo
scala> Foo.apply[Int] _
res1: (Int, Int) => Foo[Int] = <function2>
scala> Foo.unapply[Int] _
res2: Foo[Int] => Option[(Int, Int)] = <function1>
scala> Foo.tupled[Int] _
<console>:10: error: value tupled is not a member of object Foo
Foo.tupled[Int] _
^
Any idea on what's going on?
tl;dr
Companion objects of case classes cannot extend FunctionN (which defines tupled, with N >= 2) when they have type parameters. Use
(Foo[Int] _).tupled
discussion
When you have a vanilla class such as
case class Bar(x: Int, y: Int)
its constructor is effectively a Function2[Int, Int, Bar], hence when the compiler generates the companion object Bar it can conveniently make it extend Function2.
The generated code will then be
class Bar extends AnyRef with Product with Serializable { ... }
object Bar extends Function2[Int, Int, Bar] with Serializable { ... }
Now consider
case class Foo[T](x: T, y: T)
If you try to apply the same trick, you'll find yourself in trouble very soon:
// this can't compile, what's T?
object Foo extends Function2[T, T, Bar] with Serializable { ... }
Since T is unknown, the compiler can't make Foo a subclass of Function2 and it can't do much better than make it extend AnyRef:
class Foo[T] extends AnyRef with Product with Serializable { ... }
object Foo extends AnyRef with Serializable { ... }
Here's a quick proof of what discussed above (using scala -Xprint:typer):
scala> case class Bar(x: Int, y: Int)
...
<synthetic> object Bar extends scala.runtime.AbstractFunction2[Int,Int,Bar] with Serializable {
...
scala> case class Foo[T](x: T, y: T)
...
<synthetic> object Foo extends AnyRef with Serializable {
...
To wrap it up, when you have type parameters, you have to obtain a Function2 first
val f: Function2[Int, Int, Foo] = Foo[Int] _
and then you can call tupled on it
f.tupled // ((Int, Int)) => Foo[Int]

Make constructor params implicitly available in class body

I would like to make the explicit constructor parameters of my class implicitly available in the class body.
I can mark the second and subsequent parameters as "implicit val", which works:
scala> class Foo(x: Int, implicit val y: String) {
| println(implicitly[String])
| }
defined class Foo
scala> new Foo(1,"hello")
hello
but if I mark the first parameter as implicit, then Scala thinks I am marking the whole argument list as an implicit argument list, and adds an empty first argument list:
scala> class Bar(implicit val x: Int, y: String) {
| println(implicitly[Int])
| }
defined class Bar
scala> new Bar(1,"hello")
<console>:9: error: too many arguments for constructor Bar: ()(implicit x: Int, implicit y: String)Bar
new Bar(1,"hello")
^
Is there any way to make the first explicit constructor argument explicitly in scope?
You can make it implicitly available by using an implicit def inside:
class Foo(val x: Int, implicit val y: String) {
implicit def ix = x
}
which is hideously verbose, but it doesn't look like there is another way to get around the ambiguity between implicit marking a parameter list implicit and implicit val marking an implicit field.
I would have said, Add other local modifiers:
scala> class Foo(#deprecated("","") private implicit val x: Int, val y: String) { println(implicitly[Int]) }
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
defined class Foo
scala> new Foo(42, "hi")
<console>:9: error: too many arguments for constructor Foo: ()(implicit x: Int, y: String)Foo
new Foo(42, "hi")
^
Syntactically, that's different from the leading implicit keyword, so maybe it's a parser bug, or maybe it does distinguish an implicit param list from a leading param marked locally-implicit but doesn't use the difference.
class Foo(x: Int, y: String) {
implicit val xImp = x
println(implicitly[Int])
}
Actually I would do it similarly for the first case too. This is a situation where I think that the lot more understandable code is worth a bit more verbosity:
class Foo(x: Int, y: String) {
implicit val yImp = y
println(implicitly[String])
}

Is new required when calling a Scala auxiliary constructor?

When I run the following in a worksheet, everything is as expected.
case class P(x: Int, xs: Set[Int]) {
def this(x: Int) = this(x, Set[Int]())
}
P(1, Set()) //> res0: worksheet.P = P(1,Set())
new P(1) //> res0: worksheet.P = P(1,Set())
When I leave off the new I get a compiler diagnostic that says:
- not enough arguments for method apply: (x: Int, xs: Set[Int])worksheet.P in object P. Unspecified value parameter xs.
The same diagnostic appears in a regular .scala file.
Is this a compiler bug, or am I misunderstanding something?
Without new you're calling not constructor but factory method which is auto-generated by the compiler for case classes. You can define new one:
case class P(x: Int, xs: Set[Int])
object P {
def apply(x: Int): P = P(x, Set[Int]())
}
scala> P(1, Set())
res2: P = P(1,Set())
scala> P(1)
res3: P = P(1,Set())
Alternatively you can use define two constructors, but in your particular case I would go with default value:
case class P(x: Int, xs: Set[Int] = Set[Int]())
See also this answer which showcases similar situation and tour on case classes
Defining case class will also create factory method of class name, so you don't have to use new while creating an instance. So the following code works file:
P(1, Set())
Your class also have constructors, the following code will work fine, too:
new P(1, Set())
new P(1)
In the case of P(1), there's no such method, so the error occurs.

How can a parameter's default value reference another parameter?

How can a parameter's default value reference another parameter? If it cannot, how to work around that?
case class A(val x:Int, val y:Int = x*2)
Error (reasonably enough):
scala> case class B(val x:Int, val y:Int = x*2)
<console>:7: error: not found: value x
case class B(val x:Int, val y:Int = x*2)
^
This requires that you use multiple parameter lists:
case class A(x: Int)(y: Int = x*2)
Default values can only refer to parameters in preceding lists.
Be careful however with case classes, because their equality only takes into the account the first parameter list, therefore:
A(1)() == A(1)(3) // --> true!!
Since you asked for the work-around, if it's not obvious how to preserve caseness:
scala> :pa
// Entering paste mode (ctrl-D to finish)
case class Foo(x: Int, y: Int)
object Foo {
def apply(x: Int): Foo = apply(x, 2*x)
}
// Exiting paste mode, now interpreting.
defined class Foo
defined object Foo
scala> Foo(5,6)
res45: Foo = Foo(5,6)
scala> Foo(5)
res46: Foo = Foo(5,10)