How to properly type beam.Map with pyright - apache-beam

I have beam code like:
def foo(i: int) -> Foo:
...
class IntToFoo(beam.PTransform):
def expand(self, pcoll: PCollection[int]) -> PCollection[Foo]:
return pcoll | beam.Map(foo)
Running pyright it complains for the returning line:
Expression of type "PValue" cannot be assigned to return type "PCollection[Foo]"
"PValue" is incompatible with "PCollection[Foo]
Now AFAIU it seems like beam does not provide sufficient type hinting.
Is there a way of working around that as a user of beam so that pyright accepts the code (without ignoring the type error).
EDIT:
I now tried:
temp = pcoll | beam.Map(foo)
assert isinstance(temp, PCollection[Foo])
return temp
Good news is, neither temp = nor return line has any errors. Bad news is that it complains on isinstance line now:
TypeVar or generic type with type arguments not allowed
EDIT 2:
For now I settled with
temp = pcoll | beam.Map(foo)
return cast(PCollection[Foo], temp)
which IMO is still not a solution, because I then would need to specify PCollection[Foo] both the signature as well as the cast.

Related

Scala eta expression ambiguous reference doesn't list all overloaded methods

I'm using Scala 2.12.1. In the Interpreter I make an Int val:
scala> val someInt = 3
someInt: Int = 3
Then I tried to use the eta expansion and get the following error:
scala> someInt.== _
<console>:13: error: ambiguous reference to overloaded definition,
both method == in class Int of type (x: Char)Boolean
and method == in class Int of type (x: Byte)Boolean
match expected type ?
someInt.== _
^
I see in the scaladoc that the Int class has more than 2 overloaded methods.
Question: is there a particular reason that the error message shows only 2 overloaded methods as opposed to listing all of them?
By the way, to specify which method you want to use the syntax is this:
scala> someInt.== _ : (Double => Boolean)
res9: Double => Boolean = $$Lambda$1103/1350894905#65e21ce3
The choice of the two methods that are listed seems to be more or less arbitrary. For example, this snippet:
class A
class B
class C
class Foo {
def foo(thisWontBeListed: C): Unit = {}
def foo(thisWillBeListedSecond: B): Unit = {}
def foo(thisWillBeListedFirst: A): Unit = {}
}
val x: Foo = new Foo
x.foo _
fails to compile with the error message:
error: ambiguous reference to overloaded definition,
both method foo in class Foo of type (thisWillBeListedFirst: this.A)Unit
and method foo in class Foo of type (thisWillBeListedSecond: this.B)Unit
match expected type ?
x.foo _
That is, it simply picks the two last methods that have been added to the body of the class, and lists them in the error message. Maybe those methods are stored in a List in reverse order, and the two first items are picked to compose the error message.
Why does it do it? It does it, because it has been programmed to do so, I'd take it as a given fact.
What was the main reason why it was programmed to do exactly this and not something else? That would be a primarily opinion-based question, and probably nobody except the authors of the Scala compiler themselves could give a definitive answer to that. I can think of at least three good reasons why only two conflicting methods are listed:
It's faster: why search for all conflicts, if it is already clear that a particular line of code does not compile? There is simply no reason to waste any time enumerating all possible ways how a particular line of code could be wrong.
The full list of conflicting methods is usually not needed anyway: the error messages are already quite long, and can at times be somewhat cryptic. Why aggravate it by printing an entire wall of error messages for a single line?
Implementation is easier: whenever you write a language interpreter of some sort, you quickly notice that returning all errors is somewhat more difficult than returning just the first error. Maybe in this particular case, it was decided not to bother collecting all possible conflicts.
PS: The order of the methods in the source code of Int seems to be different, but I don't know exactly what this "source" code has to do with the actual Int implementation: it seems to be a generated file without any implementations, it's there just so that #scaladoc has something to process, the real implementation is elsewhere.

Scala - Function's implicit return type

I am new to scala, and got a little doubt about function definition & default return type.
Here is a function definition:
def wol(s: String) = s.length.toString.length
The prompt says it's:
wol: (s: String)Int
But, the code didn't specify return type explicitly, shouldn't it default to Unit, which means void in Java.
So, what is the rules for default return type of a Scala function?
The return type in a function is actually the return type of the last expression that occurs in the function. In this case it's an Int, because #length returns an Int.
This is the work done by the compiler when it tries to infer the type. If you don't specify a type, it automatically gets inferred, but it's not necessarily Unit. You could force it to be that be stating it:
def wol(s: String): Unit = s.length.toString.length
EDIT [syntactic sugar sample]
I just remembered something that might be connected to your previous beliefs. When you define a method without specifying its return type and without putting the = sign, the compiler will force the return type to be Unit.
def wol(s: String) {
s.length.toString.length
}
val x = wol("") // x has type Unit!
IntelliJ actually warns you and gives the hint Useless expression. Behind the scene, the #wol function here is converted into something like:
// This is actually the same as the first function
def wol(s: String): Unit = { s.length.toString.length }
Anyway, as a best practice try to avoid using this syntax and always opt for putting that = sign. Furthermore if you define public methods try to always specify the return type.
Hope that helps :)

Migration play from 2.2 to 2.3: No implicit view available from Any

Try to migrate my project from play 2.2.7 to 2.3.8.
The project has the following code:
def setCoordinatesOfAddresses(cardId: Long, caller: Option[Tuple2[Float,Float]] = None, event: Option[Tuple2[Float,Float]] = None) {
Logger.debug("callling SC.setCoordinatesOfAddresses")
DB.withConnection { implicit c =>
try {
val query = SQL("Execute Sc.setCoordinatesOfAddresses {cardId}, {deviceLatitude}, {deviceLongitude}, NULL, NULL, {eventsLatitude}, {eventsLongitude}")
query.on(
"cardId" -> cardId,
"deviceLatitude" -> caller.map(_._1).getOrElse(null),
"deviceLongitude" -> caller.map(_._2).getOrElse(null),
"eventsLatitude" -> event.map(_._1).getOrElse(null),
"eventsLongitude" -> event.map(_._2).getOrElse(null) ).execute()
} catch {
case e: Throwable => Logger.error("error", e)
}
}
Logger.debug("SC.setCoordinatesOfAddresses called")
}
And compiler gives me this error:
Error:(384, 28) Play 2 Compiler:
../app/controllers/Cards.scala:384: No implicit view available from Any => anorm.ParameterValue.
"deviceLatitude" -> caller.map(_._1).getOrElse(null),
^
What should I do to solve this problem?
As I understand from documentation, I need to define parameter types. But I can't understand where and how to do that, in following code.
Try
val ps = Seq[anorm.ParameterValue](yourParameter1,yourParameter2) // Seq[ParameterValue]
SQL("SELECT * FROM test WHERE a={a} b={b}").
on('a -> ps(0), 'b -> ps(1))
You are using parameter of Any(or someother) type but the doc says to use anorm.ParameterValue type
Source
I would suggest to first try val p: ParameterValue = caller.
I think the issue is more that there is no specific parameter conversion for Tuple2[Float,Float] (btw (Float, Float)).
Before Anorm 2.3, unsafe conversion was using .toString (which can lead to a lot of issue).
The better is to convert the tuple to a formatted string, either before passing it as parameter or by adding a custom parameter conversion.
(Anorm doc indicates type safety issues, and how to implement conversion)
The getOrElse(null) is causing your problem. This is because you're calling getOrElse on an Option[Float]. However null in Scala cannot be assigned to a value type like Float. From the scaladoc:
Null is a subtype of all reference types; its only instance is the null reference. Since Null is not a subtype of value types, null is not a member of any such type. For instance, it is not possible to assign null to a variable of type scala.Int.
The same statement applies to Float. Since null and Float do not have a common super-type (other than Any), Scala infers the caller.map(_._1).getOrElse(null) as Any instead Float. This was allowed in Play 2.2.x, however in 2.3.x, Any is no longer allowed to be passed as a parameter because it is not type safe.
The solution? Just remove the getOrElse(null)s and you're good to go. caller.map(_._1) is an Option[Float] which Anorm knows how to handle whether it's empty or not. In general, using null in Scala is bad practice. If you see yourself calling getOrElse(null), you should ask yourself if perhaps there's a better way to handle it safely.

Why do I get 'Dead code following this construct' with the following code?

I have following scala code
def message(attachmentId: UUID) : URI = {
var r : mutable.MutableList[BasicTemplate] = new mutable.MutableList[BasicTemplate]
val t : Type = new TypeToken[Iterable[BasicTemplate]](){}.getType()
val m : String = "[{\"basicTemplate\":\"TEMPLATE\",\"baseline\":\"DEMO\",\"identifier\":\"0599999999\"}]"
r = new Gson().fromJson(m, t)
Console.println(r.head.getBasicTemplateName)
URI.create("http://google.com")
}
And it gives me following compilation error:
[ERROR] Class1.scala:402: error: dead code following this construct
[ERROR] r = new Gson().fromJson(m, t)
Any ideas why I get this error are highly appreciated!
Look at the signature of fromJson:
public <T> T fromJson(String json, Type typeOfT)
As you can see, this method has type parameter T, but you called it without specifying it. This way, type inferencer understood it as new Gson().fromJson[Nothing](m, t) and the entire expression was assigned the type Nothing.
In Scala, Nothing is a bottom type that is a subtype of all types and has no values. Methods that return Nothing are guaranteed to never return, either because they always throw an exception, fall into infinite loop, forcibly terminate the program (e.g. sys.exit()), etc. In your case, the fromJson call will cause a ClassCastException to be thrown when the JVM tries to cast its result to Nothing. Therefore, everything after that call is a dead code.
This type inference behaviour is different from Java, which would normally infer new Gson().<Object>fromJson(m, t) here.

Can we infer generic output type?

Consider the following Scala code
def NOTImplementedIn[T<: AnyRef](t:T):String =
throw new Exception(t.getClass.getName+": Input type not implemented")
def NOTImplementedOut[T<: AnyRef](s:String):T =
throw new Exception("Output type not implemented")
In the first case, it is possible to infer the input type T. Is there any way to infer the output type T in the second case? I would like to include the type name in the exception.
The lower type bound of T will always be inferred. If no lower type bound is explicitly given, Nothing is the bound.
This inference is the correct one. As generic parameter type are erased as run time, calls to f[String]("foo"), f[File]("foo") and f[List[Int]]("foo") and f[Nothing]("foo") are the same call f("foo") at run time. The result must be a valid String, File, List[Int], and Nothing. Being Nothing is the only way to satisfy that (which means there will be no result). If there was a stronger lower type bound, it would only need to be of that type.
A consequence of that is that a routine f[T >: LowerBound](parameters): T, where T does not appears in the parameters, is no different from f(parameters): LowerBound, and there is no point in making it generic (when LowerBound is not explicitly stated, the bound is type Nothing)
With the same idea that the call f[T]("foo") is only f("foo") at runtime, it is clear that the name of cannot appear in the exception message. So T has to be someway passed at runtime, and manifests are the normal way to do that, as noted by Dave. Now, T does appear in the parameters, and everything is different. if one calls f[File]("foo"), the compiler will transform that to f("foo")(manifest_of_file) where manifest_of_file allows among other thing to get the name of the type File.
This is still not inference, as File has been explicitly written. The only way type T can be inferred is from the expected type of the call to f in context. If one does val file: File = f("foo"), or passes f("foo") the value of a parameter of type File to some routine, or simply writes f("foo"): File, will File be inferred? No, it won't. Once again, Nothing will be inferred. The reason is that it is a more precise type than File, and as the compiler can pass either a Manifest[File] or a Manifest[Nothing] (and any type in between) it choose the more precise one. This may be surprising for Nothing, but suppose that your lower type bound is String, and the expected type is simply AnyRef, there is no obvious reason to chose AnyRef rather than the better String.
def f[T >: String : Manifest](s: String): T = "type is " + manifest[T].toString
val x: AnyRef = f("foo"): AnyRef
x: Anyref = type is java.lang.String
Same with your example and Nothing as the lower type bound:
def g[T: Manifest](s: String): T = throw new Exception("type is " + manifest[T])
val y: String = g("foo"): String
java.lang.Exception: type is Nothing
at .g(<console>:7)
....
Yes, using a Manifest.
def NOTImplementedOut[T<: AnyRef](s:String)(implicit m: scala.reflect.Manifest[T]):T =
throw new Exception(m.toString + ": Output type not implemented")