Strange type inference behavior - purescript

I'm trying to understand why purescript is unable to properly infer type for map parameter in this simple code:
maybeInc :: String -> StateT (Maybe Int) Identity Unit
maybeInc "a" = modify $ map (1 +)
maybeInc _ = return unit
Here is my error message:
No type class instance was found for
Control.Monad.State.Class.MonadState (_0 Int)
(StateT (Maybe Int) Identity)
The instance head contains unknown type variables. Consider adding a type annotation.
However, it works if I specify the type manually:
maybeInc "a" = modify $ \(m :: Maybe Int) -> map (1 +) m
Why it doesn't want to infer this type automatically even it's already provided in function signature?

The current compiler has no way of modeling functional dependencies, which are used in Haskell and the mtl library to capture the relationship between the two type arguments in MonadState.
This means that the compiler can't figure out that the two state types have to be the same (that is, if we find an instance of MonadState for StateT (Maybe Int) Identity, the state type is forced to be Maybe Int).
For now, one solution is to add a type annotation:
maybeInc :: String -> StateT (Maybe Int) Identity Unit
maybeInc "a" = modify modifier
where
modifier :: Maybe Int -> Maybe Int
modifier = map (1 +)
maybeInc _ = return unit

Related

Scala 3 : Equality compilation error occurring only for certain types

I am trying to understand Scala3 new "Multiversal Equality" feature. I am experiencing inconsistent behavior when comparing different types.
case 1. Compare Int with String:
val x = 1
val y = "One"
x == y // gives compilation error -> "Values of types Int and String cannot be compared with == or !="
Compilation error even without importing scala.language.strictEquality
This compiles in Scala2 without any errors
case 2. Compare two case classes:
case class Cat(catname: String)
case class Dog(dogname: String)
val d = Dog("Frank")
val c = Cat("Morris")
d == c // false, but it compiles
I am aware of the fact that I need to import scala.language.strictEquality to enforce Multiversal equality in case2. But why is it not required in case1?
Notice that
case 1. summon[CanEqual[Int, String]] doesn't compile even without importing scala.language.strictEquality
case 2. summon[CanEqual[Cat, Dog]]
compiles without importing scala.language.strictEquality but
doesn't compile with such importing.
a) Instances of type class CanEqual are generated by the compiler (as well as scala.reflect.ClassTag, scala.reflect.TypeTest, scala.ValueOf, scala.deriving.Mirror.Product, scala.deriving.Mirror.Sum, scala.deriving.Mirror)
val specialHandlers = List(
defn.ClassTagClass -> synthesizedClassTag,
defn.TypeTestClass -> synthesizedTypeTest,
defn.CanEqualClass -> synthesizedCanEqual,
defn.ValueOfClass -> synthesizedValueOf,
defn.Mirror_ProductClass -> synthesizedProductMirror,
defn.Mirror_SumClass -> synthesizedSumMirror,
defn.MirrorClass -> synthesizedMirror)
https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L489-L499
b) The thing is that the case when one of type parameters L, R of CanEqual[-L, -R] is a numeric value class (Byte, Short, Char, Int, Long, Float, Double) is handled differently:
val synthesizedCanEqual: SpecialHandler = (formal, span) =>
...
if canComparePredefined(arg1, arg2)
|| !Implicits.strictEquality && explore(validEqAnyArgs(arg1, arg2))
...
https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L147-L148
Notice that here if the answer is given by canComparePredefined then it doesn't matter whether strictEquality is switched on.
c) canComparePredefined calls
def canComparePredefinedClasses(cls1: ClassSymbol, cls2: ClassSymbol): Boolean =
...
if cls1.isPrimitiveValueClass then
if cls2.isPrimitiveValueClass then
cls1 == cls2 || cls1.isNumericValueClass && cls2.isNumericValueClass
else
cmpWithBoxed(cls1, cls2)
else if cls2.isPrimitiveValueClass then
cmpWithBoxed(cls2, cls1)
...
else
false
https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L108-L114
Notice that here if one of L, R is a numeric value class then the other must be too (taking into account boxing) so that summon[CanEqual[Int, Int]], summon[CanEqual[Double, Double]], summon[CanEqual[Int, Double]] compile but summon[CanEqual[Int, String]] doesn't compile.
Universal equality only works for types without CanEqual instances already defined. As stated in Multiversal Equality:
Even though canEqualAny is not declared as given, the compiler will still construct an canEqualAny instance as answer to an implicit search for the type CanEqual[L, R], unless L or R have CanEqual instances defined on them, or the language feature strictEquality is enabled.
and since there is already an instance of CanEqual for String defined in the companion object as:
given canEqualString: CanEqual[String, String] = derived
universal equality will not work for String nor Int (as per #DmytroMitin the compiler makes special consideration to Scala's numeric types) even with strictEquality disabled.

Why I can't apply just an underscore to first parameter in Scala?

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.

Compile-time Reflection of Nested List: typecheck List[List[Int]] returns List[List[...]]?

I'm using macro annotations to inspect the fields of a class and add a member based on those fields.
e.g.
#AddVal
class A(x: Int)
expands to
class A(x: Int){
val get: Int = x
}
After extracting theValDef, it's tpe field still null so to get the type I have two options:
1) If I call .toString on the type tree, I can see the type, but now I've lost some type-safety
2) If I use c.typecheck on the type tree, I can get the type, but only if it's 1 level deep. List[List[Int]] comes back as List[List[...]]
val fieldType = c.typecheck(q"type T = ${f.tpt}") match {
case x # TypeDef(mods, name, tparams, rhs) => rhs.tpe
}
So, is there a way to recursively typecheck polytypes?
I tried typechecking rhs again but I got The argument types of an anonymous function must be fully known and I'm not sure how to resolve that.
Thanks for taking a look,
Julian
I incorrectly attributed this error to the macro, when in fact there was another underlying macro (a type provider macro) that was failing to provide the proper nested type (in this case the Int).

Strange behavior of Void and intersection types

Is this the expected behavior?
scala> val l: List[Void] = "I'm void".asInstanceOf[String with Void] :: Nil
l: List[Void] = List(I'm void)
Is valid to recognize l as List[Void] and show a String as the first element?
This is completely valid, in a sense, and is not specific to Void. By itself, "I'm void".asInstanceOf[String with Void] will not throw a ClassCastException until you actually try to use it in an way that would require treating it as an invalid type.
val l: List[Void] = "I'm void".asInstanceOf[String with Void] :: Nil
This is valid, because we're lying to the compiler to say "I'm void" is an instance of String with Void, and String with Void is a sub-type of Void, which means we can, for a moment have a List[Void], since List[String with Void] <: List[Void]. However, if we try to access to the head of the List[Void], we expect a Void, which the original String is not.
scala> l.head
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Void
It appears to work at first, because everything has a toString method, so it doesn't need to be treated as anything other than Any.
The same thing happens if we try it with String with Int.
scala> val int = "I'm an Int".asInstanceOf[String with Int]
int: String with Int = I'm an Int
scala> int % 2
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
The String with Int will happily exist until reality sets in, and we realize it isn't actually an Int, and doesn't have a % method.
A slight extension to #m-z's answer to explain why .asInstanceOf[String with Void] doesn't throw. We need to distinguish what exists in Scala and what exists in JVM bytecode. In particular, bytecode doesn't support intersection types like String with Void and therefore can't cast to it. So the bytecode emitted for .asInstanceOf[String with Void] is actually the same as for .asInstanceOf[String]; but when the compiler sees values of this type used as Void, it'll insert additional casts.
This is the same reason why you can't distinguish generic types with different parameters (like List[Int] and List[String]) using isInstanceOf: so far as bytecode is concerned, the types are the same.

scala value toInt is not a member of Any

The println in the following code works (with or without toInt)
println("retweets : ", e.getOrElse("retweets", 0).toInt)
top10Tweets(""+e.get("text").get, e.getOrElse("retweets", 0).toInt)
But when I pass it as an argument of a function (as above), it does not work. It says "value toInt is not a member of Any"
When I remove toInt, it says,
type mismatch;
[error] found : Any
[error] required: Int
e is a Map, as follows,
def tweetDetails(obj: twitter4j.Status) = {
Map(
"id" -> obj.getUser().getId(),
"screenName" -> obj.getUser().getScreenName(),
"text" -> obj.getText(),
"retweets" -> obj.getRetweetCount(),
"mentions" -> obj.getUserMentionEntities().length)
}
signature of top10Tweets,
def top10Tweets(tweets: String, retweet_c: Int, mention_c: Int) = {
}
edit:
Ok, with the new information I would suggest you to create a case class that holds the data instead of using a Map, this way you will preserve type information. I know it is common to use hashes/maps for that in dynamically typed languages, but in statically typed languages as scala data types are the preferred way.
orig:
As I neither know what e is, nor what signature top10Tweets has, I can only assume. But from your code and the error I assume that e is a Map[String, String] and you are trying to get the string representation of an integer for the key "retweets" and convert it to an Int. As a default value you pass in an Int, so the type inferencer infers type Any, because that is the most common super type of String and Int. However Any does not have a toInt method and thus you get the error.
Map("x" -> "2").getOrElse("x", 4).toInt
<console>:8: error: value toInt is not a member of Any
Map("x" -> "2").getOrElse("x", 4).toInt
Either pass in the default value as String, or convert the value of "retweets" to an Int before, if it exists:
e.get("retweets").map(_.toInt).getOrElse(0)
Anyway a little more information would help to give an accurate answer.
Yes because in Map is "string" -> "string" and You did when getOrElse ( else ) string -> int, thats why its Any.
Map("x" -> 2).getOrElse("x", 4).toInt
works fine or You can:
Map("x" -> "2").getOrElse("x", "4").toInt
The same problem bothers me before you could check below
Map("x" -> 2).getOrElse[Int](x, 4)