Suppose that I want to write a case class Stepper as follows:
case class Stepper(step: Int) {def apply(x: Int) = x + step}
It comes with a nice toStringimplementation:
scala> Stepper(42).toString
res0: String = Stepper(42)
but it's not really a function:
scala> Some(2) map Stepper(2)
<console>:10: error: type mismatch;
found : Stepper
required: Int => ?
Some(2) map Stepper(2)
A workaround is to implement the Function trait...
case class Stepper(step: Int) extends (Int => Int) {def apply(x: Int) = x + step}
But then, I can't have for free a nice toString implementation anymore:
scala> Stepper(42).toString
res2: java.lang.String = <function1>
Then, the question is: can I have the best of these two worlds? Is there a solution where I have the nice toString implementation for free AND an implementation of trait Function. In other words, is there a way to apply the linearization in such a way that case class syntaxic sugar is applied at last?
The question is not really to do with linearisation. In case-classes toString is a method automatically generated by the compiler if and only if Any.toString is not overridden in the end-type.
However, the answer is partly to do with linearisation - we need to override Function1.toString with the method that would have been generated by compiler if not for the version introduced by Function1 :
trait ProperName extends Product {
override lazy val toString = scala.runtime.ScalaRunTime._toString(this)
}
// now just mix in ProperName and... magic!
case class Stepper(step: Int) extends (Int => Int) with ProperName {
def apply(x:Int) = x+step
}
Then
println(Some(2) map Stepper(2))
println(Stepper(2))
Will produce
Some(4)
Stepper(2)
Update
Here is a version of ProperName trait that doesn't rely on the undocumented API method:
trait ProperName extends Product {
override lazy val toString = {
val caseFields = {
val arity = productArity
def fields(from: Int): List[Any] =
if (from == arity) List()
else productElement(from) :: fields(from + 1)
fields(0)
}
caseFields.mkString(productPrefix + "(", ",", ")")
}
}
Alternative toString implementation is derived from the source code for the original _toString method scala.runtime.ScalaRunTime._toString.
Please note that this alternative implementation is still based on the assumption that a case class always extends Product trait. Although the latter holds true as of Scala 2.9.0 and is a fact that is known to and relied upon by some members of Scala community it's not formally documented as part of Scala Language Spec.
EDIT: What about overriding toString?
case class Stepper(step: Int) extends (Int => Int) {
def apply(x: Int) = x + step
override def toString = "Stepper(" + step + ")"
}
You can use an implicit conversion to have Stepper treated like a function only when necessary:
case class Stepper(step: Int) { def apply(x: Int) = x + step }
implicit def s2f(s: Stepper) = new Function[Int, Int] {
def apply(x: Int) = s.apply(x)
}
Now you get the case class's toString when you call Stepper(42).toString, but Some(2) map Stepper(2) also works as desired.
(Note that I've been more verbose than necessary above to keep the mechanics clear. You can also write implicit def s2f(s: Stepper) = s.apply _ or any number of other more concise formulations).
Related
I'm trying to build myself a tiny mocking library to deal with quite obvious cases:
Mock functions and record calls to them.
Mock trait implementations and be able to record calls on the methods implemented.
The first I achieved with the following implementation:
class LoggedFunction1[A1, B](body: A1 => B) extends Function1[A1, B] {
val calls = mutable.Buffer[A1]()
override def apply(v1: A1): B = {
calls += v1
body(v1)
}
}
object LoggedFunction {
def apply[A1, B](body: A1 => B) = new LoggedFunction1(body)
}
So far, so good.
I was now wondering if I could use this FunctionN extension somehow to implement the methods of a trait, such as:
trait A {
def m(i: Int)
}
class B extends A {
override def m(i: Int) = LoggedFunction((a: Int) => a)
}
Now that of course doesn't compile, because m's return value must be of type Int and not Function1[Int, Int].
I could declare a companion value to contain my information, such as:
class C extends A {
val _m = LoggedFunction((a: Int) => a)
override def m(i: Int) = _m(i)
}
But meh. What I really would like to do is:
class D extends A {
override val m = LoggedFunction((a: Int) => a)
}
In my mind the latest "should" somehow work, as I'm overriding a Function1[Int, Int] with a Function1[Int, Int], but it complains about me not overriding anything with m.
Is there any possibility I can override m using my LoggedFunction so I end up writing:
val test = new E
test.m(1)
test.m.calls should have size 1
If not: Why is it not possibly to override the method with a value containing a function?
In my mind the latest "should" somehow work, as I'm overriding a Function1[Int, Int] with a Function1[Int, Int]
Overriding a method with a function this way won't work, the two are really different things. See Functions vs methods in Scala.
In your precise use case, you could try the following:
override def m(i: Int) = LoggedFunction((a: Int) => a).apply(i)
Given the following classes:
case class AddRequest(x: Int, y: Int)
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String)
case class ToUppercaseResponse(upper: String)
How do I define in a typesafe manner some function:
def process(req: ???): ???
Such that the following should hold true:
val r1: AddResponse = process(AddRequest(2, 3))
val r2: ToUppercaseResponse = process(ToUppercaseRequest("aaa"))
Also, the following should not compile:
val r3 = process("somestring")
This is both entirely possible and a totally reasonable thing to do in Scala. This kind of thing is all over Shapeless, for example, and something similar (but less principled) is the basis of the magnet pattern that shows up in Spray, etc.
Update: note that the following solution assumes that "given the following classes" means you don't want to touch the case classes themselves. If you don't care, see the second part of the answer below.
You'd want a type class that maps input types to output types:
case class AddRequest(x: Int, y: Int)
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String)
case class ToUppercaseResponse(upper: String)
trait Processable[In] {
type Out
def apply(in: In): Out
}
And then some type class instances:
object Processable {
type Aux[I, O] = Processable[I] { type Out = O }
implicit val toUppercase: Aux[ToUppercaseRequest, ToUppercaseResponse] =
new Processable[ToUppercaseRequest] {
type Out = ToUppercaseResponse
def apply(in: ToUppercaseRequest): ToUppercaseResponse =
ToUppercaseResponse(in.str.toUpperCase)
}
implicit val add: Aux[AddRequest, AddResponse] =
new Processable[AddRequest] {
type Out = AddResponse
def apply(in: AddRequest): AddResponse = AddResponse(in.x + in.y)
}
}
And now you can define process using this type class:
def process[I](in: I)(implicit p: Processable[I]): p.Out = p(in)
Which works as desired (note the appropriate static types):
scala> val res: ToUppercaseResponse = process(ToUppercaseRequest("foo"))
res: ToUppercaseResponse = ToUppercaseResponse(FOO)
scala> val res: AddResponse = process(AddRequest(0, 1))
res: AddResponse = AddResponse(1)
But it doesn't work on arbitrary types:
scala> process("whatever")
<console>:14: error: could not find implicit value for parameter p: Processable[String]
process("whatever")
^
You don't even have to use a path dependent type (you should be able just to have two type parameters on the type class), but it makes using process a little nicer if e.g. you have to provide the type parameter explicitly.
Update: everything above assumes that you don't want to change your case class signatures (which definitely isn't necessary). If you are willing to change them, though, you can do this a little more concisely:
trait Input[Out] {
def computed: Out
}
case class AddRequest(x: Int, y: Int) extends Input[AddResponse] {
def computed: AddResponse = AddResponse(x + y)
}
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String) extends Input[ToUppercaseResponse] {
def computed: ToUppercaseResponse = ToUppercaseResponse(str.toUpperCase)
}
case class ToUppercaseResponse(upper: String)
def process[O](in: Input[O]): O = in.computed
And then:
scala> process(AddRequest(0, 1))
res9: AddResponse = AddResponse(1)
scala> process(ToUppercaseRequest("foo"))
res10: ToUppercaseResponse = ToUppercaseResponse(FOO)
Which kind of polymorphism (parametric or ad-hoc) you should prefer is entirely up to you. If you want to be able to describe a mapping between arbitrary types, use a type class. If you don't care, or actively don't want this operation to be available for arbitrary types, using subtyping.
You can define a common trait for Requests, and a common trait for Responses where the request type is defined for specific response type:
trait Request[R <: Response]
trait Response
case class AddRequest(x: Int, y: Int) extends Request[AddResponse]
case class AddResponse(sum: Int) extends Response
case class ToUppercaseRequest(str: String) extends Request[ToUppercaseResponse]
case class ToUppercaseResponse(upper: String) extends Response Response[ToUppercaseRequest]
Then, process signature would be:
def process[A <: Request[B], B <: Response](req: A): B
When you call process, you'll have to explicitly define the types so that the returned type is what you expect it to be - it can't be inferred specifically enough:
val r1: AddResponse = process[AddRequest, AddResponse](AddRequest(2, 3))
val r2: ToUppercaseResponse = process[ToUppercaseRequest, ToUppercaseResponse](ToUppercaseRequest("aaa"))
Scala case class has a default toString function. But when this case class extends a trait with an existing toString() function, it will be rendered useless. How can I prevent this situation?
OK here is the easist answer:
override def toString = ScalaRunTime._toString(this)
end of story:)
Here's a workaround I think may work, it may be too much ceremony, you decide. It involves a trait.
trait StandardToString { this:Product =>
override def toString = productPrefix + productIterator.mkString("(", ",", ")")
}
Now trying it with some samples:
trait Human {
override def toString() = "Human"
}
case class European(firstName:String) extends Human
and running it with the trait:
scala> new European("Falco") with StandardToString
res0: European with StandardToString = European(Falco)
of course with the trait you are left with
scala> new European("Adele")
res1: European = Human
It's more precise to say that the case class toString is not generated, rather than that it is overridden.
This isn't much of an answer or workaround.
scala> trait X { override def toString = "X" }
defined trait X
scala> case class Y(i: Int) extends X
defined class Y
scala> Y(42)
res0: Y = X
scala> case class Y(i: Int)
defined class Y
scala> class Z(x: Int) extends Y(x) with X { override def toString = super[Y].toString }
defined class Z
scala> new Z(42)
res1: Z = Y(42)
You can't do that with a trait:
scala> trait R extends Y { override def toString = super[Y].toString }
<console>:9: error: Implementation restriction: traits may not select fields or methods from super[C] where C is a class
trait R extends Y { override def toString = super[Y].toString }
^
I was under the impression that this
// short syntax
def foo(bar: Bar)(baz: Baz): Quux
was syntax sugar for this
// long syntax
def foo(bar: Bar): (Baz) => Quux
But I cannot seem to mix the two when it comes to inheritance. The whole tree has to be defined in either the short syntax or the long syntax; never both.
For example:
case class Context
case class Work
trait ContextualWorker {
def workWithContext(ctxt: Context)(work: Work): Traversable[Work]
}
class ShortConcreteWorker extends ContextualWorker {
override def workWithContext(ctxt: Context)(work: Work) = Nil
}
class LongConcreteWorker extends ContextualWorker {
// error on next line: method workWithContext overrides nothing <-------------
override def workWithContext(ctxt: Context): (Work) => Traversable[Work] = {
val setupCode = 1
{ work => Nil }
}
}
If I change the trait to use the long syntax, then ShortConcreteWorker doesn't compile.
Is there a reason why these aren't interchangeable/inheritable? How have you gotten around it?
Right now the most flexible approach appears to be to define the tree in the long syntax, perhaps delegating to an implementation class in ShortConcreteWorker like so:
case class Context
case class Work
trait ContextualWorker {
def workWithContext(ctxt: Context): (Work) => Traversable[Work]
}
class ShortConcreteWorker extends ContextualWorker {
override def workWithContext(ctxt: Context) = workWithContextImpl(ctxt)_
private def workWithContextImpl(ctxt: Context)(work: Work) = Nil
}
class LongConcreteWorker extends ContextualWorker {
override def workWithContext(ctxt: Context): (Work) => Traversable[Work] = {
val setupCode = 1
{ work => Nil }
}
}
The two methods described quite simply have different signatures. The REPL confirms this:
scala> def foo1(a: Int)(b: Int): Int = a + b
foo1: (a: Int)(b: Int)Int
scala> def foo2(a: Int): (Int => Int) = (b: Int) => a + b
foo2: (a: Int)Int => Int
The first is a function that requires two arguments, given in separate argument lists, and returns an Int. The second is a function that takes one argument and returns a function from Int to Int. While these two things are conceptually similar, they are, in fact, different constructs, and Scala treats them as such.
This is not limited to functions with multiple argument lists. It works the same way here:
scala> def foo3(a: Int): Int = a + 1
foo3: (a: Int)Int
scala> def foo4: (Int => Int) = (a: Int) => a + 1
foo4: Int => Int
Note that there are different ramifications for usage as well. With foo2, because it only accepts one argument, we can call it with just one argument. However, foo1 requires two arguments, an so we cannot simply call it with one. You can however use the _ syntax to convert it into a callable function.
foo2(2) // Int => Int = <function1>
foo1(2) // error: missing arguments for method foo1
foo1(2) _ // Int => Int = <function1>
So to answer your question directly: The reason they are not interchangeable is because they are not the same. If they were the same, we would be able to call them the same way. If you could change the signature upon extension, how would Scala know which calling syntax to allow? The way to "get around" this is to simply make the signatures consistent.
I want to do this:
abstract class Context {
def getInt(id: Int): Int
}
abstract class Dependency[+T]
(val name: String, val id: Int)
extends Function1[Context,T]
class IntDependency(name: String, id: Int)
extends Dependency[Int](name, id) {
def apply(implicit context: Context): Int =
context.getInt(id)
}
But then I get an error message like this:
class IntDependency needs to be abstract, since method apply in trait
Function1 of type (v1: Context)Long is not defined (Note that T1 does
not match Context)
I understand that implicits should normally be part of the second parameter list, but I can't work out how to code it so it compiles, and gives the result I want.
Explanation: I'm trying to create a framework where one can define "Function" object, which can depend on other functions to compute their value. All functions should only take a single Context parameter. The context know the "result" of the other functions. The function instances should be immutable, with the state residing in the context. I want the functions to create "dependency" fields at creation time, which take the context implicitly, and return the value of the dependency within that context, so that accessing the dependency inside of the apply method "feels like" accessing a parameter or field, that is without explicitly giving the context as parameter to the dependency.
Are you sure you need your Dependency to extend a Function? Because if you don't, just leave the extends Function1[Context,T] part out and your code will work.
If you really need to extend a Function than I don't know of a solution in your case. But there are cases where you could try to overload the apply method. Like here:
scala> val sum = new Function1[Int, Function1[Int, Int]] {
| def apply(a: Int) = (b: Int) => a + b
| def apply(a: Int)(implicit b: Int) = a + b
|}
sum: java.lang.Object with (Int) => (Int) => Int{def apply(a:Int)(implicit b: Int): Int} = <function1>
scala> sum(2)(3)
res0: Int = 5
scala> implicit val b = 10
b: Int = 10
scala> sum(2)
res1: Int = 12
A method may have its final parameter section marked implicit; it need not be the second section, although that is most commonly seen.
But it seems that when a subclass marks a parameter section implicit, it is no longer considered to override the method in the superclass.
scala> new (Int => Int) { def apply(implicit i: Int) = i }
<console>:8: error: object creation impossible, since method apply in trait Function1 of type (v1: Int)Int is not defined
(Note that T1 does not match Int)
new (Int => Int) { def apply(implicit i: Int) = i }
^
scala> trait F1 { def f(a: Any) }; new F1 { def f(implicit a: Any) = () }
<console>:8: error: object creation impossible, since method f in trait F1 of type (a: Any)Unit is not defined
trait F1 { def f(a: Any) }; new F1 { def f(implicit a: Any) = () }
^
The spec does not specifically mention this (§5.1.4 Overriding), so it may be an implementation restriction, or an bug.
Its sure, that your apply method signature with implicit doesn´t conform with the signature of Function1.apply.
Hopefully I get your problem right, so what about (assuming that your context is mutable and perhaps singleton) having the implicit context injected at creation time? Is that possible in your case?
class IntDependency(id: Int)(implicit context: Context) extends Dependency[Int](id)
But then I wonder (and still was wondering before) what to do with the context argument at the apply method.
Here is the working solution:
abstract class Context {
def getInt(id: Int): Int
}
abstract class Dependency[+T]
(val name: String, val id: Int) {
def get(context: Context): T
}
class IntDependency(name: String, id: Int)
extends Dependency[Int](name, id) {
def get(context: Context): Int =
context.getInt(id)
}
implicit def intDep2Int(dep: IntDependency)
(implicit context: Context): Int =
dep.get(context)