Apparently Range has a method that checks if it contains a value of type Any. I understand that it is from SeqLike, but causes some problems.
For instance, i was matching hours from joda.DateTime:
DateTime.now match {
case d if 0 to 12 contains d.hourOfDay() => ...
Here d.hourOfDay() returns DateTime.Property, not Int, but code still compiles, because of contains(elem: Any). Is there any way to check for such calls at compile time?
You can use Scalaz's typesafe equals (===) in conjunction with exists method on TraversableOnce.
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> 1 to 5 exists { _ === 2 }
res1: Boolean = true
scala> 1 to 5 exists { _ === "Hullo" }
<console>:14: error: type mismatch;
found : java.lang.String("Hullo")
required: Int
1 to 5 exists { _ === "Hullo" }
^
You can pimp Range to add a type-safer contains method:
class SafeRange( range: Range ) {
def safeContains( i: Int ) = range contains i
}
object SafeRange {
implicit def safer( range: Range ) = new SafeRange( range )
}
Import the implicit and call safeContains on any range instance:
scala> import SafeRange._
import SafeRange._
scala> (0 until 10) safeContains 3
res2: Boolean = true
scala> (0 until 10) safeContains 100
res3: Boolean = false
scala> (0 until 10) safeContains "foo"
<console>:18: error: type mismatch;
found : java.lang.String("foo")
required: Int
(0 until 10) safeContains
Based on the scaladocs for Range it looks like there's not a better Range method you could use. Your options seem to be
Use an explicit type signature:
case d if 0 to 12 contains (d.hourOfDay(): Int) => ...
Make your own method:
def containsInt(r: Range, i: Int) = ...
This seems to be a holdover from Java equals being pre-generics, and is only one of the inconveniences this fact causes for Scala.
Related
I mistakenly concatted a string with an Option[String] while coding in scala.
I expected as a strongly typed language, scala would not allow me to do such operation.
This is what I tried.
This works
scala> val a:String = "aaa"
val a: String = aaa
scala> val b:Option[String] = Some("bbbb")
val b: Option[String] = Some(bbbb)
scala> a + b
val res0: String = aaaSome(bbbb)
scala> val c:Option[String] = None
val c: Option[String] = None
scala> val d = a + c
val d: String = aaaNone
scala> val e = 1
val e: Int = 1
scala> a + e
val res2: String = aaa1
while this does not work
scala> val f:Option[String] = Some("ffff")
val f: Option[String] = Some(ffff)
scala> val g:Option[String] = None
val g: Option[String] = None
scala> f + g
^
error: type mismatch;
found : Option[String]
required: String
Why does scala allow such behavior? Dynamically typed languages like python will stop me from adding strings to int types, None types or any type other than strings. Curious if this design is intentional? If so why?
Scala contains an implicit class any2stringadd in it's Predef package. This class is responsible for these concatenation operations.
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
What it means is that, by default, scope contains a method + which can concatenate value of any type A with string by converting value of this type to string via String.valueOf(...).
I can't speak of design choices, I agree that this might be an unexpected behavior. The same applies to Scala's native == method. For example, this code compiles just ok: Some("a") == "b". This can lead to nasty bugs in filtering and other methods.
If you want to eliminate this behavior I suggest you take a look at https://typelevel.org/cats/ library, which introduces different typeclasses that can solve this problem.
For example, for string concatenation you can use Semigroup typeclass (which has tons of other useful use-cases as well):
import cats.Semigroup
Semigroup[String].combine("a", "b") // works, returns "ab"
Semigroup[String].combine("a", Some("b")) // won't work, compilation error
This looks tedious, but there is a syntactic sugar:
import cats.implicits._
"a" |+| "b" // works, returns "ab"
"a" |+| Some("b") // won't work, compilation error
// |+| here is the same as Semigroup[String].combine
The same thing applies to == method. Instead you can use Eq typeclass:
import cats.implicits._
"a" == Some("b") // works, no error, but could be unexpected
"a" === Some("b") // compilation error (Cats Eq)
"a" === "b" // works, as expected
"a" =!= "b" // same as != but type safe
Suppose I want to create a NonZero type so that my integer division function is total:
def div(numerator: Int, denominator: NonZero): Int =
numerator / denominator.value
I can implement this by creating a NonZero class with a private constructor:
class NonZero private[NonZero] (val value : Int) { /*...*/ }
And a helper object to hold a Int => Option[NonZero] constructor, and an unapply so it can be used in match expressions:
object NonZero {
def build(n:Int): Option[NonZero] = n match {
case 0 => None
case n => Some(new NonZero(n))
}
def unapply(nz: NonZero): Option[Int] = Some(nz.value)
// ...
}
build is fine for runtime values, but having to do NonZero.build(3).get for literals feels ugly.
Using a macro, we can define apply only for literals, so NonZero(3) works, but NonZero(0) is a compile-time error:
object NonZero {
// ...
def apply(n: Int): NonZero = macro apply_impl
def apply_impl(c: Context)(n: c.Expr[Int]): c.Expr[NonZero] = {
import c.universe._
n match {
case Expr(Literal(Constant(nValue: Int))) if nValue != 0 =>
c.Expr(q"NonZero.build(n).get")
case _ => throw new IllegalArgumentException("Expected non-zero integer literal")
}
}
}
However this macro is less useful than it could be, as it only allows literals, not compile-time constant expressions:
final val X: Int = 3
NonZero(X) // compile-time error
I could pattern match on Expr(Constant(_)) in my macro, but then what about NonZero(X + 1)? I'd rather not have to implement my own scala expression evaluator.
Is there a helper or some easy way to determine if the value of an expression given to a macro is known at compile time (what C++ would call constexpr)?
If you ignore macros, then in Scala, only types exist at compile time, and only values exist at runtime. You can do type-level tricks to encode numbers as types at compile time, e.g. Type Level Programming in Scala
Here's a simplified version of the above Peano arithmetic example. First, we define a typeclass that shows how some type can convert to an integer.
#annotation.implicitNotFound("Create an implicit of type TValue[${T}] to convert ${T} values to integers.")
final class TValue[T](val get: Int) extends AnyVal
Then, we define the Peano 'zero' type and show how it can convert to a runtime integer 0:
case object TZero {
implicit val tValue: TValue[TZero.type] = new TValue(0)
}
Then the Peano 'successor' type and how it can convert to a runtime integer 1 + previous value:
case class TSucc[T: TValue]()
object TSucc {
implicit def tValue[TPrev](implicit prevTValue: TValue[TPrev]): TValue[TSucc[TPrev]] =
new TValue(1 + prevTValue.get)
}
Then test safe division:
object Test {
def safeDiv[T](numerator: Int, denominator: TSucc[T])(implicit tValue: TValue[TSucc[T]]): Int =
numerator / tValue.get
}
Trying it out:
scala> Test.safeDiv(10, TZero)
<console>:14: error: type mismatch;
found : TZero.type
required: TSucc[?]
Test.safeDiv(10, TZero)
^
scala> Test.safeDiv(10, TSucc[String]())
<console>:14: error: Create an implicit of type TValue[String] to convert String values to integers.
Test.safeDiv(10, TSucc[String]())
^
scala> Test.safeDiv(10, TSucc[TZero.type]) // 10/1
res2: Int = 10
scala> Test.safeDiv(10, TSucc[TSucc[TZero.type]]) // 10/2
res3: Int = 5
As you can imagine though, this can get verbose fast.
som-snytt's advice to check out ToolBox.eval led me to Context.eval, which the helper I'd been wanting:
object NonZero {
// ...
def apply(n: Int): NonZero = macro apply_impl
def apply_impl(c: Context)(n: c.Expr[Int]): c.Expr[NonZero] = try {
if (c.eval(n) != 0) {
import c.universe._
c.Expr(q"NonZero.build(n).get")
} else {
throw new IllegalArgumentException("Non-zero value required")
}
} catch {
case _: scala.tools.reflect.ToolBoxError =>
throw new IllegalArgumentException("Unable to evaluate " + n.tree + " at compile time")
}
}
So now I can pass NonZero.apply constants and expressions made with constants:
scala> final val N = 3
scala> NonZero(N)
res0: NonZero = NonZero(3)
scala> NonZero(2*N + 1)
res1: NonZero = NonZero(7)
scala> NonZero(N - 3)
IllegalArgumentException: ...
scala> NonZero((n:Int) => 2*n + 1)(3))
IllegalArgumentException: ...
While it'd be nice if eval could handle pure functions like the last example above, this is good enough.
Embarrassingly, reviewing and retesting my earlier code from the question proved that my original macro handled the same expressions just as well!
My assertion that final val X = 3; NonZero(X) // compile-time error was just wrong, since all the evaluation was being handled by inlining (as som-snytt's comment implied).
I think Scala is the first language I've come across where the following doesn't work:
true + true
// Name: Compile Error
// Message: <console>:32: error: type mismatch;
// found : Boolean(true)
// required: String
// true + true
// ^
// StackTrace:
Can someone explain this error? Why on earth are Strings getting involved?
Also, what is the canonical way to accumulate Boolean values in Scala? Do I need to convert them to Int / is there a way to overload + for it to work as expected?
The reason "String" is getting involved is the implicit conversion from Boolean to String which allows writing expressions like "text " + true (which evaluates to text true).
You can create a similar implicit conversions from Boolean to Int, as suggested in here:
implicit def bool2int(b:Boolean) = if (b) 1 else 0
Do note that now you'll have two implicit conversions from Boolean, which might get a bit tricky:
// without bool2int implicit conversion:
scala> true + true
<console>:12: error: type mismatch;
found : Boolean(true)
required: String
true + true
^
// conversion to String works:
scala> true + " text"
res1: String = true text
scala> "text " + true
res2: String = text true
// now we add bool2int:
scala> implicit def bool2int(b:Boolean) = if (b) 1 else 0
bool2int: (b: Boolean)Int
// which makes this work:
scala> true + true
res3: Int = 2
// BUT! now this conversion will be picked up because Int has a `+` method:
scala> true + " text"
res4: String = 1 text // probably not what we intended!
// This would still work as before:
scala> "text " + true
res5: String = text true
You can use implicit conversions. When the compiler find the expression is of wrong type, it will look for an implicit function.
On creating the implicit function, the compiler will call it each time when Boolean is found but Int is required in the context.
import scala.language.implicitConversions
implicit def toInt(v:Boolean):Int = if (v) 1 else 0
To see how it works, let's add a print statement
implicit def toInt(v:Boolean):Int = {
println (s"Value: $v")
if (v) 1 else 0
}
Output:
scala> val x = false
x: Boolean = false
scala> val y = true
y: Boolean = true
scala> x
res6: Boolean = false // No implicit function called.
scala> x + y
Value: false // Implicit function called for x.
Value: true // Implicit function called for y.
res5: Int = 1
So this function is called when Boolean is found and Int is required in the context, but not otherwise.
In addition to implicit conversion, you can also convert the boolean to a wrapped type which supplies an addition operator.
class WrappedBoolean(bool: Boolean) {
val value = if (bool) 1 else 0
def +(other: Boolean) = (if (other) 1 else 0) + value
}
implicit def boolean2WrappedBoolean(bool: Boolean) = new WrappedBoolean(bool)
I would like to experiment foldleft on Futures. I start with a simple/dumb example as a worksheet:
import scala.concurrent._
import ExecutionContext.Implicits.global
val list = (1 to 10).toList
def doubleFuture(i: Int) = Future { println(i);i }
val twoFutures = list map doubleFuture //returns List[Future[Int]]
val res = (twoFutures foldLeft(List[Int]())
((theList:List[Int], aFuture:Future[Int]) =>
{
theList :+ 1
}))
The compiler is not happy with it and indicates:
Error:(11, 48) type mismatch;
found : (List[Int], scala.concurrent.Future[Int]) => List[Int]
required: Int
((theList:List[Int], aFuture:Future[Int]) =>
^
I don't understand why the second argument of the foldleft function is not of type Future[Int] as twoFutures is of type List[Future[Int]].
Could you explain what's wrong ?
thanks!
You need to use a period (.) after the list to tell compiler that the block or parentheses after the parentheses be bound to foldLeft instead of twoFutures:
import scala.concurrent._
import ExecutionContext.Implicits.global
object FutureList {
def main(args: Array[String]) {
val list = (1 to 10).toList
def doubleFuture(i: Int) = Future { println(i); i }
val twoFutures = list map doubleFuture //returns List[Future[Int]]
val res = twoFutures.foldLeft(List[Int]())(
(theList, aFuture) => theList :+ 1)
println(res)
// Uncomment these lines to unfold the mystery
// val theList = List[Int]()
// val aFuture = Future[Int](0)
// twoFutures((theList: List[Int], aFuture: Future[Int]))
}
}
To explain what it means, you can uncomment the three commented lines above. You will see the same compilation error as you will get without the period after twoFutures list:
Multiple markers at this line
- type mismatch; found : (List[Int], scala.concurrent.Future[Int]) required:
Int
- type mismatch; found : (List[Int], scala.concurrent.Future[Int]) required:
Int
Output
2
4
1
3
5
6
8
10
7
9
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
This the solution use the dot notation on the fold left i.e.
val res = (twoFutures.foldLeft(List[Int]())
((theList:List[Int], aFuture:Future[Int]) =>
{
theList :+ 1
}))
Inspired by this, I was wondering if we can have type-safe string interpolations in Scala (maybe using macros)?
For example, I want to have something like this
def a[A] = ???
val greetFormat = f"Hi! My name is ${a[String]}. I am ${a[Int]} years old"
greetFormat.format("Rick", 27) // compiles
//greetFormat.format("Rick", false) // does not compile
//greetFormat.format(27, "Rick") // does not compile
//greetFormat.format("Rick", 27, false) // does not compile
//greetFormat.format("Rick") // does not compile or is curried?
The f string interpolator is already implemented with a macro.
This can be demonstrated inside of the REPL:
scala> val b = "not a number"
b: String = not a number
scala> f"$b%02d"
<console>:9: error: type mismatch;
found : String
required: Int
f"$b%02d"
^
Just wrap it in a function.
def greet(name: String, age: Int) = s"Hi! My name is $name. I am $age years old"
You can supply implicits to the f-interpolator:
scala> case class A(i: Int)
defined class A
scala> implicit def atoi(a: A): Int = a.i
warning: there were 1 feature warning(s); re-run with -feature for details
atoi: (a: A)Int
scala> f"${A(42)}%02d"
res5: String = 42
See also Travis Brown's examples and solution for using regex group names in extractions. It took me about a minute to steal that great idea.
"a123bc" match {
case res # xr"(?<c>a)(?<n>\d+)(?<s>bc)" => assert {
res.c == 'a' && res.n == 123 && res.s == "bc"
}
}
For the record, on the composition side, I would like:
val a = A(Rick, 42)
val greeter = f"Hi! My name is $_. I am ${_}%d years old"
greeter(a, a)
But it was deemed too much for the poor underscore. You'll have to write the function as in the other answer.
Your form, in which your macro sees "${a[Int]}" and writes a function with an Int param, doesn't look hard to implement.
Other features of the f-interpolator include other static error checking:
scala> f"$b%.02d"
<console>:19: error: precision not allowed
f"$b%.02d"
^
and support for Formattable:
scala> val ff = new Formattable { def formatTo(fmtr: Formatter, flags: Int, width: Int, precision: Int) = fmtr.format("%s","hello, world") }
ff: java.util.Formattable = $anon$1#d2e6b0b
scala> f"$ff"
res6: String = hello, world
A quick macro might emit (i: Int) => f"${ new Formattable {...} }".