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)
Related
I want to write a method that returns true if an Option[Int] contains a specific value and false otherwise. What is the idiomatic way of doing this?
var trueIf5(intOption: Option[Int]): Boolean {
intOption match {
case Some(i) => i == 5
case None => false
}
}
This above solution clearly works, but the Scala docs label this approach as less-idiomatic.
Is there some way I can do the same thing using map, filter, or something else?
I got this far, but it only changes the problem to "Return true if Option contains true", which is effectively the same as "Return true if Option contains 5".
var trueIf5(intOption: Option[Int]): Boolean {
intOption.map(i => i == 5).???
}
Since you're testing whether it contains a value:
scala> Some(42) contains 42
res0: Boolean = true
Don't neglect your -Xlint:
scala> Option(42).contains("")
res0: Boolean = false
scala> :replay -Xlint
Replaying: Option(42).contains("")
<console>:12: warning: a type was inferred to be `Any`; this may indicate a programming error.
Option(42).contains("")
^
res0: Boolean = false
Those built-in warnings aren't as effective with universal equality:
scala> Option(42).exists("" == _) // no warning
res1: Boolean = false
intOption.exists(_ == 5)
The doc
Why has no one suggested:
intOption == Some(5)
I have two functions that take one argument, a String. I was to apply either one or the other based on some condition. This is what I attempted:
def foo(s: String) = { ... }
def bar(s: String) = { ... }
(if (condition) foo else bar)("baz")
But I get an error like this:
<console>:10: error: missing arguments for method foo;
follow this method with `_' if you want to treat it as a partially applied function
(if (true) foo else bar)("baz")
^
I tried writing foo_ but of course I got error: not found: value foo_. What's the correct way to express this idiom in Scala?
You need a space between the method name and the underscore. This works fine:
def foo(s: String) = s + "-FOO"
def bar(s: String) = s + "-BAR"
val condition = true
(if (condition) foo _ else bar _)("baz")
// res0: String = baz-FOO
The underscore after the method name tells Scala that you want to want to pass the method as a higher-level function. From what I understand, this is a way to disambiguate whether you want to pass a method as a function or pass the result of a method with no arguments. For example:
def f = 1
val x = Some(f)
What should the type of x be? Will it be Some[Int] or Some[()=>Int]? It should default to the former, but if you want the latter you can use the underscore notation:
val y = Some(f _)
You have to deal with all this underscore nonsense because Scala methods aren't functions. If you declare foo and bar as functions rather than methods then your original code works as-is:
val foo = (s: String) => s + "-FOO"
val bar = (s: String) => s + "-BAR"
val condition = false
(if (condition) foo else bar)("baz")
// res1: String = baz-BAR
There are several things I want to mention:
def foo(s: String) = { ... }
def bar(s: String) = { ... }
foo and bar are not functions, there are just normal method. Also, def f = 3 is also a method not function.
(if (condition) foo else bar)("baz") obviously, this statement need foo and bar to be a function because of ("baz") argument.
as #wendao mentioned to use _ to change method to function. I think the simplest solution is to define foo and bar as a function.
def foo: String => String = { value =>
"Hi " + value
}
def bar: String => String = { value =>
"farewell " + value
}
val x: Some[String => String] = Some(foo)
(if (true) foo else bar)("John") // Hi John
It doesn't know that what you actually want to return a function, you'd have to tell it that what you want is a by-name parameter:
def foo(x : String) = x //> foo: (x: String)String
def bar(x : String) = x //> bar: (x: String)String
val condition = true //> condition : Boolean = true
val result : String => String = if (condition) foo else bar
//> result : String => String = <function1>
result("something") //> res0: String = something
This is a little more absurd:
scala> var b = true
b: Boolean = true
scala> def f(s: String) = s"f+$s"
f: (s: String)String
scala> def g(s: String) = s"g+$s"
g: (s: String)String
scala> import Function._ ; import PartialFunction._
import Function._
import PartialFunction._
scala> unlift(condOpt(_: String) { case s if b => f(s) }) applyOrElse ("hi", g)
res0: String = f+hi
scala> b = false
b: Boolean = false
scala> unlift(condOpt(_: String) { case s if b => f(s) }) applyOrElse ("hi", g)
res1: String = g+hi
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 {...} }".
When trying to define a generic method def f[T] (x:T) = x + 1 Scala gives below error
<console>:8: error: type mismatch;
found : Int(1)
required: String
def f[T] (x:T) = x + 1
^
The question is why Scala is assuming that it should be a String?
What if we want a function that can do +1 to Int, Char and String. I know I can do something like below, but it would work only on Int and Char.
def f[T <% Int](x:T) = x + 1
So what's the reason of this error and how to handle it in generic way.
The question is why Scala is assuming that it should be a String?
I can't guarantee this analysis, but it appears that Scala is applying the Predef.any2stringadd() implicit conversion to x, in an attempt to turn it into something that supports the + operator.
Here's a variant that will compile, and which demonstrates that implicit conversion:
scala> def f(x:Any) = { x + "1" }
f: (x: Any)String
scala> f("foo")
res3: String = foo1
scala> f(123)
res0: String = 1231
scala> f(classOf[String])
res2: String = class java.lang.String1
What if we want a function that can do +1 to Int, Char and String.
What does it mean to add 1 to any of these values?
If you simply want to invoke the + operator, then you need to use the match operator to select different behaviors depending on the actual type. This is because, while the name + is used for both, there's no common behavior between strings and numbers.
On the other hand, perhaps you want to deal with numbers that can be provided as either strings or numeric values (in which case, why Char?). To make that work, you need an implicit function that converts Any to a number.
scala> implicit def any2int(x:Any) : Int = { x.toString.toInt }
warning: there were 1 feature warning(s); re-run with -feature for details
any2int: (x: Any)Int
scala> def f(x:Any) : Int = { x + 1 }
f: (x: Any)Int
scala> f(123)
res0: Int = 124
scala> f("123")
res1: Int = 124
The return type of the function will always be Int as I assume so it should be that the compiler choose Int but why String.
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.