IntelliJ Idea: Align on Colon for Scala Case Class Members? - scala

Is there a way to prevent Idea from reformatting multiline case class members?
Intended result
(position of the colon is not so important, but the alignment of the type annotations would be great)
case class ApiError(
timestamp : LocalDateTime,
status : Int,
code : Option[String],
message : String
)
I've spent some time in the Code Styling settings for Scala and am stuck with code styling like this:
case class ApiError(
timestamp: LocalDateTime,
status: Int,
code: Option[String],
message: String
)
I am okay with cluttering the code with #formatter:off, but it would be awesome if there was a better way.

Just like Madoc commented, those kind of formats may lead to huge changes when adding a new line. Also, while pretty in small cases, it doesn't scale, and it seems we are programming in tables...

my IntelliJ IDEA version is ultimate 2019.3
step 1. Prefrences -> Editor -> Code Style -> Scala
step 2. Wrapping and Braces -> Method declaration parameters, then
check "Align paramenter types in multiline declarations"
I tried the example in question, it was
case class ApiError(
timestamp: LocalDateTime,
status : Int,
code : Option[String],
message : String
)
if you uncheck "Align when multiline", it is consistent with intended result.
case class ApiError(
timestamp: LocalDateTime,
status : Int,
code : Option[String],
message : String
)

Related

Reading data into custom case classes in Doobie

Let's say I have a case class X(id: Int, name: String, age: Int) and some function (in my case, withUniqueGeneratedKeys in doobie) that returns X. If I have already defined X I am good.
But in my case the core data structure is something like:
case class XData(name: String, age: Int)
case class MaterializedX(id: Int, element: XData)
And of course I could write a line like case class X(id: Int, name: String, age: Int) to create X but it would be duplication of logic - whenever something about XData changes, I'd have to change the code for X as well. Intuitively, it feels like there should be a way to derive X from XData and MaterializedX. This transformation might mean some code, but it would save me lots of future work because I have many item types beyond X.
How could this be done? Happy to hear other approaches.
I am using Scala 3.1.2 in case this matters. Thank you!
Edit: Changed title per helpful comment, to make this question easier to understand.
I think you should be more clear about the question, I mean what title says is almost completely different from your question description (and what you might be actually looking for). Anyway, in my assumptions, what you need is probably a custom Read[MaterializedX] and Write[MaterializedX], as follows:
implicit val pointMaterializedX: Read[MaterializedX] =
Read[(Int, String, Int)]
.map {
case (id, name, age) => MaterializedX(id, XData(name, age))
}
implicit val pointWrite: Write[MaterializedX] =
Write[(Int, String, Int)]
.contramap { materializedX =>
(materializedX.id, materializedX.element.name, materializedX.element.age)
}
More documentations here
This works out of the box with doobie. Nested case classes get flattened into one row. The following code compiles without defining any custom decoders.
case class XData(name: String, age: Int)
case class MaterializedX(id: Int, element: XData)
implicitly[doobie.Read[MaterializedX]]
implicitly[doobie.Write[MaterializedX]]

Syntax for accepting tuple in a function in Scala

I would like a function to consume tuple of 7 but compiler won't let me with the shown message. I failed to find a proper way how to do it. Is it even possible without explicitely typing all the type parameters like Tuple7[String,String...,String] and is it even a good idea to use Scala like this ?
def store(record:Tuple7): Unit = {
}
Error:(25, 20) class Tuple7 takes type parameters
def store(record: Tuple7): Unit = {
^
As stated by Luis you have to define what Type goes on which position for every position in the Tuple.
I`d like to add some approaches to express the same behaviour in different ways:
Tuple Syntax
For that you have two choices, what syntax to use to do so:
Tuple3[String, Int, Double]
(String, Int, Double)
Approach using Case Classes for better readability
Long tuples are hard to handle, especially when types are repeated. Scala offers a different approach for handling this. Instead of a Tuple7 you can use a case class with seven fields. The gain in this approach would be that you now can attach speaking names to each field and also the typing of each position makes more sense if a name is attached to it.
And the chance of putting values in wrong positions is reduced
(String, Int, String, Int)
// vs
case class(name: String, age: Int, taxNumber: String, numberOfChildren: Int)
using Seq with pattern matching
If your intention was to have a sequence of data seq in combination with pattern matching could also be a nice fit:
List("name", 24, "", 5 ) match {
case name:String :: age:Int ::_ :: _ :: Nil => doSomething(name, age)
}
This only works nice in a quite reduced scope. Normally you would lose a lot of type information as the List is of type Any.
You could do the following :
def store(record: (String, String, String, String, String, String, String)):Unit = {
}
which is the equivalent of :
def store(record: Tuple7[String, String, String, String, String, String, String]):Unit = {
}
You can read more about it in Programming in Scala, 2nd Edition, chapter "Next Steps in Scala", sub-chapter "Step 9. use Tuples".

fpinscala book : Error Handling with Option Type

In the book "Functional programming in Scala", Chapter 4 page 55 :
We have the code below :
case class Employee(name: String, department: String)
def lookupByName(name: String): Option[Employee] = ???
// Use of "map"
lookupByName("Joe").map(_.departement)
// Use of "flatMap"
lookupByName("Joe").flatMap(_.manager)
For "Map" exemple side note say we have two results cases :
Joe's dept if Joe is an employee
None if Joe is not an employee
But for me response 1 is false. The function is not returning the department (= a string) but an Some(department) (= an Option).
Am i right or wrong ?
For "flatMap" exemple side note say we have two results cases :
Some(manager) if Joe has a manager
None if Joe is not an employee or doesn't have a manager
Problem 1 : But for me both responses are false because "manager" is not part of Employee's type so the program is not even compiling.
Problem 2 : Event if i add "manager: String" as part of the Employee type i can't even write lookupByName("Joe").flatMap(_.manager) in Intellij cause i get a "Type mismatch". I must write lookupByName("Joe").flatMap(x => Some(x.manager))
Problem 3 : When i write the function as lookupByName("Joe").flatMap(x => Some(x.manager)) to make my program compile then map and flatMap here have exactly the same results
What am i not understanding here ? What am i doing wrong ?
The resulting here is that i don't get the difference between map and flatMap from those exemples.
Map
You are absolutely right. The return type of lookupByName("Joe").map(_.department) is Option[String] (not String). So, indeed the first case returns Some(<department>).
FlatMap
You are right as well. It seems that the authors forgot to declare a third field in the Employee class. The correct class declaration should be:
case class Employee(name: String, department: String, manager: Option[Employee])

Pre-process parameters of a case class constructor without repeating the argument list

I have this case class with a lot of parameters:
case class Document(id:String, title:String, ...12 more params.. , keywords: Seq[String])
For certain parameters, I need to do some string cleanup (trim, etc) before creating the object.
I know I could add a companion object with an apply function, but the LAST thing I want is to write the list of parameters TWICE in my code (case class constructor and companion object's apply).
Does Scala provide anything to help me on this?
My general recommendations would be:
Your goal (data preprocessing) is the perfect use case of a companion object -- so it is maybe the most idiomatic solution despite the boilerplate.
If the number of case class parameters is high the builder pattern definitely helps, since you do not have to remember the order of the parameters and your IDE can help you with calling the builder member functions. Using named arguments for the case class constructor allows you to use a random argument order as well but, to my knowledge, there is not IDE autocompletion for named arguments => makes a builder class slightly more convenient. However using a builder class raises the question of how to deal with enforcing the specification of certain arguments -- the simple solution may cause runtime errors; the type-safe solution is a bit more verbose. In this regard a case class with default arguments is more elegant.
There is also this solution: Introduce an additional flag preprocessed with a default argument of false. Whenever you want to use an instance val d: Document, you call d.preprocess() implemented via the case class copy method (to avoid ever typing all your arguments again):
case class Document(id: String, title: String, keywords: Seq[String], preprocessed: Boolean = false) {
def preprocess() = if (preprocessed) this else {
this.copy(title = title.trim, preprocessed = true) // or whatever you want to do
}
}
But: You cannot prevent a client to initialize preprocessed set to true.
Another option would be to make some of your parameters a private val and expose the corresponding getter for the preprocessed data:
case class Document(id: String, title: String, private val _keywords: Seq[String]) {
val keywords = _keywords.map(kw => kw.trim)
}
But: Pattern matching and the default toString implementation will not give you quite what you want...
After changing context for half an hour, I looked at this problem with fresh eyes and came up with this:
case class Document(id: String, title: String, var keywords: Seq[String]) {
keywords = keywords.map(kw => kw.trim)
}
I simply make the argument mutable adding var and cleanup data in the class body.
Ok I know, my data is not immutable anymore and Martin Odersky will probably kill a kitten after seeing this, but hey.. I managed to do what I want adding 3 characters. I call this a win :)

i would love to know what this annoying feature of IDEA is --- so that I can turn it off

Code can explain this better than I can:
// how do i make IntelliJ IDEA *stop doing this* to my code?
case class Person(
name: String,
age: Int,
gender: Gender
)
// it's annoying!
This is a code style setting, just disable it in the prefs:
Code Style -> Scala -> Wrapping and Braces -> Method declaration parameters -> Align when multiline.