Approaches to testing that a method is not available on a type - testing

Given a type hierarchy for a game which strongly distinguishes whose turn is next:
trait Game
trait BlackToPlay extends Game {
def move(p: BlackPiece, s: Square): Either[FinishedGame, WhiteToPlay]
}
trait WhiteToPlay extends Game {
def move(p: WhitePiece, s: Square): Either[FinishedGame, BlackToPlay]
}
Can I make the following, important assertion without resorting to reflection?
"A game with white to play" should {
"not allow black to play" in {
// an instance of whiteToPlay should not
// have the `move(BlackPiece, Square)` method.
}
}
EDIT: My attempt to implement #Martin's solution doesn't work. Any thoughts on what's wrong here? From the REPL:
scala> class B() {
| def b(s: String) = s
| }
defined class B
scala> val b = new B()
b: B = B#420e44
scala> b.c("")
<console>:8: error: value c is not a member of B
b.c("")
^
scala> b match {
| case _: { def c(s: String) } => false
| case _ => true
| }
warning: there were unchecked warnings; re-run with -unchecked for details
res7: Boolean = false
res7 should have been true, because b should not match on the structural type of { def c(s: String) }

You don't test what the type system already guarantees. In fact, the type system is already a test of certain properties of your program.
You could further on to test that the types you have guarantee a certain property (like no player making a move twice in a row), but this kind of thing is restricted to languages like Agda and Coq, for now.

Assuming BlackPiece is not a subtype of WhitePiece:
WhiteToPlayInstance.move(BlackPiece, s) should not compile - which means you can't write a test for it. The type system ensures that you can't move a BlackPiece on a WhiteToPlay.

EDIT: As Thomas pointed out, the below answer is nonsense since structural types cannot be used in pattern matches in the JVM version of scala.
Under normal cicumstances this doesnt make much sense because scala is statically typed and things like that are taken care of by the compiler but if you do make massive use of reflection or structural typing in your code it might be a good test:
instance match {
case x: { def move(p: BlackPiece, s: Square): Either[FinishedGame, WhiteToPlay] } => // error
case _ => // no error
}

If you really want to test for such things, move the check from type checking to something dynamic. Assume that WhitePiece and BlackPiece share a common supertype Piece:
trait Game {
def move(p : Piece, s : Square) : Either[FinishedGame, WhiteToPlay]
}
trait BlackToPlay extends Game
trait WhiteToPlay extends Game
Then a test could look like this:
val b2p : BlackToPlay = ...
val bp : BlackPiece = ...
val wp : WhitePiece = ...
{a move bp} must not produce [IllegalMoveException]
{a move wp} must produce [IllegalMoveException]
I am not sure this would be good design, but it makes your system explicitly testable.

I know you didn't want a reflection solution, but you could (if scala 2.9 is acceptable) use the new Dynamic trait like this:
class ReflectionDynamic[T <: AnyRef](t: T) extends Dynamic {
def typed[A]: A = sys.error("doh");
def applyDynamic(name: String)(args: Any*) = {
val argRefs = args.map {
case a: AnyRef => a
case _ => sys.error("only AnyRefs")
}
t.getClass.getMethod(name, argRefs.map(_.getClass): _*).invoke(t, argRefs: _*)
}
}
... and that will give this positive test:
val dynamicWhiteToPlay = new ReflectionDynamic(whiteToPlay)
dynamicWhiteToPlay.move(new WhitePiece, new Square) must_== Right(blackToPlay)
... and this for negative:
dynamicWhiteToPlay.move(new BlackPiece, new Square) must throwA[NoSuchMethodException]

The question is akin to asking: Given val f: (Boolean) => Int, how can I test that f("hello world") is rejected by the compiler?
After some brief conversation at the Melbourne Scala User Group my question was validated (yay). After all, the restriction I'm trying to test for is included by design and therefore deserves a test.
Bernie Pope suggested that the mechanism required is Automated Theorem Proving. #daniel-c-sobral was kind enough to mention Agda and Coq in a slightly different context and indeed these are ATP technologies which could prove my application to be correct.
Another suggestion was to execute the offending code as a script and assert that it fails. A poor-mans eval, if you like.

Related

Type Class for Complex Numbers

I need to implement a custom type class for Complex DSP and Complex Ring operations. I'm aware about DspTools project, but purposely want to exclude it from consideration.
I've got a hardware module, which I want to instantiate with diff type classes: UInt, SInt, FixedPoint, Real and Complex(FixedPoint, FixedPoint).
Here's my minimal class:
class Complex[A <: Data, B <: Data] (val re:A, val im:B) extends Bundle {
override def cloneType: this.type = new Complex(re, im).asInstanceOf[this.type]
}
object Complex {
def apply[A <: Data, B <: Data](re:A, im:B) = new Complex(re, im)
implicit def UInt2Complex(re:UInt, im:UInt) = Complex(re,im)
}
When I instantiate this with different data types from Chisel3.Core, the code compiles and works.
However, when I try to do :
import chisel3.core.{FixedPoint => FP}
...
val inType = Complex ( FP(20.W,10.BP), FP(20.W,10.BP))
val outType = Complex ( FP(20.W,10.BP), FP(20.W,10.BP))
...
I get the code compiled, but FIRRTL emits an error:
[info] using --backend-name verilator
[info] chisel3.core.Binding$RebindingException: Attempted reassignment of binding to chisel3.core.FixedPoint#d
[info] at chisel3.core.Data.binding_$eq(Data.scala:250)
What's wrong with this? How to fix the issue?
The issue is that Chisel needs fresh clones of any Data when it recursively calls cloneType, and you're simply passing re and im to the Complex constructor resulting in the exact same objects. Put a little more concretely:
val a = Complex(UInt(8.W), UInt(8.W))
val b = a.cloneType
a.re eq b.re // This will be true and it *must* not be
This is kind of an age old problem that we don't have a great solution to, in your case, you should call .cloneType on re and im in Complex.cloneType
I know you aren't using DSPTools, but it can still provide a reference and they do that: https://github.com/ucb-bar/dsptools/blob/fe8f9d08987f3a403f6281ba4face1c26b627b71/src/main/scala/dsptools/numbers/chisel_concrete/DspComplex.scala#L75
Here's the actual minimalistic implementation, which worked for me. #jkoenig pointed me to the right direction. Thanks a lot!
class Complex[A <: Data, B <: Data] (val re:A, val im:B) extends Bundle {
override def cloneType: this.type = new Complex(re.cloneType, im.cloneType).asInstanceOf[this.type]
}
object Complex {
def apply[A <: Data, B <: Data](re:A, im:B):Complex[A,B] = {
val real = re.cloneType
val imag = im.cloneType
new Complex(real,imag)
}
}

Can you concatenate statements at run-time in Kotlin?

I am trying to interface with TeamCity using Kotlin-DSL
In this section of the TC Kotlin guide there is a rather odd looking part where it seems like it causes statements to become concatenated on the fly.
It first defines these:
val linux = Requirements() {
contains("os.name", "linux")
}
val oracle = Requirements() {
equals("db.name", "oracle")
}
val java6 = Requirements() {
contains("env.JAVA_HOME", "1.6")
}
Then does this with those definitions:
buildType {
...
requirements(linux + oracle + java6)
...
}
I know that the above section of code is equivalent to
buildType {
...
requirements {
contains("os.name", "linux")
equals("db.name", "oracle")
contains("env.JAVA_HOME", "1.6")
}
...
}
So I suppose what my question boils down to is what is the return type of the 'Requirements' function that can just be concatenated together? My guess is it is some sort of statement/ function wrapper and Kotlin lets you concatenate these as you go, and the function signature looks like this:
fun Requirements(init: (a: String, b: String) -> UnknownTypeA) : UnknownTypeB
EDIT:
For anyone who is confused when reading this in the future, the calls to Requirements are actually an object initialisation via the Requirements constructor. I do inevitably feel embarrassed for not picking up on this (The casing of the name should have been hint enough!) but I'm making this edit to make it clear to people that it is not a function. Thank you to Hotkey for pointing that out.
First, note that Requirements accepts a function into its constructor. Without knowing what is the type of that function, let's assume it's Context.() -> Unit (a function with receiver of Context, accepting no arguments and returning Unit).
Now, we can naturally overload the plus operator for the Requirements type, so that it returns another Requirements instance that has a function that applies both functions from the operands.
You could do that in your own code in the following way:
class Requirements(val check: Context.() -> Unit)
operator fun Requirements.plus(other: Requirements) =
Requirements { check(); other.check() }

Example of when should we use run, let, apply, also and with on Kotlin

I wish to have a good example for each function run, let, apply, also, with
I have read this article but still lack of an example
All these functions are used for switching the scope of the current function / the variable. They are used to keep things that belong together in one place (mostly initializations).
Here are some examples:
run - returns anything you want and re-scopes the variable it's used on to this
val password: Password = PasswordGenerator().run {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
generate()
}
The password generator is now rescoped as this and we can therefore set seed, hash and hashRepetitions without using a variable.
generate() will return an instance of Password.
apply is similar, but it will return this:
val generator = PasswordGenerator().apply {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
}
val pasword = generator.generate()
That's particularly useful as a replacement for the Builder pattern, and if you want to re-use certain configurations.
let - mostly used to avoid null checks, but can also be used as a replacement for run. The difference is, that this will still be the same as before and you access the re-scoped variable using it:
val fruitBasket = ...
apple?.let {
println("adding a ${it.color} apple!")
fruitBasket.add(it)
}
The code above will add the apple to the basket only if it's not null. Also notice that it is now not optional anymore so you won't run into a NullPointerException here (aka. you don't need to use ?. to access its attributes)
also - use it when you want to use apply, but don't want to shadow this
class FruitBasket {
private var weight = 0
fun addFrom(appleTree: AppleTree) {
val apple = appleTree.pick().also { apple ->
this.weight += apple.weight
add(apple)
}
...
}
...
fun add(fruit: Fruit) = ...
}
Using apply here would shadow this, so that this.weight would refer to the apple, and not to the fruit basket.
Note: I shamelessly took the examples from my blog
There are a few more articles like here, and here that are worth to take a look.
I think it is down to when you need a shorter, more concise within a few lines, and to avoid branching or conditional statement checking (such as if not null, then do this).
I love this simple chart, so I linked it here. You can see it from this as written by Sebastiano Gottardo.
Please also look at the chart accompanying my explanation below.
Concept
I think it as a role playing way inside your code block when you call those functions + whether you want yourself back (to chain call functions, or set to result variable, etc).
Above is what I think.
Concept Example
Let's see examples for all of them here
1.) myComputer.apply { } means you want to act as a main actor (you want to think that you're computer), and you want yourself back (computer) so you can do
var crashedComputer = myComputer.apply {
// you're the computer, you yourself install the apps
// note: installFancyApps is one of methods of computer
installFancyApps()
}.crash()
Yup, you yourself just install the apps, crash yourself, and saved yourself as reference to allow others to see and do something with it.
2.) myComputer.also {} means you're completely sure you aren't computer, you're outsider that wants to do something with it, and also wants it computer as a returned result.
var crashedComputer = myComputer.also {
// now your grandpa does something with it
myGrandpa.installVirusOn(it)
}.crash()
3.) with(myComputer) { } means you're main actor (computer), and you don't want yourself as a result back.
with(myComputer) {
// you're the computer, you yourself install the apps
installFancyApps()
}
4.) myComputer.run { } means you're main actor (computer), and you don't want yourself as a result back.
myComputer.run {
// you're the computer, you yourself install the apps
installFancyApps()
}
but it's different from with { } in a very subtle sense that you can chain call run { } like the following
myComputer.run {
installFancyApps()
}.run {
// computer object isn't passed through here. So you cannot call installFancyApps() here again.
println("woop!")
}
This is due to run {} is extension function, but with { } is not. So you call run { } and this inside the code block will be reflected to the caller type of object. You can see this for an excellent explanation for the difference between run {} and with {}.
5.) myComputer.let { } means you're outsider that looks at the computer, and want to do something about it without any care for computer instance to be returned back to you again.
myComputer.let {
myGrandpa.installVirusOn(it)
}
The Way to Look At It
I tend to look at also and let as something which is external, outside. Whenever you say these two words, it's like you try to act up on something. let install virus on this computer, and also crash it. So this nails down the part of whether you're an actor or not.
For the result part, it's clearly there. also expresses that it's also another thing, so you still retain the availability of object itself. Thus it returns it as a result.
Everything else associates with this. Additionally run/with clearly doesn't interest in return object-self back. Now you can differentiate all of them.
I think sometimes when we step away from 100% programming/logic-based of examples, then we are in better position to conceptualize things. But that depends right :)
There are 6 different scoping functions:
T.run
T.let
T.apply
T.also
with
run
I prepared a visual note as the below to show the differences :
data class Citizen(var name: String, var age: Int, var residence: String)
Decision depends on your needs. The use cases of different functions overlap, so that you can choose the functions based on the specific conventions used in your project or team.
Although the scope functions are a way of making the code more concise, avoid overusing them: it can decrease your code readability and lead to errors. Avoid nesting scope functions and be careful when chaining them: it's easy to get confused about the current context object and the value of this or it.
Here is another diagram for deciding which one to use from https://medium.com/#elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84
Some conventions are as the following :
Use also for additional actions that don't alter the object, such as logging or printing debug information.
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
The common case for apply is the object configuration.
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
If you need shadowing, use run
fun test() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
If you need to return receiver object itself, use apply or also
let, also, apply, takeIf, takeUnless are extension functions in Kotlin.
To understand these function you have to understand Extension functions and Lambda functions in Kotlin.
Extension Function:
By the use of extension function, we can create a function for a class without inheriting a class.
Kotlin, similar to C# and Gosu, provides the ability to extend a class
with new functionality without having to inherit from the class or use
any type of design pattern such as Decorator. This is done via special
declarations called extensions. Kotlin supports extension functions
and extension properties.
So, to find if only numbers in the String, you can create a method like below without inheriting String class.
fun String.isNumber(): Boolean = this.matches("[0-9]+".toRegex())
you can use the above extension function like this,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber)
which is prints true.
Lambda Functions:
Lambda functions are just like Interface in Java. But in Kotlin, lambda functions can be passed as a parameter in functions.
Example:
fun String.isNumber(block: () -> Unit): Boolean {
return if (this.matches("[0-9]+".toRegex())) {
block()
true
} else false
}
You can see, the block is a lambda function and it is passed as a parameter. You can use the above function like this,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber {
println("Block executed")
})
The above function will print like this,
Block executed
true
I hope, now you got an idea about Extension functions and Lambda functions. Now we can go to Extension functions one by one.
let
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
Two Types T and R used in the above function.
T.let
T could be any object like String class. so you can invoke this function with any objects.
block: (T) -> R
In parameter of let, you can see the above lambda function. Also, the invoking object is passed as a parameter of the function. So you can use the invoking class object inside the function. then it returns the R (another object).
Example:
val phoneNumber = "8899665544"
val numberAndCount: Pair<Int, Int> = phoneNumber.let { it.toInt() to it.count() }
In above example let takes String as a parameter of its lambda function and it returns Pair in return.
In the same way, other extension function works.
also
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
extension function also takes the invoking class as a lambda function parameter and returns nothing.
Example:
val phoneNumber = "8899665544"
phoneNumber.also { number ->
println(number.contains("8"))
println(number.length)
}
apply
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Same as also but the same invoking object passed as the function so you can use the functions and other properties without calling it or parameter name.
Example:
val phoneNumber = "8899665544"
phoneNumber.apply {
println(contains("8"))
println(length)
}
You can see in the above example the functions of String class directly invoked inside the lambda funtion.
takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
Example:
val phoneNumber = "8899665544"
val number = phoneNumber.takeIf { it.matches("[0-9]+".toRegex()) }
In above example number will have a string of phoneNumber only it matches the regex. Otherwise, it will be null.
takeUnless
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
It is the reverse of takeIf.
Example:
val phoneNumber = "8899665544"
val number = phoneNumber.takeUnless { it.matches("[0-9]+".toRegex()) }
number will have a string of phoneNumber only if not matches the regex. Otherwise, it will be null.
You can view similar answers which is usefull here difference between kotlin also, apply, let, use, takeIf and takeUnless in Kotlin
According to my experience, since such functions are inline syntactic sugar with no performance difference, you should always choose the one that requires writing the least amount of code in the lamda.
To do this, first determine whether you want the lambda to return its result (choose run/let) or the object itself (choose apply/also); then in most cases when the lambda is a single expression, choose the ones with the same block function type as that expression, because when it's a receiver expression, this can be omitted, when it's a parameter expression, it is shorter than this:
val a: Type = ...
fun Type.receiverFunction(...): ReturnType { ... }
a.run/*apply*/ { receiverFunction(...) } // shorter because "this" can be omitted
a.let/*also*/ { it.receiverFunction(...) } // longer
fun parameterFunction(parameter: Type, ...): ReturnType { ... }
a.run/*apply*/ { parameterFunction(this, ...) } // longer
a.let/*also*/ { parameterFunction(it, ...) } // shorter because "it" is shorter than "this"
However, when the lambda consists of a mix of them, it's up to you then to choose the one that fits better into the context or you feel more comfortable with.
Also, use the ones with parameter block function when deconstruction is needed:
val pair: Pair<TypeA, TypeB> = ...
pair.run/*apply*/ {
val (first, second) = this
...
} // longer
pair.let/*also*/ { (first, second) -> ... } // shorter
Here is a brief comparison among all these functions from JetBrains's official Kotlin course on Coursera Kotlin for Java Developers:
I must admit that the difference is not so obvious at first glance, among other things because these 5 functions are often interchangeable. Here is my understanding :
APPLY -> Initialize an object with theses properties and wait for the object
val paint = Paint().apply {
this.style = Paint.Style.FILL
this.color = Color.WHITE
}
LET -> Isolate a piece of code and wait for the result
val result = let {
val b = 3
val c = 2
b + c
}
or
val a = 1
val result = a.let {
val b = 3
val c = 2
it + b + c
}
or
val paint: Paint? = Paint()
paint?.let {
// here, paint is always NOT NULL
// paint is "Paint", not "Paint?"
}
ALSO -> Execute 2 operations at the same time and wait for the result
var a = 1
var b = 3
a = b.also { b = a }
WITH -> Do something with this variable/object and don't wait for a result (chaining NOT allowed )
with(canvas) {
this.draw(x)
this.draw(y)
}
RUN -> Do something with this variable/object and don't wait for a result (chaining allowed)
canvas.run {
this.draw(x)
this.draw(y)
}
or
canvas.run {this.draw(x)}.run {this.draw(x)}

Scala class inheritance

Tagged as homework.
I'm having trouble in the object oriented world while trying to implement a class.
I'm implenting various functions to take action on lists, that I'm using to mock a set.
I'm not too worried about my logic on how to find union, for example, but really just the structure.
For eg:
abstract class parentSet[T] protected () {
def union(other:parentSet[T]):parentSet[T]
}
Now I want a new class extending parentSet:
class childSet[T] private (l: List[T]) extends parentSet[T] {
def this() = this(List())
private val elems = l
val toList = List[T] => new List(l)
def union(other:parentSet[T]):childSet[T] = {
for (i <- this.toList) {
if (other contains i) {}
else {l :: i}
}
return l
}
}
Upon compiling, I receive errors such that type childSet isn't found in def union, nor is type T to keep it parametric. Also, I assume my toList isn't correct as it complains that it isn't a member of the object; to name a few.
Where in my syntax am I wrong?
EDIT
Now I've got that figured out:
def U(other:parentSet[T]):childSet[T] = {
var w = other.toList
for (i <- this.toList) {
if (!(other contains i)) {w = i::w}
}
return new childSet(w)
}
Now, I'm trying to do the same operations with map, and this is what I'm working on/with:
def U(other:parentSet[T]):MapSet[T] = {
var a = Map[T,Unit]
for (i <- this.toList) {
if (!(other contains i)) {a = a + (i->())}
}
return new MapSet(elems + (a->()))
}
I still want to use toList to make it easily traversable, but I'm still getting type errors while messing with maps..
This code has a few problems:
It seems that you are not realizing that List[T] is an immutable type, meaning you cannot change its value once created. So if you have a List[T] and you call the :: method to prepend a value, the function returns a new list and leaves your existing one unchanged. Scala has mutable collections such as ListBuffer which may behave more like you expect. So when you return l, you're actually returning the original list.
Also, you have the order wrong in using ::. It should go i :: l, since :: is a right-binding function (because it ends with a :).
Lastly, in your union method you are doing (other contains i). Maybe it's just the Scala syntax that's confusing you, but this is the same as doing (other.contains(i)) and clearly contains is not a defined method of parentSet. It is a method on the List[T] type, but you're not calling contains on a list.
You tagged this as homework so I'm not going to fix your code, but I think you should
Look at some examples of correct Scala code involving lists, try here for starters
Play around in the Scala REPL and try creating and working with some lists, so you get a feel for how immutable collections work.
To answer your direct question, even though childSet is inheriting parentSet the original method specify parentSet as the return type and not childSet. You can either only use parentSet as the type OR you could specify the return type to be anything that inherits parentSet.

Why is method overloading not defined for different return types?

In Scala, you can overload a method by having methods that share a common name, but which either have different arities or different parameter types. I was wondering why this wasn't also extended to the return type of a method? Consider the following code:
class C {
def m: Int = 42
def m: String = "forty two"
}
val c = new C
val i: Int = C.m
val s: String = C.m
Is there a reason why this shouldn't work?
Thank you,
Vincent.
Actually, you can make it work by the magic of 'implicit'. As following:
scala> case class Result(i: Int,s: String)
scala> class C {
| def m: Result = Result(42,"forty two")
| }
scala> implicit def res2int(res: Result) = res.i
scala> implicit def res2str(res: Result) = res.s
scala> val c = new C
scala> val i: Int = c.m
i: Int = 42
scala> val s: String = c.m
s: String = forty two
scala>
You can of course have overloading for methods which differ by return type, just not for methods which differ only by return type. For example, this is fine:
def foo(s: String) : String = s + "Hello"
def foo(i: Int) : Int = i + 1
That aside, the answer to your question is evidently that it was a design decision: the return type is part of the method signature as anyone who has experienced an AbstractMethodError can tell you.
Consider however how allowing such overloading might work in tandem with sub-typing:
class A {
def foo: Int = 1
}
val a: A = //...lookup an A
val b = a.foo
This is perfectly valid code of course and javac would uniquely resolve the method call. But what if I subclass A as follows:
class B extends A {
def foo: String = "Hello"
}
This causes the original code's resolution of which method is being called to be broken. What should b be? I have logically broken some existing code by subtyping some existing class, even though I have not changed either that code or that class.
The main reason is complexity issues: with a "normal" compiler approach, you go inside-out (from the inner expression to the outer scope), building your binary step by step; if you add return-type-only differentiation, you need to change to a backtracking approach, which greatly increases compile time, compiler complexity (= bugs!).
Also, if you return a subtype or a type that can be automatically converted to the other, which method should you choose? You'd give ambiguity errors for perfectly valid code.
Not worth the trouble.
All in all, you can easily refactor your code to avoid return-type-only overload, for example by adding a dummy parameter of the type you want to return.
I've never used scala, so someone whack me on the head if I'm wrong here, but this is my take.
Say you have two methods whose signatures differ only by return type.
If you're calling that method, how does the compiler (interpreter?) know which method you actually want to be calling?
I'm sure in some situations it might be able to figure it out, but what if, for example, one of your return types is a subclass of the other? It's not always easy.
Java doesn't allow overloading of return types, and since scala is built on the java JVM, it's probably just a java limitation.
(Edit)
Note that Covariant returns are a different issue. When overriding a method, you can choose to return a subclass of the class you're supposed to be returning, but cannot choose an unrelated class to return.
In order to differentiate between different function with the same name and argument types, but different return types, some syntax is required, or analysis of the site of an expression.
Scala is an expression oriented language (every statement is an expression). Generally expression oriented languages prefer to have the semantics of expressions to be dependent only on the scope evaluation occurs in, not what happens to the result, so for the expression foo() in i_take_an_int( foo() ) and i_take_any_type ( foo()) and foo() as a statement all call the same version of foo().
There's also the issue that adding overloading by return type to a language with type inference will make the code completely incomprehensible - you'd have to keep an incredible amount of the system in mind in order to predict what will happen when code gets executed.
All answers that say the JVM does not allow this are straight up wrong. You can overload based on return type. Surprisingly, the JVM does allow this; it's the compilers for languages that run on the JVM that don't allow this. But there are ways to get around compiler limitations in Scala.
For example, consider the following snippet of code:
object Overload{
def foo(xs: String*) = "foo"
def foo(xs: Int*) = "bar"
}
This will throw a compiler error (Because varargs, indicated by the * after the argument type, type erase to Seq):
Error:(217, 11) double definition:
def foo(xs: String*): String at line 216 and
def foo(xs: Any*): String at line 217
have same type after erasure: (xs: Seq)String
def foo(xs: Any*) = "bar";
However, if you change value of the second foo to 3 instead of bar (that way changing the return type from String to Int) as follows:
object Overload{
def foo(xs: String*) = "foo"
def foo(xs: Int*) = 3
}
... you won't get a compiler error.
So you can do something like this:
val x: String = Overload.foo()
val y: Int = Overload.foo()
println(x)
println(y)
And it will print out:
3
foo
However, the caveat to this method is having to add varargs as the last (or only) argument for the overloaded functions, each with with their own distinct type.
Source: http://www.drmaciver.com/2008/08/a-curious-fact-about-overloading-in-scala/