I am in the process of implementing a simple in-memory, Redis-like KeyValue store and experiencing a compilation failure on the if statement within the for comprehension following piece of code:
/*
Returns the specified elements of the list stored at key. The offsets start and
stop are zero-based indexes, with 0 being the first element of the list to n. */
def lrange(k: keyT, maxIdx:Int, minIdx:Int): List[valT] = {
val l = lookup(k)
//assert(maxIdx >= minIdx && maxIdx <= (l length) && minIdx >= 0, "invalid min or max argument. list size ")
for {
(x: valT, i: Int) <- l zipWithIndex //tried without explicit typing
if i <= maxIdx && i >= minIdx //tried indenting if
} yield x
}
The editor (IntelliJ) shows no errors, but I receive the following build error when attempting to build and run tests.
[INFO] --- scala-maven-plugin:3.3.2:compile (default) # DS4300Project3 ---
[INFO] .../Spring2019/DS4300/scala/DS4300Project3/src/main/scala:-1: info: compiling
[INFO] Compiling 3 source files to .../Spring2019/DS4300/scala/DS4300Project3/target/classes at 1550678144065
[ERROR] .../Spring2019/DS4300/scala/DS4300Project3/src/main/scala/com/rejevichb/homework3/KeyValStore.scala:70: error: illegal start of simple expression
[ERROR] if (i <= maxIdx) && (i >= minIdx) //tried indenting if
[ERROR] ^
[ERROR] one error found
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
Specifically:
KeyValStore.scala:70: error: illegal start of simple expression
Any guidance or insight into what is going wrong here is appreciated, as the solution is not clear to me.
This is exactly the reason why you should use postfix operators with caution.
for {
i <- "a" zipWithIndex
if true
} yield i
is parsed as
for { i <- ("a" zipWithIndex if true) } yield i
because the compiler attempts to interpret zipWithIndex as a binary infix operator, but then runs into if true, which is indeed not a simple expression.
Workarounds:
just don't use postfix ops, use a period:
for {
i <- "a".zipWithIndex
if true
} yield i
add a semicolon to force zipWithIndex to be interpreted as postfix op:
for {
i <- "a" zipWithIndex;
if true
} yield i
and then enjoy your feature warning:
warning: postfix operator zipWithIndex should be enabled
by making the implicit value scala.language.postfixOps visible.
Related
In the book "Scala for the impatient", it says on page 16
In Scala, a { } block contains a sequence of expressions, and the
result is also an expression. The value of the block is the value of
the last expression.
OK, then let's create a block and let the last value of the block be assigned:
scala> val evens = for (elem <- 1 to 10 if elem%2==0) {
| elem
| }
val evens: Unit = ()
I would have expected that evens is at least the last value of the sequence (i.e. 10). But why not?
You need to yield the value, then it's a for expression:
val evens = for (elem <- 1 to 10 if elem % 2 == 0) yield elem
Without that it's just a statement (does not return anything) and is translated to foreach.
P.S.: Of course this will return a collection of all the elements that fulfill the predicate and not the last one.
When in doubt just run it through the typechecker to peek under the hood
scala -Xprint:typer -e 'val evens = for (elem <- 1 to 10 if elem%2==0) { elem }'
reveals
val evens: Unit =
scala.Predef
.intWrapper(1)
.to(10)
.withFilter(((elem: Int) => elem.%(2).==(0)))
.foreach[Int](((elem: Int) => elem))
where we see foreach to be the last step in the chain, and its signature is
def foreach[U](f: A => U): Unit
where we see it returns Unit. You can even do this straight from within the REPL by executing the following command
scala> :settings -Xprint:typer
and now you will get real-time desugaring of Scala expressions at the same time they are interpreted. You can even take it a step further and get at the JVM bytecode itself
scala> :javap -
For-comprehensions are some of the most prevalent syntactic sugar in Scala so I would suggest to drill them as much as possible by perhaps trying to write them at the same time in both their suggared and desugared from until it clicks: https://docs.scala-lang.org/tutorials/FAQ/yield.html
Unit is the exception to the rule stated in your book. Unit basically says "ignore whatever type the block would have returned because I only intended to execute the block for the side effects." Otherwise, in order to get it to typecheck, you'd have to add a unit value to the end of any block that was supposed to return Unit:
val evens = for (elem <- 1 to 10 if elem%2==0) {
elem
()
}
This throwing away of type information is one reason people tend to avoid imperative for loops and similar in Scala.
These are the errors:
scala.scala:13: error: not found: value listaFibonaccisAux
listaFibonaccisAux int 0
^
scala.scala:4: error: illegal start of simple expression
if (int>fibby) fibby :: (listaFibonaccisAux int (n+1))
^
scala.scala:1: error: illegal start of definition
else List()
^
scala.scala:1: error: eof expected but '}' found.
}
^
And here's my code, the errors seem to refer to the simple if/else statements, I already tried wrapping and unwrapping stuff in parens, but it did not help:
def listaFibonaccis (int:Int):List[Int]=
{
listaFibonaccisAux (int, 0)
}
def listaFibonaccisAux (int:Int, n:Int):List[Int]=
{
var fibby = fib n
if (int> fibby)
fibby :: (listaFibonaccisAux (int, (n+1)))
else
List()
}
def fib( n : Int) : Int = n match
{
case 0 | 1 => n
case _ => fib( n-1 ) + fib( n-2 )
}
fib finds a number n in the fibonacci sequence
listaFibonaccisAux creates a list of fibonacci numbers using fib, and stops when the numbers get bigger than int
listaFibonaccis is just a simple wrapper to call the other one with the 0 to start it
it's a plain if/else statement giving me trouble, and that's sad.
The compiler is complaining about the if/else because it doesn't think the line before has ended properly.
Try this: fib(n)
While "infix" (space notation) is sometimes handy...
instance.method(arg) into instance method arg
...it can't be applied as you've tried to use it.
I tried to add an element to a Scala HashMap
val c2 = new collection.mutable.HashMap[String,Int]()
c2 += ("hh",1)
but the above gives me a compile error.
[error] found : String("hh")
[error] required: (String, Int)
[error] c2 += ("hh", 1)
[error] ^
[error] /scalathing/Main.scala:5: type mismatch;
[error] found : Int(1)
[error] required: (String, Int)
[error] c2 += ("hh", 1)
[error] ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 3 s, completed Sep 1, 2016 1:22:52 AM
The pair I'm adding seems to be of the correct type as demanded by the HashMap. Why do I get a compile error?
The += operator is overloaded to work with variadic arguments. Therefore when the compiler sees c2 += ("hh", 1) it interprets that as two arguments being passed in, one of which is "hh" and the other of which is 1. You can fix that either by using the -> operator, i.e. c2 += ("hh" -> 1) or enclosing the tuple in another series of parantheses, i.e. c2 += (("hh, 1)).
Slightly gory details below as requested in the comments.
As for how all this works, in the case of mutable collections such as HashMap, += is simply an ordinary method called with operator syntax (i.e. spaces instead of a .) or "infix notation" as the Scala community calls it, as any method in Scala can be. It is provided by the Growable trait which mutable collections mix in. You can see on the documentation for Growable both the single argument += method and the variadic method. In other words the following code would have also worked.
c2.+=(("hh", 1))
Not all +=s are created equal however. += commonly shows up in vars as well. Although it can be called with method syntax ., it's magic syntax sugar implemented directly by the Scala compiler. In particular any nonalphanumeric name followed by an = gets desugared. x $NAME= y becomes x = x.$NAME(y). In this case $NAME= is variadic if and only if $NAME is variadic.
var i = 0
i += 1
i.+=(1) // Also compiles
case class SuperInt(raw: Int) {
def &*&(x: SuperInt) = SuperInt(raw + x.raw)
}
var x = SuperInt(1)
x &*&= SuperInt(1) // This works
x.&*&=(SuperInt(1)) // As does this
x &*&= (SuperInt(1), SuperInt(1)) // Does not compile because &*& is not variadic
Taking this example right from the User Guide of Scalacheck:
scala> import org.scalacheck.Prop.{forAll, BooleanOperators}
scala> val propTrivial = forAll { n: Int =>
| (n == 0) ==> (n == 0)
| }
scala> propTrivial.check
! Gave up after only 4 passed tests. 500 tests were discarded.
I'd like to see the actual value that caused the test to fail. I have a similar test case in my project that produces a similar (useless) message.
I tried adding the verbosity option to sbt:
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "5")
and also tried calling the property like
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "5")
But neither works. I cannot get any more output.
In this case, no value caused the test to fail, but ScalaCheck gave up because it could not find enough values to try. This is why it says 500 tests were discarded - it's not a useless error message, it's just saying that the generated values did not match your n == 0 precondition.
If you try with a test that does fail, it will tell you the failing test:
scala> forAll { n: Int => n > 0 }
res0: org.scalacheck.Prop = Prop
scala> res0.check
! Falsified after 2 passed tests.
> ARG_0: -2147483648
I'm wondering if there is something that can give me results similar to Groovy's nice power assert statement.
> assert ["1", '2']*.size() == [2, 3]
Result: Assertion failed:
assert ["1", '2']*.size() == [2, 3]
| |
[1, 1] false
AFAIK there is no support for such thing neither in language, nor in
scalatest, which I'm currently using.
But maybe someone can suggest some side library doing that? It's a pet project, so experimental and not well-supported libs are fine.
EDIT: I know about matchers (scalatest ones, or even plain-java hamcrest matchers). I find them verbose to write and that their output lacks details.
The example above shows intermediate computation steps, facilitating detection of errors. It shows you what's wrong with tested code with more details.
I expect, that introducing such behaviour will require having information about expression AST at runtime. But I suppose, that this information can be "baked" compile time with usage of macroses.
I.e. if we have expression assert a + b == c scala (or some macro extension I'm looking for) can rewrite it to something like:
if (!(a + b == c)) {
// detailed message is
// compute a
// compute b
// compute a + b
// compute c
// compute a + b == c
// Make it pretty.
throw new AssertionFailedException(prettyDetailedMessage)
}
So I'm looking if it's already implemented, and if yes - where.
In ScalaTest, you can use DiagrammedAssertions.
See http://www.scalatest.org/release_notes/2.2.0#diagrammedAssertions
This is based on Expecty which is a macro-based implementation of Spock's power assertions. See https://github.com/pniederw/expecty
Specs2 matchers do a good job with error messages:
class Specs2Specification extends Specification {
"specs2 assertion" should {
"fail" in {
List("1", "2").map(_.length) must_=== List(2, 3)
}
}
}
run output:
[info] Specs2Specification
[info]
[info] specs2 assertion should
[error] x fail
[error] List(1, 1) is not equal to List(2, 3)
[info]
[error] Added (2)
[error] 1
[error] 1
[info]
[error] Missing (2)
[error] 2
[error] 3
or
List("1", "2").map(_.length) must contain(exactly(2, 3)).inOrder
which produces
[error] x fail
[error] the values 2, 3 are not in order
There are lots of them and you can create custom ones.
Your groovy code snippet is literally translated to the following scala code (given that you're already using scalatest):
assert((List("1", "2") map (_.length)) === List(2, 3))
It produces the following error message:
*** FAILED ***
List(1, 1) did not equal List(1, 3)