Scala 3 Using - problems when reading from file - scala

In a Scala 3 project, I have a method which returns a Try from a given String
def translate(text: String) : Try[Thing] = ...
and a method which is supposed to read the contents of a file and pass it on to the first method. Here, I want to use Using because as far as I understand, this is the functional way to handle file I/O, and it returns a Try which I need anyway, and it makes sure the used resource is closed:
def translateFromFile(filepath: String) : Try[Thing] =
Using(Source.fromFile(filepath).getLines.mkString) match
case Success(s) => translate(s)
case Failure(e) => Failure(e)
However, the compiler says
given instance of type scala.util.Using.Releasable[String] was found for parameter evidence$1 of method apply in object Using
Honestly, I don't understand this error message, and I couldn't find any help online. Can someone help? What's the correct way to do this? Thanks!

The error means that you're trying to substitute into Using(...) not something that can be closed but a String.
It should be
def translateFromFile(filepath: String) : Try[Thing] =
Using(Source.fromFile(filepath)) { s =>
translate(s.getLines.mkString) match {
case Success(s) => ???
case Failure(e) => ???
}
}
or just
def translateFromFile(filepath: String) : Try[Thing] =
Using(Source.fromFile(filepath)) { s =>
translate(s.getLines.mkString)
}.flatten
Using(...){s => ...} returns Try and your translate returns Try, so it's Try[Try[...]], that's why .flatten.
There is nothing specific to Scala 3.

Related

How to handle type-erasure when dealing with Scalatest's Prettifier

I encountered the following problem when writing unit tests for my Scala application:
For my unit-tests I don't want to see the whole object in the output (in case of a failing test) because I only care about the id. That's why I am using a org.scalactic.Prettifier like this:
case class Node(n: id, doesntMatterString: String)
implicit val prettifier: Prettifier = Prettifier(
{
case n: Node=> "n" + n.id
case ns: Seq[Node] => ns.map("n" + _.id).mkString(",")
}
)
Some tests may output one single Node while some others may output a Seq[Node].
While this generally works I still get the following warning:
non-variable type argument Node in type pattern Seq[Node] (the underlying of Seq[Node]) is unchecked since it is eliminated by erasure
I know that type erasure is the problem here, but cannot figure out how to write my Prettifier in a way so that this warning won't be displayed. Thanks in advance.
Try to use recursive pattern matching.
Here's an example (without a Prettifier):
case class Id(id: String)
case class Node(n: Id, s: String)
def prettifyRec(a : Any): String = a match {
case Node(n, _) => "n" + n.id
case ((n: Node) :: Nil) => prettifyRec(n)
case ((n: Node) :: (tail: Seq[_])) => prettifyRec(n) + "," + prettifyRec(tail)
case _ => "ERR"
}
println(prettifyRec(Seq(Node(Id("e"), "i"))))
Try it out!

Is it possible to call a generically-typed function by passing in a class specified in a map?

Asking here because I'm pulling out my hair trying to figure out what exactly it is I need to do here.
I'm writing a batch-processing endpoint that attempts to convert the body of a request to a specific Scala case class before executing the logic within the endpoint itself.
This is as far as I currently got. First, I have a function executeWithType that takes a generic type, a controller method, and the request body and executes the controller method after converting the request body to the provided type. The request param is available in a scope outside this function.
def executeWithType[A](action: () => Action[A], batchRequest: BatchRequest): Future[Result] = {
action()(request.map(_ => batchRequest.body.map(_.as[A]).get))
}
Then, I have some code that checks what endpoint to call and what type to cast to depending on what's in the BatchRequest itself.
val res: Future[Result] = (batchRequest.method, batchRequest.endpoint) match {
case ("POST", "/endpoint1") => {
executeWithType[EndpointOneType](controller.endpointOne _, batchRequest)
}
case ("POST", "/endpoint2") => {
executeWithType[EndpointTwoType](controller.endpointTwo _, batchRequest)
}
case _ => Future.successful(NotFound)
}
The above works perfectly fine - however, I want to avoid this sort of tuple-matching with individual cases if possible, and specify a Map that does this instead. In my ideal world, the end result of the code block immediately above would look like this:
val actions = Map(
Seq("POST", "/endpoint1") -> (controller.endpointOne _, EndpointOneType),
Seq("POST", "/endpoint2") -> (controller.endpointTwo _, EndpointTwoType)
)
val res = actions.get(Seq(batchRequest.method, batchRequest.endpoint)) match {
case Some(action) => {
executeWithType[action._2](action._1, batchRequest)
}
case _ => Future.successful(NotFound)
}
Is this possible at all? I've been trying to fight with it but my understanding of reflection in Scala is really weak, so I'm not sure exactly how I'd go about doing this. I've tried a bunch of classOf and typeTag and Class[_] stuff but I'm basically swinging in the dark. Hoping someone more knowledgeable than I am could help me out.
The big things are:
What needs to go in the second space of the tuple in the value of the Map? How do you pass a Class variable?
How do we use that class-as-a-variable to call a generically typed method?
How do we use that class-as-a-variable to call a generically typed method?
You can't. But I'd like to suggest an alternate solution.
Just define a local class instead of tuples:
class RequestAction[A](action: () => Action[A]) {
def apply(request: BatchRequest) = executeWithType(action, request)
}
val actions = Map(
Seq("POST", "/endpoint1") -> new RequestAction(controller.endpointOne _), // type parameter is inferred
Seq("POST", "/endpoint2") -> new RequestAction(controller.endpointTwo _)
)
val res = actions.get(Seq(batchRequest.method, batchRequest.endpoint)) match {
case Some(action) => action(batchRequest)
case _ => Future.successful(NotFound)
}
(while this depends on code not shown in the question, it looks likely that you can simplify by passing Action[A] instead of () => Action[A]).

Scala methods with generic parameter type

I have been working with Scala for close to a year, but every now and then I come across a piece of code that I don't really understand. This time it is this one. I tried looking into documents on "scala methods with generic parameter type", but I am still confused.
def defaultCall[T](featureName : String) (block : => Option[T])(implicit name: String, list:Seq[String]) : Option[T] =
{
val value = block match {
case Some(n) => n match {
case i : Integer => /*-------Call another method----*/
case s : String => /*--------Call another method----*/
}
case _ => None
}
The method is called using the code shown below :
var exValue = Some(10)
val intialization = defaultCall[Integer]("StringName"){exValue}
What I don't understand in the above described code is the "case" statement in the defaultCall method.
I see that when the exValue has a value and is not empty, the code works as expected. But in case I change the exValue to None, then my code goes into the "case _ = None" condition. I don't understand why this happens since the match done here is against the "variable" which would be either an Integer or a String.
What happens here is that when you pass a None it will match on the second case, which "catches" everything that is not an instance of a Some[T]:
block match {
case Some(n) => // Will match when you pass an instance of Some[T]
case _ => // Will match on any other case
}
Note that None and Some are two different classes that inherit from Option.
Also, the variable match is only done if the first match succeeds, otherwise not. To achieve the type checking in the first match you could do:
block match {
case Some(n: Int) => // do stuff
case Some(n: String) => // do stuff
case _ => // Will match on any other case
}
Hope that helps

Making a convenient logging function for Try[T], but got stuck because of the type system

I wonder if this can be done using Scala's type system.
Basically, I want to make a logging method which accepts a result of type Try[T] and prints out a message that differs a bit depending whether result is a Success or a Failure
For example, the signature might look like
def logTry[T](what: Try[T], block: T => String): Unit
and can be used as such:
val size: Try[(Int, Int)] = Try(getSizeAndTimestampFromDatabase())
logTry(size, e => "size is " + e._2 + " kb")
which will output
size is 13 kb if size is Success(x: Int), e.g., Success(13)
or
error: size is (not available) if size if of type Failure(t: Throwable)
The tricky part is that we need to be able to access the object within the Try for printing to the screen if it's a Success, or print a chosen default string (e.g. "(not available)") if it's a Failure as the placeholder. Also, it must work with a very general type T or Any that can range from a simple scalar value to an instance of a class, etc.
The use case for this eccentric function is that it will be very handy for logging a Try object in an informative way, without cluttering the code with map / recover or a match statement.
This is the skeleton that I have came up with, but of course the hard part hasn't been figured out yet.
def logTry[T](what: Try[T], block: T => String): Unit = {
what match {
case Success(res) => println(block(res))
case Failure(t) => println(???) // how to do this
}
}
Here the solution, which not forces you to explicitly supply types:
implicit class LogTry[T](attempt: Try[T]) {
def log[E](comp: T => E, block: String => String, error: Throwable => String = _ => "(N/A)") =
println(block(attempt map (comp andThen (_.toString)) recover { case ex => error(ex) } get))
}
use it as
size.log(_._2, size => f"size is $size kb")
or
size.log(_._2, size => f"size is $size kb", err => f"(not available because $err)")
This is what I would do (well, if I didn't use implicits to add the method to Try):
def logTry[T](t: Try[T])(f: Try[T] => String) { println(f(t)) }
val size: Try[Integer] = Try(getSizeFromDatabase())
logTry(size) {
case Success(e) => s"size is $e kb"
case Failure(e) => s"error: size is (${e.getMessage}})"
}
Edit: after understanding your request
def logTry(t: Try[Any], f: Any => String) {
t match {
case Success(e) => println(f(e))
case Failure(e) => println(f(new Object {
override def toString = "(not available)"
}))
}
}
val size: Try[Integer] = Try(getSizeFromDatabase())
logTry(size, s => s"Size is $s kb")
I don't know of a way to achieve for a generic type T, but I don't think that's a good idea anyway: it's possible for the client to make special use of the methods of T when printing, but you want it to always print "not available" where it was supposed to print the information from T, and that's not so simple.
Another option is to create a special formatter (think "size is ${...} kb") so the logger would know how to replace it, but I'm not sure if this is what you want.

Pattern matching using current object

I'm trying to match an Option, and test to see if it's a Some containing the object making the call. So the code I want to write looks like this:
methodReturningOption() match {
case Some(this) => doSomething()
case _ => doSomethingElse()
}
but that fails to compile, with the error
'.' expected but ')' found
I also tried using Some(`this`) which gives the error
not found: value this
I can make it work if I add a variable which refers to this
val This = this
methodReturningOption() match {
case Some(This) => doSomething()
case _ => doSomethingElse()
}
but that looks ugly and seems like an unpleasant workaround. Is there an easier way to pattern match with this as an argument?
I suppose you could try this:
methodReturningOption() match {
case Some(x) if x == this => doSomething()
case _ => doSomethingElse()
}
It looks like this is considered a special keyword and can't be used in that context.
Jack Leow's solution is probably the best - I'd recommend going with that since it's much more explicit. However as an alternative you can also create a variable point to 'this' using the following syntax. (Note the self => on the first line)
class Person { self =>
def bla() = methodReturningOption() match {
case Some(`self`) => ???
case _ => ???
}
}
This doesn't really answer the question, it's just a potential alternative syntax that may be useful to you.