Implicit String to Seq[Char] pattern matching fail - scala

Warning - this is a code which deals with Codility BinaryGap task - just to warn as this may spoil something to somebody.
I have a piece of code, like
#tailrec
def count2(max:Int, count:Int, d:Seq[Char]):Int = d match {
case '1' :: s => count2(Math.max(max, count), 0, s)
case '0' :: s => count2(max, count+1, s);
case Nil => max
}
I call it like
println(Solution.count2(0, 0, Seq('1', '0')))
println(Solution.count2(0, 0, "10"))
It compiles, however the second call does not work - throwing "Match not found 10" And I cannot understand why. There is a similar question around that topic which states, that explicit conversion is needed. However, I feel like I do have one in form of a method parameter type.
Debugger clearly states that d variable is of type WrappedString - which should do the job. But apparently, it does not.
What is going on here?

Your pattern matching is working only on a the type List[Char] while you are passing an object of type Seq[Char]. Therefore you will never actually match on the very first call. The reason it compiles is that match is not exhaustive for Seq. It is, however, exhaustive for List.
Update:
Let me point out two things:
The default constructor for Seq produces a List. So the first example "works."
The String you've given is implicitly convertable to a Seq[Char] but it is not a List! Hence, it will give you your match error.

Related

Scala method type parameterization

I am trying to get a better handle on understanding methods parameterized by types and have this piece of code -
def inferType[T, U](x: T, y: U): Unit = y match {
case _: T => println("T")
case _ => println("Not T")
}
inferType("0", 11) // call 1
inferType(0, 11) // call 2
I would have expected call 1 to have printed "Not T" and call 2 to have printed "T". However, its printing "T" in both cases. Obviously I am missing something here. Can someone help me with why pattern matching is not matching the generic type T here?
The JVM is performing type-erasure, so at runtime all generic types are actually Object, and your match is checking whether y is of type T = Object, which it is.
You can get around this using TypeTag, but this is an anti-pattern in most cases.

Why is a scala Set casted to a Vector instead of a List?

I wonder why a Set[A] is converted to a Vector[A] if I ask for a Seq[A] subclass? To illustrate this take the following example:
val A = Set("one", "two")
val B = Set("one", "two", "three")
def f(one: Seq[String], other : Seq[String]) = {
one.intersect(other) match {
case head :: tail => head
case _ => "unknown"
}
}
f(A.to, B.to)
This function will return "unknown" instead of one. The reason is that A.to will be casted to a Vector[String]. The cons operator (::) is not defined for Vectors but for Lists so the second case is applied and "unknown" is returned. To fix this problem I could use the +: operator which is defined for all Seqs or convert the Set to List (A.to[List]). So my (academic) question is:
Why does A.to returns a Vector. At least according to the scala docs the default implementation of Seq is LinearSeq and the default of this is List. What did I got wrong?
Because it can, you are depending on runtime class implementation details, instead of compile-time type information guarantees. The to or toSeq method is free to return anything that typechecks, it could even generate a random number and chose a concrete class in base of that number, so you may get a List something other times a Vector or whatever. It may even decide in base of the operating system. Of course, I am being pedantic here and hopefully, they do not do that, but my point is, we can't really explain, that is what the implementation does and it may change in the future.
Also, the "default implementation of Seq is a List", applies only in the constructor. And again, they may change that in any moment.
So, if you want a List ask for a List, not for a Seq.

Zero-arg pattern matches when one arg expected

Given this definition in Scala:
class Foo(val n: Int)
object Foo {
def unapply(foo: Foo): Option[Int] = Some(foo.n)
}
This expression compiles and returns ok:
new Foo(1) match {
case Foo() => "ok"
}
Why does this even compile? I would expect that an extractor with Option[T] implies matching patterns with exactly one argument only.
What does the pattern Foo() mean here? Is it equivalent to Foo(_)?
In other words, what is the language rule that enables the experienced behavior.
Today (in 2.11 milestone) you get the error:
<console>:15: error: wrong number of patterns for object Foo offering Int: expected 1, found 0
case Foo() => "ok"
^
I encountered this when adding Regex.unapply(c: Char). At some point, the case you point out was accepted, then later rejected. I remember I liked the idea that if my extractor returns Some(thing), then the Boolean match case r() would work the same as case r(_).
What works is in the scaladoc of unapply(Char) :
http://www.scala-lang.org/files/archive/nightly/docs-master/library/#scala.util.matching.Regex
Section 8.18 of the Scala Language Reference discusses this type of pattern matching. According to the reference, for a pattern like Foo(), it should only match if unapply returns a boolean. If unapply returns Option[T] for some T that isn't a tuple, then the pattern must include exactly one parameter, e.g. Foo(_). Unless I'm really misunderstanding what is happening here, it looks like this is an edge case where the compiler violates the spec.

Omitting dots when chaining calls

I don't understand why the following code doesn't compile:
class Abc
{
def b (x : String) = x + "abc"
def a (y : String) =
{
val ls : List[String] = y.lines toList
b (ls.head)
}
}
Main.scala:8: error: type mismatch;
found : java.lang.String
required: Int
b (ls.head)
When I change "y.lines toList" to
y.lines.toList
or even to
y.lines toList;
it does compile.
Perhaps the compiler understands it like
(y.lines).toList(b (ls.head))
or something like that, but I still don't understand the rules.
It's not obvious, and it's a combination of Scala's shortcut syntax and list indexing. If you want a hint, try redefining b to:
def b(x : String) = 0
You'll get some other compiler garbage back, but the error will change. In short, the Scala compiler will let you omit parens and dots for zero- or one-parameter methods, and we know b looks like it's somehow getting chained.. The rub is that Scala also uses parens for list indexing, so toList, which returns an iterator, may take one parameter as the list index. I'm not sure of this part exactly, but it looks like once you start omitting dots, the lexer will become greedy, and when it comes across a method that may take one parameter, will attempt to pass the next statement to it. In this case, that's a string, so it throws a syntax error.
You got it spot on with this:
(y.lines).toList(b (ls.head))
With the only possible correction being:
(y.lines).toList(b).apply(ls.head)
I'm not sure that Scala would decide in this particular case.
The rule, roughly speaking, is object (method parameters)* [method]. The compiler will continue as long as it finds tokens for a valid expression. A ; finishes the expression, and so would a ) or }. If the next line is blank, the expression also ends. If the next line begins with a reserved keyword (val, def, if, etc), the expression would end too.

Scala unexpectedly not being able to ascertain type for expanded function

Why, in Scala, given:
a = List(1, 2, 3, 4)
def f(x : String) = { x }
does
a.map(_.toString)
work, but
a.map(f(_.toString))
give the error
missing parameter type for expanded function ((x$1) => x$1.toString)
Well... f() takes a String as a parameter. The construct _.toString has type A <: Any => String. The function f() expects a type of String, so the example above does not type check. It seems that Scala is friendly in this case and gives the user another chance. The error message means: "By my type inference algorithms this does not compile. Put the types in and it might, if it's something I can't infer."
You would have to write the anonymous function longhand in this case, i.e. a.map(n => f(n.toString)). This is not a limitation of type inference, but of the wildcard symbol. Basically, when you write a.map(f(_.toString)), the _.toString gets expanded into an anonymous function inside the closest brackets it can find, otherwise this would lead to enormous ambiguity. Imagine something like f(g(_.toString)). Does this mean f(g(x => x.toString)) or f(x => g(x.toString))? Worse ambiguities would arise for multiple nested function calls. The Scala type checker therefore takes the most logical solution, as described above.
Nitpick: the first line of your code should be val a = List(1,2,3,4) :).