I am learning Scala right now. I see that specifying type while assigning to new val is not necessary. But then consider the following code:
object MyObject {
def firstResponse(r: Array[String]): String = r(0)
def mostFrequent(r: Array[String]): String = {
(r groupBy identity mapValues (_.length) maxBy(_._2))._1
}
def mostFrequent(r: Array[String], among: Int): String = { mostFrequent(r take among) }
// throws compile error
val heuristics = Array(
firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)
}
If I change the last line and specify the type explicitly, then the error is gone
val heuristics: Array[Array[String] => String] = Array(
firstResponse, mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)
What's wrong here?
Edit: As #mdm correctly pointed out,
//This works
val heuristics = Array(firstResponse(_), firstResponse(_))
//This does not work
val heuristics = Array(mostFrequent(_,1), mostFrequent(_,2))
Open question is, why Scala can determine the type of firstResponse(_) correctly while it has difficulty to do the same for mostFrequent(_,1).
The compiler complains with something similar to this:
Error:(28, 29) missing parameter type for expanded function ((x$3: ) => mostFrequent(x$3, 3))
As you probably already figured out, that happens because the compiler cannot figure out automatically (infer) the type of the input parameter of those functions, when you use _. More precisely, it can't infer the type of mostFrequent(_, 3).
So, if you give the compiler a nudge, either by val heuristics: Array[Array[String] => String] = or by the following:
val heuristics = Array(
(a : Array[String]) => firstResponse(a),
(a : Array[String]) => mostFrequent(a, 3),
(a : Array[String]) => mostFrequent(a, 4),
(a : Array[String]) => mostFrequent(a, 5)
)
Things will work as expected.
Looking at posts about _ uses like this or this, you will see that it can mean very many things, depending on the context. In this case I suspect the confusion comes from the fact that you are using _ to transform a call to a method with more than one parameter to an anonymous function.
Notice that both of the following will work fine:
val heuristics = Array(
firstResponse(_),
firstResponse(_),
firstResponse(_)
)
val heuristics2 = Array(
firstResponse(_),
mostFrequent(_: Array[String], 3)
)
As to the specific reason why a method with more than one argument cannot be transformed into an anonymous function, while one with one argument can, I will delegate to someone with more in-depth knowledge of the compiler's inference mechanics.
Sometimes when you use underscores as placeholders for parameters,
the compiler might not have enough information to infer missing parameter
types. Therefore, you need to explicitly provide type information. Placeholder syntax act as a “blank” in the expression that needs to be “filled in" and you can fill any value to it. Therefore, compiler will have no information about the type of this placeholder.
val foo = _ + _
//will fail - error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
The above expression will fail, because compiler will unable to find type of value that fill the placeholder. Therefore, there need to be some way for compiler to know the type. The one way is to provide type information of variable/method explicitly.
val foo: (String, String) => String = _ + _
The above expression will successfully compiled. Because, compiler resolve type of the parameter from type of variable foo (1st and 2nd placeholder are both as String).
In certain case, compiler can resolve the type from value:
List(1,2,3).foreach(println(_))
In above case, List(1,2,3) is a List of type Int, hence compiler will know type information of placeholder in println(_) as Int which is resolved from value of List.
In addition, you can also provide type of value explicitly in order to let compiler know about type.
val foo = (_:String) + (_:String) //will return function (String, String) => String
In certain case, if your method have only one parameter, then you don't need to provide explicit type parameter otherwise you need to provide type for placeholder syntax as below:
scala> def firstResponse(r: Array[String]): String = r(0)
firstResponse: (r: Array[String])String
scala> val foo = firstResponse(_) //no need to provide type information
foo: Array[String] => String = <function1>
scala> def firstResponse2(r: Array[String], index:Int): String = r(index)
firstResponse2: (r: Array[String], index: Int)String
scala> val foo = firstResponse2(_, 3) //will fail, need to provide type information.
<console>:12: error: missing parameter type for expanded function ((x$1) => firstResponse2(x$1, 3))
val foo = firstResponse2(_, 3)
^
scala> val foo = firstResponse2((_:Array[String]), 3)
foo: Array[String] => String = <function1>
Now coming to your case:
val heuristics = Array(
firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)
Here, compiler will have no idea of what is the type because:
val heuristics have no type
Type for placeholder syntax is not explicitly provided.
You have solve the issue by providing type Array[Array[String] => String] to heuristics val as in case 1, and hence compiler compiles it fine.
For case 2, you can modify your code as below:
val heuristics = Array(
firstResponse(_), mostFrequent(_:Array[String], 3), mostFrequent(_:Array[String], 4), mostFrequent(_:Array[String], 5)
)
The weird thing is that val foo = firstResponse(_) works, because the specification directly forbids it:
If there is no expected type for the function literal, all formal parameter types Ti must be specified explicitly, and the expected type of e is undefined.
I thought that it could be treated as equivalent to eta-expansion firstResponse _ which worked without expected type because firstResponse isn't overloaded, but it's defined to be the other way around: firstResponse _ means the same as x => firstResponse(x), which is not supposed to work according to the above quote.
So strictly speaking, it appears to be a bug and you should write firstResponse(_: Array[String]) as well.
Though in this case, to avoid repetition I'd provide the expected type as
val heuristics = Array[Array[String] => String](
firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)
Related
I don't know why pattern d is bad in this list below.
Why need expicit type declaration?
def adder1(m:Int,n:Int) = m + n
val a = adder1(2,_) //OK
val b = adder1(_,2) //OK
def adder2(m:Int)(n:Int) = m + n
val c = adder2(2)(_) //OK
val d = adder2(_)(2) //NG:missing parameter type
val e = adder2(_:Int)(2) //OK
I just want to know the reason pattern d needs parameter type.
Very welcome just showing citation language spec.
So I believe this comes from the concept of Partial Application.
Intuitively, partial function application says "if you fix the first arguments of the function, you get a function of the remaining arguments"
...
Scala implements optional partial application with placeholder, e.g. def add(x: Int, y: Int) = {x+y}; add(1, _: Int) returns an incrementing function. Scala also support multiple parameter lists as currying, e.g. def add(x: Int)(y: Int) = {x+y}; add(1) _.
Lets take a look at adder2
From the REPL:
scala> def adder2(m:Int)(n:Int) = m + n
def adder2(m: Int)(n: Int): Int
Lets get a value to represent this:
scala> val adder2Value = adder2
^
error: missing argument list for method adder2
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `adder2 _` or `adder2(_)(_)` instead of `adder2`.
Ok, let's try:
val adder2Value = adder2 _
val adder2Value: Int => (Int => Int) = $Lambda$1382/0x0000000840703840#4b66a923
Ahha!
In English: "A function that takes an Int and returns a function that takes an Int and returns an Int"
How can we bind the second argument using this signature? How can we access the inner function unless we first have gone through the outer one?
As far as I know, this is not possible to do using this signature, unless you explicitly define the type of your first argument.
(But what about adder2(_)(_)?)
scala> adder2(_)(_)
^
error: missing parameter type for expanded function ((<x$1: error>, x$2) => adder2(x$1)(x$2))
^
error: missing parameter type for expanded function ((<x$1: error>, <x$2: error>) => adder2(x$1)(x$2))
(Maybe this hints at our solution?)
Notice what happens if we explicitly define both arguments:
val adder2Value2= adder2Value (_:Int) (_:Int)
val adder2Value2: (Int, Int) => Int = $Lambda$1394/0x000000084070d840#32f7d983
This is much more manageable, we can now fix either argument, and get a simplified partial function:
scala> val adder2FirstArg = adder2Value (_:Int) (10)
val adder2FirstArg: Int => Int = $Lambda$1395/0x000000084070d040#47f5ddf4
scala> val adder2SecondArg = adder2Value (5) (_:Int)
val adder2SecondArg: Int => Int = $Lambda$1396/0x000000084070c840#21ed7ce
So what's really going on here?
When you bind an argument to a value, you have explicitly expressed the type (maybe it's inferred, but it's definitely that type, in this case, Ints). It's sugar so we don't need to write it. But under the hood, these are composed functions, and how they are composed is very important. To be able to match and simplify the function signature, the compiler requires us to provide this information in an outside-in manner. Otherwise, we need to give it some help to get there.
EDIT:
I think that this question serves as more of a Scala language spec. puzzle exercise, however. I can't think of any good reason, from a design perspective, for which you would need to implement a curried function in such a way that you cannot order the parameters such that the last parameters are the ones being inferred.
I am going through some exercises to get accustomed to Scala's type system. I have a helper function for running unit tests where I specify the input and expected output of a function. In this case the function I'm testing returns the first n elements of a list:
val inputs: List[TestCase[(List[Any], Int), List[Any]]] = List(
TestCase(
input = (List('a', 't', 'o'), 2),
output = List('a', 't')
),
TestCase(
input = (List("Vegetable", "Fruit"), 4),
output = List("Vegetable", "Fruit")
),
TestCase(
input = (List(3.14, 6.22, 9.5), -7),
output = Nil
)
)
My question is if it is possible to specify a type parameter for this input val. That List[Any] is alright, but I want to specify somehow that the input list has the same type of elements as the output List. Something like this (doesn't work btw):
val inputs[SameType]: List[TestCase[(List[SameType], Int), List[SameType]]] = ...
I'd appreciate any suggestions. Maybe type parameters are not meant to be used with vals?
In case you're wondering why I'm not just asserting the results: it's because I have multiple implementations of the same function and I don't want to repeat the test cases for every implementation.
Consider parameterising your model like so
case class TestCase[T](input: (List[T], Int), output: List[T])
so now we can define, say,
val inputs: List[TestCase[String]] = ???
In Scala 2 value definition cannot be parameterised but a method definition can, for example
def input[T](x: List[TestCase[T]]) = ???
however Scala 3 might change that so we can write
val input = [T] => (x: List[TestCase[T]]) => ???
Another approach is to use generalised constraints, for example,
def assertEquals[A, B](input: List[A], output: List[B])(implicit ev: A =:= B) = ???
assertEquals(List(1), List("")) // error: Error: Cannot prove that Int =:= String
Consider munit for examples of generalised constraints.
I have a unit test, which test some solution. But this test code can also be applied for testing the other, very similar solution. What I want to make is code of test be generic to be applied to both solutions, like this:
describe("when table contains all correct rows") {
it("should be empty") {
def check[T](func: T => List[Row]) = {
val tableGen = new TableGenerator()
val table: Vector[Row] = tableGen.randomTable(100)
.sortWith(_.time isBefore _.time).distinct
val result: List[Row] = func(table)
assert(result.isEmpty)
}
check(Solution.solution1)
check(Solution.solution2)
}
}
where solutions have types:
solution1: IndexedSeq[Row] => List[Row]
solution2: Seq[Row] => List[Row]
how check() function has to be written to be able to do that?
And what's the best approaches to write this (might be in other way) with eliminated code duplication?
Update:
When I try to compile this code I get type mismatch error in func(table):
Error:(36, 29) type mismatch;
found : table.type (with underlying type scala.collection.immutable.Vector[com.vmalov.tinkoff.Row])
required: T
val result = func(table)
For this to work, you need to be able to pass a Vector[Row] to func, so any Vector[Row] has to be a T; that is, T is a supertype of Vector[Row]. You can tell this to the compiler by using a type parameter bound:
def check[T >: Vector[Row]](func: T => List[Row])
Alternately, by the above reasoning, a function T => List[Row] will also be a function Vector[Row] => List[Row] precisely when T is a supertype of Vector[Row], and the Scala compiler knows about this (functions are contravariant in their argument type(s)). So this signature is equivalent to simpler
def check(func: Vector[Row] => List[Row])
Of course, you can generalize this, but how much depends on your specific desires. E.g. you can replace List[Row] with Seq[Row] (everywhere), or with a type parameter and pass an extra function to check:
def check[A](func: Vector[Row] => A)(test: A => Boolean) = {
val table = ...
val result = func(table)
assert(test(result))
}
check(Solution.solution1)(_.isEmpty) // the compiler infers A is List[Row]
Your case, maybe is enough to abstract the type on a more specific way, like defining that you are expecting a Travesable.
def check[S[_] : Traversable](func: S[Row] => List[Row])
That would accept either Seq or IndexedSeq as valid parameter, while it also be limiting it.
I hope it helps
EDITED: Check Alexey Romanov Answer, as with this you will not be able to call func the way you do it. Sorry about that
def check(func: Vector[Row] => List[Row])
I am trying to merge two sequences of dates in Scala such that the merged sequence has sorted elements. I am using a partial implementation of isBefore as follows:
val seq1 = Seq(LocalDate.of(2014, 4, 5), LocalDate.of(2013, 6 ,7), LocalDate.of(2014, 3, 1))
val seq2 = Seq(LocalDate.of(2012, 2, 2), LocalDate.of(2015, 2, 1))
var arr = (seq1 ++ seq2).sortWith(_.isBefore(_) = 1)
println(arr)
But it shows compilation error for the isBefore function:
Multiple markers at this line
- missing arguments for method isBefore in class LocalDate; follow this method with `_' if you want to
treat it as a partially applied function
- missing arguments for method isBefore in class LocalDate; follow this method with `_' if you want to
treat it as a partially applied function
I am relatively new to Scala. What seems to be the problem?
At first there is no such term as partial implementation, at least i didn't heard of such, i guess you've meant partial application, but there is no partial application in this case, partial application is about curried functions, what complier is trying to tell you in your error message. Example of this:
def test(a: String)(f: String => String) = f(a)
val onString = test("hello world") _
onString(_.capitalize)
test: (a: String)(f: String => String)String
onString: (String => String) => String = <function1>
res8: String = Hello world
This is a partial application, you take a curried function, which returns another function and passes it one argument (partially applies it) and latter passes another argument.
As for you sorting problem, that should work. I don't which which library you are using but with Date time it's similar. I think the problem is in assignment (_.isBefore(_) = 1), this is illegal. Should be like this:
val seq1 = Seq(LocalDate.parse("2014-04-05"), LocalDate.parse("2013-06-07"), LocalDate.parse("2014-03-01"))
val seq2 = Seq(LocalDate.parse("2012-02-02"), LocalDate.parse("2015-02-01"))
var arr = (seq1 ++ seq2).sortWith(_.isBefore(_))
arr: Seq[org.joda.time.LocalDate] = List(2012-02-02, 2013-06-07, 2014-03-01, 2014-04-05, 2015-02-01)
def foo(num:Int, str:String):Int = 1
val bar = foo(3, _) // compiler complains "missing parameter type for expanded function ((x$1) => test(3, x$1))"
val baz = foo(3, _:String) // compiles fine
Why do I have to explicitly specify the type of _ when it looks inferrable from the context?
EDIT: Renamed to avoid name collision following David Soergel's suggest.
First of all, to avoid confusion between "def test" and "val test", let's write:
def foo(num:Int, str:String):Int = 1
val bar = foo(3, _) // compiler complains "missing parameter type for expanded function ((x$1) => foo(3, x$1))"
val baz = foo(3, _:String) // compiles fine
What's inferrable from context is only that the argument to bar must somehow be convertible to a String. That could be due to inheritance (if instead of String you use some non-final type there), or due to an implicit conversion.
Basically the potential for implicits means that the argument to bar could be just about any type at all, so the code as written is indeed underspecified. I don't know whether the compiler actually checks whether there are any appropriate implicit conversions in scope before issuing the "missing type" error, but I would guess not. (In the case of String there are likely to be a bunch present, anyway). It would be brittle and confusing if the signature of baz changed as a result of importing a new implicit that could produce a String.
I think David Soergel's explanation is essentially correct: if type T has an implicit conversion to String then val bar = foo(3, _:T) is valid, giving a function of type T => Int, which is unrelated to String => Int.
Why the compiler doesn't make a sensible assumption (in the absence of explicit typing) that the type is in fact the same as in the method (which is the essence of your question), I don't know - I can only guess it's because it would complicate the language spec.
Where no types are specified, i.e. val bar = foo(_, _), it seems the compiler interprets it as simple eta-conversion, the same as val bar = foo _, which does give a String => Int.
My preferred idiom would be to give the function type on the left hand side, which has the benefit of allowing you to easily see bar's type:
val bar: String => Int = foo(3, _)
If you're allergic to re-typing the word String, you could write
val bar = (foo _).curried(3)