def fn[String] seems to break Scala / java.lang.String compatibility - scala

Hello there Stack Overflow,
I hope you'll help me with my very first question here :)
So I'm having a problem with Scala type inference. Here is the code:
object Problem {
def ok(fn: (String) => Unit) = fn("")
// type mismatch; found: java.lang.String("") required: String
def fail[String](fn: (String) => Unit) = fn("")
}
What kind of String does Scala expect here?
Note that this is a minimal example to explain my problem. The original issue appeared when I tried to implement a more complex interface (Play's Iteratee, to be precise), so, no, leaving out the [String] is not an option. (If anyone thinks that the actual code would help, I'll provide it.)
I tried def fail[java.lang.String] ... but then it says expected ], found ..
I did read Scala String vs java.lang.String - type inference which gives a good explanation on java.lang.String vs. scala.Predef.String, but I still could not come up with a solution for my specific problem.
Any ideas?
EDIT: So here is the original attempt how I tried to implement http://www.playframework.org/documentation/api/2.0/scala/play/api/libs/iteratee/Iteratee.html only that I wrote String instead of T. (With T it compiles, and it makes sense!) My fail; obviously I was a bit overwhelmed by all the type parameters:
val stream = WS.url("url").get({ headers =>
(new Iteratee[Array[Byte], String] {
def fold[T](done: (String, Input[Array[Byte]]) => Promise[T],
cont: (Input[Array[Byte]] => Iteratee[Array[Byte], String]) => Promise[T],
error: (String, Input[Array[Byte]]) => Promise[T]): Promise[T] =
{
done("something", Input.Empty)
}
})
})
Regards,
Hendrik

When you write:
def fail[String](fn: (String) => Unit) = fn("")
The type parameter between square brackets String is just an arbitrary name that, in your case, will hide scala or java string. It is fully equivalent to:
def fail[T](fn: (T) => Unit) = fn("")
If you want to constrain the type to string, you just have to write:
def fail(fn: (String) => Unit) = fn("")
And it will work for scala and java strings (since they are the same).

This problem has nothing to do with Java vs Scala strings.
In the line def fail[String](fn: (String) => Unit) = fn("") you're definining a completely new type parameter and naming it String. This shadows the general definition.
A type paramter is needed if you intend to abstract over the type. You are not doing this in the fail example, and this should be dropped.
If you are overriding something that uses a type parameter, then you should specify this at the class level:
class A extends B[String]
Looking at the code for Iteratee, I'll guess you're trying to implement fold with its various done, cont and error functions. Fold only has one type paramter, which is used in the return type, so I'm not sure where this can come from. The input type parameters are specified on the class, so if you extend Iteratee[String, Unit] these should be provided.

Related

Mixing dependent types and 'concrete' types in Scala 3

I'm fairly new to Scala in general, and Scala 3 in particular, and I'm trying to write some code that deals with transparently encoding + decoding values before they are passed to another library.
Basically, I need to map a set of types like Ints to a counterpart in the underlying library. The code I've written is too verbose to replicate here in full, but here's a minimal example demonstrating the kind of thing, using a higher-kinded Encoder type that encapsulates encoding values into types which depend on the values' original types:
trait Encoder[T] {
type U
def encode(v: T): U
}
object Encoder {
given Encoder[Int] with {
override type U = String
override def encode(v: Int): String = v.toString
}
}
case class Value[T : Encoder](v: T) {
val encoder: Encoder[T] = summon[Encoder[T]]
}
I also need to be able to write functions that deal with specific types of Value and which have 'concrete' return types. Like this:
def doStuff(v1: Value[Int]): String = {
v1.encoder.encode(v1.v)
}
However, even though in this case v1.codec.encode does indeed return a String, I get an error:
-- [E007] Type Mismatch Error: -------------------------------------------------
2 | v1.encoder.encode(v1.v)
| ^^^^^^^^^^^^^^^^^^^^^^^
| Found: v1.encoder.U
| Required: String
What can I do differently to solve this error? Really appreciate any pointers to help a newbie out 🙏
Answering the question in the comments
Is there any sensible way I tell the compiler that I’m only interested in Values with Encoders that encode to String?
You can force Value to remember its encoder's result type with an extra type argument.
case class Value[T, R](val v: T)(
using val encoder: Encoder[T],
val eqv: encoder.U =:= R,
)
The encoder is the same as your encoder, just moved to the using list so we can use it in implicit resolution.
eqv is a proof that R (our type parameter) is equivalent to the encoder's U type.
Then doStuff can take a Value[Int, String]
def doStuff(v1: Value[Int, String]): String = {
v1.eqv(v1.encoder.encode(v1.v))
}
Let's be clear about what's happening here. v1.encoder.encode(v1.v) returns an encoder.U. Scala isn't smart enough to know what that is. However, we also have a proof that encoder.U is equal to String, and that proof can be used to convert an encoder.U to a String. And that's exactly what =:=.apply does.
We have to do this back in the case class because you've already lost the type information by the time we hit doStuff. Only the case class (which instantiates the implicit encoder) knows what the result type is, so we need to expose it there.
If you have other places in your codebase where you don't care about the result type, you can fill in a type parameter R for it, or use a wildcard Value[Int, ?].
I would also suggest giving Match Types a try if we are only talking about Scala 3 here.
import scala.util.Try
type Encoder[T] = T match
case Int => String
case String => Either[Throwable, Int]
case class Value[T](v: T):
def encode: Encoder[T] = v match
case u: Int => u.toString
case u: String => Try(u.toInt).toEither
object Main extends App:
val (v1, v2) = (Value(1), Value(2))
def doStuff(v: Value[Int]): String =
v.encode
println(doStuff(v1) + doStuff(v2)) //12
println(Value(v1.encode).encode) //Right(1)

Implicit conversions weirdness

I am trying to understand why exactly an implicit conversion is working in one case, but not in the other.
Here is an example:
case class Wrapper[T](wrapped: T)
trait Wrapping { implicit def wrapIt[T](x: Option[T]) = x.map(Wrapper(_))
class NotWorking extends Wrapping { def foo: Option[Wrapper[String]] = Some("foo") }
class Working extends Wrapping {
def foo: Option[Wrapper[String]] = {
val why = Some("foo")
why
}
}
Basically, I have an implicit conversion from Option[T] to Option[Wrapper[T]], and am trying to define a function, that returns an optional string, that gets implicitly wrapped.
The question is why, when I try to return Option[String] directly (NotWorking above), I get an error (found : String("foo") required: Wrapper[String]), that goes away if I assign the result to a val before returning it.
What gives?
I don't know if this is intended or would be considered a bug, but here is what I think is happening.
In def foo: Option[Wrapper[String]] = Some("foo") the compiler will set the expected type of the argument provided to Some( ) as Wrapper[String]. Then it sees that you provided a String which it is not what is expected, so it looks for an implicit conversion String => Wrapper[String], can't find one, and fails.
Why does it need that expected type stuff, and doesn't just type Some("foo") as Some[String] and afterwards try to find a conversion?
Because scalac wants to be able to typecheck the following code:
case class Invariant[T](t: T)
val a: Invariant[Any] = Invariant("s")
In order for this code to work, the compiler can't just type Invariant("s") as Invariant[String] because then compilation will fail as Invariant[String] is not a subtype of Invariant[Any]. The compiler needs to set the expected type of "s" to Any so that it can see that "s" is an instance of Any before it's too late.
In order for both this code and your code to work out correctly, I think the compiler would need some kind of backtracking logic which it doesn't seem to have, perhaps for good reasons.
The reason that your Working code does work, is that this kind of type inference does not span multiple lines. Analogously val a: Invariant[Any] = {val why = Invariant("s"); why} does not compile.

Write function with type parameter

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])

Strange implicit def with function parameter behaviour in Scala

I've written a simple code in Scala with implicit conversion of Function1 to some case class.
object MyApp extends App{
case class FunctionContainer(val function:AnyRef)
implicit def cast(function1: Int => String):FunctionContainer = new FunctionContainer(function1)
def someFunction(i:Int):String = "someString"
def abc(f : FunctionContainer):String = "abc"
println(abc(someFunction))
}
But it doesn't work. Compiler doesn't want to pass someFunction as an argument to abc. I can guess its reasons but don't know exactly why it doesn't work.
When you use a method name as you have, the compiler has to pick how to convert the method type to a value. If the expected type is a function, then it eta-expands; otherwise it supplies empty parens to invoke the method. That is described here in the spec.
But it wasn't always that way. Ten years ago, you would have got your function value just by using the method name.
The new online spec omits the "Change Log" appendix, so for the record, here is the moment when someone got frustrated with parens and introduced the current rules. (See Scala Reference 2.9, page 181.)
This has not eliminated all irksome anomalies.
Conversions
The rules for implicit conversions of methods to functions (§6.26) have been tightened. Previously, a parameterized method used as a value was always implicitly converted to a function. This could lead to unexpected results when method arguments were forgotten. Consider for instance the statement below:
show(x.toString)
where show is defined as follows:
def show(x: String) = Console.println(x)
Most likely, the programmer forgot to supply an empty argument list () to toString. The previous Scala version would treat this code as a partially applied method, and expand it to:
show(() => x.toString())
As a result, the address of a closure would be printed instead of the value of s. Scala version 2.0 will apply a conversion from partially applied method to function value only if the expected type of the expression is indeed a function type. For instance, the conversion would not be applied in the code above because the expected type of show’s parameter is String, not a function type. The new convention disallows some previously legal code. Example:
def sum(f: int => double)(a: int, b: int): double =
if (a > b) 0 else f(a) + sum(f)(a + 1, b)
val sumInts = sum(x => x) // error: missing arguments
The partial application of sum in the last line of the code above will not be converted to a function type. Instead, the compiler will produce an error message which states that arguments for method sum are missing. The problem can be fixed by providing an expected type for the partial application, for instance by annotating the definition of sumInts with its type:
val sumInts: (int, int) => double = sum(x => x) // OK
On the other hand, Scala version 2.0 now automatically applies methods with empty parameter lists to () argument lists when necessary. For instance, the show expression above will now be expanded to
show(x.toString())
Your someFunction appears as a method here.
You could try either
object MyApp extends App{
case class FunctionContainer(val function:AnyRef)
implicit def cast(function1: Int => String):FunctionContainer = new FunctionContainer(function1)
val someFunction = (i:Int) => "someString"
def abc(f : FunctionContainer):String = "abc"
println(abc(someFunction))
}
or
object MyApp extends App{
case class FunctionContainer(val function:AnyRef)
implicit def cast(function1: Int => String):FunctionContainer = new FunctionContainer(function1)
def someFunction(i:Int): String = "someString"
def abc(f : FunctionContainer):String = "abc"
println(abc(someFunction(_: Int)))
}
By the way: implicitly casting such common functions to something else can quickly lead to problems. Are you absolutely sure that you need this? Wouldn't it be easier to overload abc?
You should use eta-expansion
println(abc(someFunction _))

Scala Syntax: Syntactic sugar for converting a Function0 into function1?

Sorry for the nebulous title, I had a hard time to to formulate what I mean. For that reason, lets directly dive into the code:
def bar(b: Int => String) = b(23)
def foo(b: => String) = bar(_ => b)
foo("foo" + "bar")
I found a similar code in the play framework and was wondering what actually happens with that code. I tinkered a bit and figured that bar(_ => b) will just create a function1 with the required argument type (given by bars signature) and uses the given function0 to generate the return value, ignoring the actual parameter of the function1.
However, this is just me trying to figure out what happens, a more definitive answer would be way better for my understanding - and besides that I might be completely wrong.
You're sort of right, except that => String is not a Function0 - () => String is.
=> String is a call-by-name string. It'll be evaluated when it's referred to.
def foo(b: String) = bar(_ => b) would also get the job done, with the exception that b would be evaluated eagerly.