Scala inline unapply extractor - scala

The following code:
object Test {
#inline def unapply(i: Int): Option[String] =
i match {
case 1 => Some("Got 1")
case 2 => Some("Got 2")
case 3 => throw new Exception("Should not test 3")
case _ => None
}
def test(i: Int) = i match {
case Test(k) => k
case 4 => "Another 4"
case _ => ""
}
}
Test.test(3)
results in the following error:
...
at Test$.unapply(<console>:13)
at Test$.test(<console>:17)
...
Note that it's clear where the error comes from. However, the error shows that the method unapply is not inlined as I wanted to.
How can I inline this unapply method? This is for performance reason as well as code reuse.

#inline only requests that the compiler try to inline a method, but it doesn't have to, and in some cases, it can't. I don't think there is a spec for exactly what can and cannot be inlined by the compiler (though I'd love to see one if there is), but I'd bet the compiler simply won't in this case, no matter what you do.
Generally, you would only want to inline something that is a constant or small and unlikely to change, anyway. Otherwise, many uses of Test.unapply will cause the compiled code to increase in size quickly, and changes to the inlined method will proliferate across everything that references it.
This answer from #RexKerr says enough.

Related

Future.successful(Failure(Exception)) vs just Future.failed()

I am trying to understand the difference why someone will use Future.successful(Failure(ServiceException("error"))) rather than Future.failed(ex) in Scala.
Future.successful(
Failure(
ServiceException(ErrorTypes.Validation, "Value should not be more than 30")
)
)
It's hard to say why the author did what they did, I can guess a couple of reasons:
They are not aware that you can Future.fail to return a failed future. This may happen because the signature of future doesn't convey the failure path explicitly in the type. When you see Future[A], you may think this computation must succeed in the future
They wanted to be explicit about failure. When I declare Future[Try[A]], I am forcing the caller to deal with the fact that can't directly access A, but have to "jump another hop" to get inside for A. Although this is delicate, as one can easily write:
val f: Future[Try[String]] = ???
f.map(_.map(str => // stuff with string))
But perhaps the signature forces the caller to realize the possibility of failure. Although Try is pretty limiting in it's expressiveness as well, as it can only return a Throwable on the failure path. I find Future[Either[E, A]] to be more expressive in that regards, which allows you to build your own failure hierarchy and encode it in the type.
There is an ever lasting debate regarding unary functor IO types (Future[A]) vs bi-functor IO types (IO[E, A], i.e. ZIO). I am in favor of bi-functor and have been using it solely recently, and I find it to be really time-saving when I don't have to dig into the implementation to understand the possible failures of a computation.
I would like to add on top of the great answer by #YuvalItzchakov, that writing code as in your question complicates the code and makes it unnatural. For example, let's assume that this future normally an Int, something like:
val succeeded = true // or any other computation
val future = if(succeeded) {
Future.successful(42)
} else {
Future.successful(Failure(new Exception("some failure text")))
}
Then first, the type of future is Future[Any], because Any is the lowest common root of Int and Failure, which is not convenient. But then, to map on it, you need to do:
future.map {
case i: Int =>
println(i)
case Failure(ex) =>
println(ex.getMessage)
} recover {
case NonFatal(ex) =>
println(s"recover ${ex.getMessage}")
}
Which feels weird to pattern match Int and Failure in the same match case. If you do want to pattern match Success and Failure, we had to do:
val succeeded = true // or any other computation
val future = if(succeeded) {
Future.successful(Success(42))
} else {
Future.successful(Failure(new Exception("some failure text")))
}
But that is already pretty clear as redundancy, right? No one will call Future.successful(Success(42)). So I think that it is pretty clear to use Future.failed(new Exception("some failure text")).

Pattern match on value of Either inside a for comprehension?

I have a for comprehension like this:
for {
(value1: String, value2: String, value3: String) <- getConfigs(args)
// more stuff using those values
}
getConfigs returns an Either[Throwable, (Seq[String], String, String)] and when I try to compile I get this error:
value withFilter is not a member of Either[Throwable,(Seq[String], String, String)]
How can I use this method (that returns an Either) in the for comprehension?
Like this:
for {
tuple <- getConfigs()
} println(tuple)
Joking aside, I think that is an interesting question but it is misnamed a bit.
The problem (see above) is not that for comprehensions are not possible but that pattern matching inside the for comprehension is not possible within Either.
There is documentation how for comprehensions are translated but they don't cover each case. This one is not covered there, as far as I can see. So I looked it up in my instance of "Programming in Scala" -- Second Edition (because that is the one I have by my side on dead trees).
Section 23.4 - Translation of for-expressions
There is a subchapter "Translating patterns in generators", which is what is the problem here, as described above. It lists two cases:
Case One: Tuples
Is exactly our case:
for ((x1, …, xn) <- expr1) yield expr2
should translate to expr1.map { case (x1, …, xn) => expr2).
Which is exactly what IntelliJ does, when you select the code and do an "Desugar for comprehension" action. Yay!
… but that makes it even weirder in my eyes, because the desugared code actually runs without problems.
So this case is the one which is (imho) matching the case, but is not what is happening. At least not what we observed. Hm?!
Case two: Arbitrary patterns
for (pat <- expr1) yield expr2
translates to
expr1 withFilter {
case pat => true
case _ => false
} map {
case pat => expr2
}
where there is now an withFilter method!
This case totally explains the error message and why pattern matching in an Either is not possible.
The chapter ultimately refers to the scala language specification (to an older one though) which is where I stop now.
So I a sorry I can't totally answer that question, but hopefully I could hint enough what is the root of the problem here.
Intuition
So why is Either problematic and doesn't propose an withFilter method, where Try and Option do?
Because filter removes elements from the "container" and probably "all", so we need something that is representing an "empty container".
That is easy for Option, where this is obviously None. Also easy for e.g. List. Not so easy for Try, because there are multiple Failure, each one can hold a specific exception. However there are multiple failures taking this place:
NoSuchElementException and
UnsupportedOperationException
and which is why Try[X] runs, but an Either[Throwable, X] does not.
It's almost the same thing, but not entirely. Try knows that Left are Throwable and the library authors can take advantage out of it.
However on an Either (which is now right biased) the "empty" case is the Left case; which is generic. So the user determines which type it is, so the library authors couldn't pick generic instances for each possible left.
I think this is why Either doesn't provide an withFilter out-of-the-box and why your expression fails.
Btw. the
expr1.map { case (x1, …, xn) => expr2) }
case works, because it throws an MatchError on the calling stack and panics out of the problem which… in itself might be a greater problem.
Oh and for the ones that are brave enough: I didn't use the "Monad" word up until now, because Scala doesn't have a datastructure for it, but for-comprehensions work just without it. But maybe a reference won't hurt: Additive Monads have this "zero" value, which is exactly what Either misses here and what I tried to give some meaning in the "intuition" part.
I guess you want your loop to run only if the value is a Right. If it is a Left, it should not run. This can be achieved really easy:
for {
(value1, value2, value3) <- getConfigs(args).right.toOption
// more stuff using those values
}
Sidenote: I don't know whats your exact use case, but scala.util.Try is better suited for cases where you either have a result or a failure (an exception).
Just write Try { /*some code that may throw an exception*/ } and you'll either have Success(/*the result*/) or a Failure(/*the caught exception*/).
If your getConfigs method returns a Try instead of Either, then your above could would work without any changes.
You can do this using Oleg's better-monadic-for compiler plugin:
build.sbt:
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.2.4")
And then:
object Test {
def getConfigs: Either[Throwable, (String, String, String)] = Right(("a", "b", "c"))
def main(args: Array[String]): Unit = {
val res = for {
(fst, snd, third) <- getConfigs
} yield fst
res.foreach(println)
}
}
Yields:
a
This works because the plugin removes the unnecessary withFilter and unchecked while desugaring and uses a .map call. Thus, we get:
val res: Either[Throwable, String] =
getConfigs
.map[String](((x$1: (String, String, String)) => x$1 match {
case (_1: String, _2: String, _3: String)
(String, String, String)((fst # _), (snd # _), (third # _)) => fst
}));
I think the part you may find surprising is that the Scala compiler emits this error because you deconstruct the tuple in place. This is surprisingly forces the compiler to check for withFilter method because it looks to the compilers like an implicit check for the type of the value inside the container and checks on values are implemented using withFilter. If you write your code as
for {
tmp <- getConfigs(args)
(value1: Seq[String], value2: String, value3: String) = tmp
// more stuff using those values
}
it should compile without errors.

Match may not be exhaustive warning is incorrect

So the scala compiler is complaining that a pattern match might not be exhaustive for the method foo and I wonder why. This is the code:
abstract class Foo {
def foo(that: Foo): Unit = (this, that) match {
case (Foo_1(), Foo_1()) => //case 1
case (Foo_1(), Foo_2()) => //case 2
case (Foo_2(), Foo_1()) => //case 3
case (Foo_2(), Foo_2()) => //case 4
// Compiler warning
}
def fooThis(): Unit = this match {
case Foo_1() => //do something
case Foo_2() => //do something
// Works fine
}
def fooThat(that: Foo): Unit = that match {
case Foo_1() => //do something
case Foo_2() => //do something
// Works fine
}
}
case class Foo_1() extends Foo
case class Foo_2() extends Foo
And this is the error:
Warning:(5, 32) match may not be exhaustive.
It would fail on the following inputs: (Foo(), _), (Foo_1(), _), (Foo_2(), _), (_, Foo()), (_, Foo_1()), (_, Foo_2()), (_, _)
def foo(that: Foo): Unit = (this, that) match {
Since this and that are of type Foo, and Foo can only be of type Foo_1 or Foo_2, the cases in foo are all possible combinations.
I added fooThis and fooThat for sake of completeness and to show that matching Foo_1 and Foo_2 suffices. The compiler message suggests that there are other types that can be matched (i.e. Foo and _).
So why is this warning shown?
Related:
Scala bug: fixed in 2.12.0-M4. My scala welcome message:
Welcome to Scala 2.12.1 (Java HotSpot(TM) Client VM, Java 1.8.0_131).
Scala bug: false match not exhaustive warning on (unsealed, sealed) tuple
EDIT
The compiler seems to complain as soon as you use tuples. If we add a dummy variable to fooThis as follows
def fooThis(): Unit = (this, Foo_1()) match {
case (Foo_1(),_) => //do something
case (Foo_2(),_) => //do something
}
we get the following compiler warning
Warning:(13, 27) match may not be exhaustive.
It would fail on the following input: (_, _)
def fooThis(): Unit = (this, Foo_1()) match {
The Scala compiler won't give exhaustive match warnings for non-sealed traits (like your Foo). This explains why fooThis and fooThat compile without warnings.
If you want warnings here (and you should, because they're better than MatchError exceptions at runtime) you have a couple of options:
Make Foo sealed. This creates an ADT, which is safe to pattern match against in the sense that you'll get exhaustivity warnings when you forget a case. Option is an ADT that you're probably familiar with from the standard library. Here, you've already got cases for both Foo_1 and Foo_2, so you won't get an exhaustivity warning. But if you ever forget either case, you will. You probably want to make Foo_1 and Foo_2 final while you're at it.
Leave Foo unsealed, use Typelevel Scala and enable its -Xlint:strict-unsealed-patmat warnings.
On the other hand, the Scala compiler will give exhaustive match warnings for final case classes like Tuple2, which is what you're matching against in your foo method.
To answer "why is the warning shown?", consider what happens if we do this:
case class Foo3() extends Foo
val foo3 = Foo3()
foo3.foo(foo3)
(Answer: it throws a MatchError at runtime.)
The warning is the Scala compiler's way of helping you avoid the exception at runtime. If you want to make the warning go away, you could:
Make Foo sealed (again, creating an ADT), preventing Foo3 from sneaking in elsewhere.
Add a wildcard case _ => ....
Make the match unchecked: ((this, that): #unchecked) match { ....
Don't do number 3, because it leaves you vulnerable to MatchErrors at runtime when someone introduces Foo3.
So, perhaps the question isn't really "why does the match in foo generate a warning", but "why doesn't the match in fooThis and fooThat generate a warning".
It seems making the abstract class sealed at least makes the compiler warning go away:
sealed abstract class Foo {
Though I'm not too sure why. It might be related to: https://issues.scala-lang.org/browse/SI-9351
Figuring out all subclasses of a class is called Class Hierarchy Analysis, and doing static CHA in a language with dynamic code loading is equivalent to solving the Halting Problem.
Plus, one of the goals of Scala is separate compilation and deployment of independent modules, so the compiler simply cannot know whether or not a class is subclassed in another module, because it never looks at more than one module. (After all, you could compile a module against the interface of some other module without that module even existing on your system!) That's why sealed requires all subclasses to be defined in the same compilation unit.
That's why the compiler doesn't show warnings, because its aware of the existing subclasses.

Is this scala function to heavy/doing too many things to unit test properly?

OK, so I am looking at testing libraries, in particular ScalaTest & ScalaMock.
I wanted to write a test to test this function that I have written:
def gameMenuSelect(): State = {
Try(UI.readOption) match {
case Success(i) => {
i match {
case 1 => HumanGame
case 2 => MachineGame
case 3 => sys.exit(0)
case _ =>
UI.invalidSelectionMsg
ChoosingGame
}
}
case Failure(e) => UI.invalidSelectionMsg; ChoosingGame
}
}
A bit of background, UI.readOption is a simple scala.io.StdIn.readInt.
State is a Trait - subsequently HumanGame, MachineGame and ChoosingGame are also Traits that extend State.
The problem is I have no idea how I would test it, the reason is because I feel like this function does too much.
It is reading input, validating that the input given is indeed a number/integer and not throwing a NumberFormatException. Given that the input is an integer it is match on allowable integers.
I really feel like there is a lot to test, also a lot that I am not sure is unit testable.
Could I have some opinions on whether you feel that this function is doing too many things, whether I should try to break up the reading of the integer and the matching of it?
Thanks.
Yes, should absolutely try to separate the "side-effecting" bits, reading and writing, from the selection logic. The selection logic can return something like
import scalaz._, Scalaz._
def selectGame(i: Int): GameError \/ State =
i match {
case 1 => HumanGame.right
case 2 => MachineGame.right
case _ => InvalidGame(i).left
}
sealed trait GameError
case class InvalidGame(i: Int) extends GameError
object GameError {
def render(e: GameError): String =
e match {
case InvalidGame(i) =>
s"Invalid game choice: $i. Only 1 and 2 are acceptable values"
}
}
Note that I also model the error into a specific type instead of just using strings.
Then you can do the same with your number parsing:
def parseInt(i: String): ParseError \/ Int =
???
For your "effects" you can use Scalaz IO for the interactions with the console:
def readLine: IO[String] =
IO(StdIn.readLine)
def printLine(line: String): IO[Unit] =
IO(println(line))
Then, with a bit more code, you can use the EitherT[IO, E, A] monad to "assemble" all your functions:
// I will provide a full example if you want to go this way
val actions: EitherT[IO, ApplicationError, Unit] =
for {
line <- readLine
i <- parseInt(line)
s <- selectGame(i)
_ <- printLine(s.render)
} yield ()
The actions value will both have IO side-effects and collect errors, stopping the process if there are any.
Eventually this all makes your testing a lot easier because you have isolated the "pure" parts which are way easier to test: no setup, no mocks, just pure functions.
In my opinion, there are too many side effects in your function. Not only the reading of the integer, but also sys.exit(0). You could change the method to accept the integer as a parameter, and also add an EndingGame state that you could use for case 3. You would then have a pure function that is easily testable.

Get Option value or throw an exception

Given an Option, what is the idiomatic way to get its value or throw an exception trying?
def foo() : String = {
val x : Option[String] = ...
x.getOrException()
}
A throw "statement" is really an expression in Scala, and it has type Nothing, which is a subtype of every other type. This means you can just use plain old getOrElse:
def myGet[A](oa: Option[A]) = oa.getOrElse(throw new RuntimeException("Can't."))
You really, really shouldn't be doing this, though.
(EDIT: this is not the best or most idiomatic way to do it. I wrote it when I was not familiar with Scala. I leave it here for an example of how not to do it. Nowadays I would do as #TravisBrown)
I think it really boils down to two things:
how sure are you that the value is there?
how do you want to react if it isn't?
If at that point in your code you expect the value to be there, and in the remote case that it isn't you want your program to fail fast, then I would only do a normal get and let Scala throw a NoSuchElementException if there was no value:
def foo() : String = {
val x : Option[String] = ...
x.get
}
If you want to handle the case differently (throw your own exception) I think a more elegant way would look like this:
def foo(): String = {
val x: Option[String] = None
x match {
case Some(value) => value
case None => throw new MyRuntimeException("blah")
}
}
And of course if you want to supply your own alternative value for the case that the Option is None you would just use getOrElse:
def foo(): String = {
val x: Option[String] = None
x.getOrElse("my alternative value")
}
I hope this will help you to understand how to represent errors (and generally effects) using types.
Error handling strategies in functional Scala
Use Option to return optional values. For example - fail to find entity in storage.
Use Option(possiblyNull) to avoid instances of Some(null).
Use Either[Error, T] to report expected failure. For example - email format is wrong, cannot parse a string to a number, etc.
Model your errors as ADTs (simply speaking kind of type hierarchies) to use it, for example, on the Left of the Either to represent more complex error scenarios.
Throw Exception only to signal unexpected and not-recoverable failures. Like missing config file.
Use Either.catchOnly or Try or Cats.IO (advanced) rather than a catch block for handling unexpected failures. Hint: You can still use ADT but extend them from throwables. More about Either vs Try.
Use Validated data-type from Cats lib to accumulate errors rather than fail-fast (Either), but prefer Either's on module-level to simplify the composition of the program (to have the same types). For example - form data validation, parsing errors accumulation.
Use mentioned types and don't optimize program preemptively - since most probably, bottle-necks would be in business logic, not in effect types.
Such an approach will simplify maintenance and updates of your code since you can reason about it without going to implementation specifics (aka local-reasoning). Also - reduce bugs - you cannot miss an error in the type. And compose the program easier (with help of map, flatMap and other combinators) - since it's simpler on type level, rather than with non-local exceptions and side-effects.
More about learning functional Scala.
But be aware that sometimes with this approach types could stack up and it could become harder to compose things. Given, for example: x: Future[Either[Error, Option[T]]] What you can do:
Use map and flatMap in combination with pattern-matching to compose different values of such types, for example:
x.faltMap { case Right(Some(v)) => anotherFuture(v); case Left(er) => ... }
If it doesn't help you can try to use MonadTransformers (don't be scared of the name, it's just wrappers around the effect types like Either and Future)
Also, an option is to simplify your errors ADT by extending them from the Throwable to unify it with Future, then it'll be Future[Option[T]]
And finally, in your case one option will be:
def foo() : Either[Error, String] = {
val x : Option[String] = ...
x match {
case Some(v) => Right(v)
case None => Left(Error(reason))
}
}
Just use the .get method.
def get[T](o:Option[T]) = o.get
It will throw a NoSuchElementException if o is an instance of None.
Basically, I would work with options like this:
def addPrint(oi:Option[Int]) = oi.map(_+1).foreach(println)
addPrint(Some(41))
addPrint(Some(1336))
addPrint(None)
to avoid your specific question.
Scala now support this operation on maps using getOrElse() method, see documentation here
As pointed out already, throwing an exception in Scala is an expression as well.
So you can do the following:
myMap.getOrElse(myKey, throw new MyCustomException("Custom Message HERE")