kaileena
kaileena

Reputation: 119

scala syntax explanation involving higher order functions, type parameter and return type

I am having problems understanding the Scala syntax, please advice. I have two snippets of code.

abstract class Try[T] {

  def flatMap[U](f: T => Try[U]): Try[U] = this match {
    case Success(x) => try f(x) catch { case NonFatal(ex) => Failure(ex) }
    case fail: Failure => fail
  }
}

My understanding:

Q1 - Is my understanding correct?

Q2 - what is the relation between the return type from f (namely Try[U]) and the return type of flat map Try[U]? Does it have to be the same?

def flatMap[U](f: T => Try[U]): Try[U]

Or can I somehow have something like

def flatMap[U](f: T => Option[U]): Try[U]

In the last snippet of code, I guess that, after I use the function f inside my flatMap, I would need to make the connection between the output of f (namely Option[U]) and the final output demanded by flatMap (I mean Try[U])

EDIT

This code is taken from a scala course. here is the full code (some people asked about it). I just want to understand the syntax.

abstract class Try[T] {

def flatMap[U](f: T => Try[U]): Try[U] = this match {
    case Success(x) => try f(x) catch { case NonFatal(ex) => Failure(ex) }
    case fail: Failure => fail
}

def map[U](f: T => U): Try[U] = this match {
    case Success(x) => Try(f(x))
    case fail: Failure => fail
}
}

Upvotes: 1

Views: 265

Answers (2)

joel
joel

Reputation: 7867

Q1 Largely correct, but just to clarify, all of this happens at compile time - T is not known at runtime (see here)

Q2 Of course you can create a method with signature

...[U](f: T => Option[U]): Try[U]

and you're free to call that method flatMap, but it won't be a standard flatMap:

trait T[A] {
  flatMap[B](f: A => T[B]): T[B]
}

There are mathematical reasons for the form of flatMap (which also have implications in Scala's implementation of for expressions). To avoid confusion ...

Rather than altering flatMap's signature, wrap your T => Option[U] with an Option[U] => Try[U] to create a T => Try[U] before passing it to flatMap.

Upvotes: 0

Leo C
Leo C

Reputation: 22449

Q1 - Is my understanding correct?

It's hard to comment based on your sample code which has method implementation in an abstract class while no concrete classes are defined. Lets consider the following toy version of Try extracted from the Scala API with the flatMap implementation in its concrete classes:

import scala.util.control.NonFatal 

sealed abstract class MyTry[+T] {
  def flatMap[U](f: T => MyTry[U]): MyTry[U]
}

object MyTry {
  def apply[T](r: => T): MyTry[T] =
    try MySuccess(r) catch { case NonFatal(e) => MyFailure(e) }
}

final case class MyFailure[+T](exception: Throwable) extends MyTry[T] {
  override def flatMap[U](f: T => MyTry[U]): MyTry[U] =
    this.asInstanceOf[MyTry[U]]
}

final case class MySuccess[+T](value: T) extends MyTry[T] {
  override def flatMap[U](f: T => MyTry[U]): MyTry[U] =
    try f(value) catch { case NonFatal(e) => MyFailure(e) }
}

Testing it out with the following function f: T => MyTry[U] where T = String and U = Int, I hope it helps answer your question:

val f: String => MyTry[Int] = s => s match {
  case "bad" => MyFailure(new Exception("oops"))
  case s => MySuccess(s.length)
}

MyTry("abcde").flatMap(f)
// res1: MyTry[Int] = MySuccess(5)

MyTry("bad").flatMap(f)
// res2: MyTry[Int] = MyFailure(java.lang.Exception: oops)

Q2 - what is the relation between the return type from f (namely Try[U]) and the return type of flat map Try[U]? Does it have to be the same?

In Scala, flatMap is a common method defined in many of Scala containers/collections such as Option[T], List[T], Try[T], Future[T], with a standard signature:

class Container[T] {
  def flatMap[U](f: T => Container[U]): Container[U]
}

If you want to have a special map that takes a T => Container1[U] function and returns a Container2[U], it'd probably best not to name it flatMap.

Upvotes: 0

Related Questions