How to wrap Scala Infix expressions within long lines in IntellijIDEA? - scala

I am using the .editorconfig file in order to configure my IntellijIDEA IDE to reformat Scala code in a specific way.
I specified a right margin, and specified for it not to be exceeded, however, this has a bad impact on long lines that have an Infix expression, which breaks the code.
Everything else is reformatted correctly.
Specific editorconfig configuration :
max_line_length = 100
ij_scala_call_parameters_wrap = normal
ij_scala_method_call_chain_wrap = normal
ij_scala_variable_annotation_wrap = normal
ij_scala_wrap_long_lines = true
The line i am trying to reformat :
override val fooBarJognDoeLongVar: List[fooBarJognDoeLongVarType] = DoThisDoThat() :: DoThisDoThatAgain() :: DoThisDoThatAgainOneMoreTime() :: DoThisDoThatAgainOneMoreTimeOnceAgain() :: Nil
When it's reformatted, it looks like this :
override val fooBarJognDoeLongVar: List[fooBarJognDoeLongVarType] = DoThisDoThat() :: DoThisDoThatAgain() :: DoThisDoThatAgainOneMoreTime()
:: DoThisDoThatAgainOneMoreTimeOnceAgain() :: Nil
And that breaks the expression apparently. This line should be broken like this :
override val fooBarJognDoeLongVar: List[fooBarJognDoeLongVarType] = DoThisDoThat() :: DoThisDoThatAgain() :: DoThisDoThatAgainOneMoreTime() ::
DoThisDoThatAgainOneMoreTimeOnceAgain() :: Nil
With the :: kept on the first line, before the line return.
Any ideas on how to do this please ? Of course, using the editorconfig file.

Related

Hard time using Scala Worksheets in IntelliJ

I'm following the course Functional Programming Principles in Scala
but I am experiencing a lot of issues when using Scala Worksheets in IntelliJ to make quick tests.
For example, I have set up a new Scala project where I have created a package object called lecture5 (it's in the file) src/main/scala/lecture5/package.scala
The content of the file is:
package object lecture5 {
def last[T](xs:List[T]): T = xs match {
case List() => throw new Error("empty list")
case List(x) => x
case x :: y => last(y)
}
/* init should return all elements but last */
def init[T](xs: List[T]): List[T] = xs match {
case List() => throw new Error("List is empty")
case List(x) => List[T]()
case y :: ys => y :: init(ys)
}
def concat[T](xs: List[T], ys: List[T]): List[T] = xs match {
case List() => ys
case z:: zs => z :: concat(zs, ys)
}
}
In the worksheet I have the following:
import lecture5._
val x = List("a","b","c")
val xs = List("a","b")
val ys = List("c")
last(x)
init(x)
concat(xs, ys) == x
In the settings for the worksheet I checked Interactive Mode, Make project before run and use Run Type = REPL (Plain doesn't work for some reason) and Compiler profile = Default.
When I click on the "play" button to run the worksheet the functions init and last work, but for the function concat I get error:
Error:(13, 9) not found: value concat
concat(xs, ys) == x
Why is concat not found?
Note that if I use the Scala console from within the sbt-shell and execute the same commands then everything works.
How can I configure IntelliJ to work with a Worksheet without issues?
I replicated the issue on IntelliJ 2019.1.2, Scala Plugin 2019.1.8. No form of building the project before running the worksheet worked. Package object was finally successfully imported after Invalidate Caches / Restart.... A workaround that seems to work for me without restarting is to use Scala Scratch file instead of Scala Worksheet:
Right click project | New | Scratch file | Scala
Possibly related to issue SCL-12890

Scala - ReadLines with linenumber awareness

I am trying to read from a (\n separated) file in Scala. I would like to treat the last and second to last line of the file differently from the others. how do I achieve this? As far as I know, LineNumberedReader which is a BufferedReader can get the current line number but is not aware of the total number of lines(to process the last line differently). Desired:
var aLine = lineNumberedReader.readLine
while (aLine != null) {
val currentLineNum: Int = lineNumberedReader.getLineNumber
if (currentLineNum == total_line_count - 1) {
do_this // To know if its the last line/ second to last, I need the total_line_count available in hand. which I (maybe incorrectly?) believe needs a file iteration by itself
} else {
do_that
}
aLine = lineNumberedReader.readLine
}
Same issues exist with Scala io.Source. it requires a minimum of two file iterations. Any ideas/APIs using which this can be achieved using a single iteration?
EDIT: Extending the question to include case for second-to-last line
Something like this perhaps?
val lines = Source
.fromFile("filename")
.getLines
lines.foreach { line =>
if (lines.hasNext) doThis(line) else doThat(line)
}
For one before last it gets a bit more involved:
val it = Source.fromFile("filename").getLines.sliding(2)
it.foreach {
case x :: _ if it.hasNext => doCommon(x)
case x :: y :: _ => doBeforeLast(x); doLast(y)
}
Or in general for N-before-last (before you edit your question again :D):
val it = Source.fromFile("filename").getLines.sliding(n+1)
it.foreach {
case x :: _ if it.hasNext => doCommon(x)
case tail => doNBeforeLast(tail)
}

How to add a set to a list of Sets in Scala

I have a list of Sets and would like to add a Set to it. My assumption would be that I have to add to a list using the :: operator.
My code:
// attribute_sets is a tuple (String, Set[String])
val listsOfSets: List[Set[String]] = attribute_sets
.foldLeft(List[Set[String]])((acc, attribute_set) => {
acc :: (attribute_set._2 + attribute_set._1)
})
IntelliJ tells that there is a type mismatch (expected String, actual Set[String]), when I try to add to acc.
You can use :: only to add an element at the beginning of a list:
x :: list
To add something at the end of the list, you have to use the :+ operator:
list :+ x
The type of attribute_set._2 + attribute_set._1 is String, since the + operation used is the one of String.
Hence the :: operation cannot resolve, since it's used to concatenate sets, not to add a String to a Set.
So you either have to change the + or the :: operator depending on the actual result you expect.

"undefined value, reference not allowed" workaround

I'm looking for some clarification on the compiler error message The value of xyz is undefined here, so reference is not allowed., together with the do-notation. I did not manage to generalize the example enough, all I can give is the concrete example where I stumbled upon this behaviour. Sorry for that.
Using purescript-parsing, I want to write a parser which accepts nested multiline-comments. To simplify the example, each comment starts with (, ends with ) and can contain either an a, or another comment. Some examples: (a) and ((a)) accepted, (), (a or foo get rejected.
The following code results in the error The value of comment is undefined here, so reference is not allowed. on the line content <- string "a" <|> comment:
comment :: Parser String String
comment = do
open <- string "("
content <- commentContent
close <- string ")"
return $ open ++ content ++ close
commentContent :: Parser String String
commentContent = do
content <- string "a" <|> comment
return content
I can get rid of the error by inserting a line above content <- string "a" <|> comment which as far as I understand it does not change the resulting parser at all:
commentContent :: Parser String String
commentContent = do
optional (fail "")
content <- string "a" <|> comment
return content
The questions are:
What is happening here? Why does the extra line help?
What is a non-hacky way to get the code to compile?
The reason the second case works becomes more apparent if you desugar the do manually:
commentContent :: Parser String String
commentContent =
optional (fail "") >>= \_ ->
string "a" <|> comment >>= \content ->
return content
When defined this way, the comment reference is inside a lambda, so therefore is not evaluated during the definition of commentContent.
As for the non-hacky solution, it would involve some use of fix I imagine. fix allows you to define recursive parsers like:
myParser = fix \p -> do
... parser definition ....
Where p is a reference to myParser that you can use within itself. As for the case here where you have mutually recursive parsers, I'm not exactly sure how best to solve it with fix, there are a few options I can think of, but none are particularly elegant. Perhaps something like this:
parens :: Parser String String -> Parser String String
parens p = do
open <- string "("
content <- p
close <- string ")"
return $ open ++ content ++ close
comment :: Parser String String
comment = parens commentContent
commentContent :: Parser String String
commentContent = fix \p -> do
content <- string "a" <|> parens p
return content
It might be easier to use a trick similar to the strange do case and insert a Unit -> in front of one of the parsers, so you can delay the recursive reference until the Unit value is provided, something like:
comment :: Parser String String
comment = do
open <- string "("
content <- commentContent unit
close <- string ")"
return $ open ++ content ++ close
commentContent :: Unit -> Parser String String
commentContent _ = do
content <- string "a" <|> comment
return content

scala sys.process_ illegal start of simple expression error

I got an error "illegal start of simple expression" in scala while trying to do this:
def test() = {
val h = "ls"!
if (h != 0)
println("error")
}
this is the error
[error] if (h != 0)
[error] one error found
[error] (compile:compileIncremental) Compilation failed
Can anyone tell me what I did wrong?
Scala compiler is getting confused here because you are calling ! as a postfix operator. It can't figure out which version to use and where to place the implied semicolon. You can either add a semicolon like this:
def test() = {
val h = "ls"!;
if (h != 0)
println("error")
}
or call it as a method:
def test() = {
val h = "ls".!
if (h != 0)
println("error")
}
or add a new line:
def test() = {
val h = "ls"!
if (h != 0)
println("error")
}
Best practice to use a postfix operator is to use dot . operator. You should invoke it like val h = "ls".!
It makes code less ambiguous as semicolon is optional in Scala and compiler might treat it as an infix notation.
From Scala doc:
This style is unsafe, and should not be used. Since semicolons are
optional, the compiler will attempt to treat it as an infix method if
it can, potentially taking a term from the next line.
For full reference see this link: Suffix Notation. This post may also be helpful Scala's "postfix ops".