Hello, in this video, I will show you the most common high-level operations to use to manipulate values of type Try. Often, you want to deal with the successful case of a Try, and you want to postpone error handling to a later point in the program. For instance, consider a situation where you want to read two dates from String values and compute the duration between them. Parsing a date is an operation that may fail, but in your case you don't want to deal with the failures at this point of the program. You want to focus on the case where both dates were successfully parsed to compute the duration between them. In this video, I will show you how you can deal with such situations by using the most common transformation operations on Try, namely flatMap and map. So we want to parse two dates and compute the duration between them. We have this method, parseDate to parse a single date. It takes a String and returns a Try of LocalDate. What we want to implement is the operation tryPeriod that takes two dates as parameters and tries to return a Period. To implement tryPeriod, we call parseDate on the first String. So we get a Try of LocalDate. To parse the second date, I'm going to use flatMap. flatMap takes, as a parameter, a continuation function. That is a function that will be invoked with the successful result of the first call to parseDate. So with a LocalDate. And to continue invoking computations that may fail. In the continuation function, I call parseDate with the second string. Finally, I call map to transform the successful result, which is the second date, into the period between the first date and the second date. If parsing the first date fails, the continuation function passed to flatMap is not even called, and flatMap immediately returns the failure. So we won't even try to parse the second date in that case. And if parsing the second date fails, the function passed to map will not be called, and the map method just returns the failure. So here are some example of use of tryPeriod. With two valid dates, we get the success. And if the first date is invalid, what do we get? A failure with the exception from when we tried to parse the first date. So here the month is invalid. If the second date is invalid, we get a failure. And if both dates are invalid, we get a failure that reports about the first. We didn't even even try to parse the second one. Now, I want to show you an alternative syntax to write the same program. We have seen previously that collections could be manipulated with for expression instead of calling flatMap and map. And we can do the same here, just because the methods are named map and flatMap as well. So we can rewrite the implementation of tryPeriod like this. We can read the program like this for a first date parsed from the first String, and then a second date parsed from the second String. We return the period between them. Here is a summary of the transformation operations on the type Try. map transforms a successful value of type A into another successful value of type B with the given transformation function. Similarly, flatMap tries to transform a successful value of type A into a value of type B with the given continuation function that may also fail because the return type here is Try of B. Both map and flatMap immediately return a failure if the underlying Try value on which they are applied is already a failure. Then we have recover and recoverWith, which turn failures into successes. recover takes a partial function from Throwable to A. So if the underlying Try value is a failure and that the recovery function is defined for this type of failure, then recover returns a success computed from the function f. Otherwise, it returns the same underlying failure. recoverWith is similar, but it takes a continuation function, that is a function that may fail to recover because it returns a Try of A. So these four methods are the most common operations you use when you work with values of type Try. Let's solve a more complex problem. Instead of parsing just two dates, you want to parse an arbitrary number of dates that you read from a file. Each date is written in a new line. Like here, we have three lines. So you want to implement a method with the following signature. It takes a fileName and returns a Try of a collection of LocalDate. We can break down the problem into two parts. First, reading the content of the file, and then parsing each line as a date. So, to read from a file, you can use Source.fromFile from the standard library. It gives you a source from which you can get the lines, and you can convert them into a collection of lines. So a sequence of strings. Before returning this collection of strings, you want to close the source to free any allocated resource. Since reading from a file is an operation that may fail by throwing an exception, you can wrap the whole method body in a Try block to catch any exception and make such failures part of the result type. So now that we can read the file as a sequence of lines, as String values, the next step is to try parsing every line of the file as a date. So we want to transform the value returned by a readDateStrings. In case of success, we want to continue parsing every line of the file, so we could either use map of flatMap here. But since parsing the line of the file is a computation that can fail, it will return Try again. So we use flatMap here. The continuation function passed to flatMap takes, as a parameter, the collection of lines and it returns a Try of collection of dates. To compute this result, we will iterate over the lines, over the date strings here, and we will parse them. So we do this with foldLeft and the accumulating result will have type Try of Seq of LocalDate. We start with a successful empty collection of dates. Remember that the first parameter of foldLeft is the value we return in case the collection on which we apply the foldLeft method is empty. So in this case, we return a successful empty List of LocalDate. Then, for every line of the file, we want to parse it as a date and, in case of success, and if we were also able to successfully parse the previous lines, we want to append the new date to the previously parsed date. So here, we have the accumulating result and the line that we want to parse as a String. We do this with this for expression. for the previously parsed dates of tryDates and the newly parsed date from parseDate of dateString. We yield the new date appended to the previous dates. You can see that it works here, but it fails to parse the file because there is one of the line that is invalid. So I can fix the problem by changing the content of the file. So here we have the first two lines, which are invalid dates, and the last one is also an invalid date. So now it should work. And let's see, okay, we get a Success and a collection of two dates. Okay, let's have another look at the first operation, readDateStrings. Consider what happens if an exception is thrown after the file has been opened, so after this line has been executed, but before it was closed. So if an exception is thrown in the evaluation of this expression. What will happen is that the Try block here will catch the exception, and it will return it as a failure. But this can be a problem because opening a file is an operation that allocates resources. But we never release these resources because the exception is thrown before we invoke the close method here. So we really want the close operation to be called in any case, whether an exception was thrown here or not. To do that, we can use another utility from the Scala library named Using. Using takes two parameters. The first one performs the resource acquisition. So here, we open the file, and the second parameter is a function that uses the resource. In our case, it's the source instance. So we just return the lines of the file. The Using construct ensures that the resource acquired here will eventually be released, whether an exception occurs or not when it is used here. So Using returns a Try, so we return a Try of Seq of String. In summary, you should leverage the operations map and flatMap to manipulate Try values while postponing failure handling to a later point in the program. And you should also leverage Using to mix in resource acquisition and release with operations that can fail