{"id":1138,"date":"2019-12-17T14:06:38","date_gmt":"2019-12-17T14:06:38","guid":{"rendered":"https:\/\/tech.avant.net\/q\/?p=1138"},"modified":"2019-12-18T02:24:39","modified_gmt":"2019-12-18T02:24:39","slug":"fp-error-handling","status":"publish","type":"post","link":"https:\/\/tech.avant.net\/q\/fp-error-handling\/","title":{"rendered":"FP Error Handling"},"content":{"rendered":"\n<p>I would like to handle errors in pure FP (functional programming), something more idiomatic and easier to read than nested try\/catch blocks.<\/p>\n\n\n\n<p>Let&#8217;s say we have the following procedure,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def sumCirclesDiv(rs: List[String], d: Int = 1): Int = {\n    val areas = rs.map(r => {math.Pi * r.toDouble * r.toDouble})\n    val divs = areas.map(_ \/ d)\n    divs.sum\n}<\/pre>\n\n\n\n<p>This is pretty straightforward. Given a list of numbers (represented as strings), compute the areas of these as circles, and then divide by a common divisor (default to 1) and return an Integer sum. But what happens if one of the input strings cannot be parsed? What happens if the divisor is 0?<\/p>\n\n\n\n<p>We could use try\/catch exception handling, and end up with something like this,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def safeSumCirclesDiv(rs: List[String], d: Int = 1): Int = {\n    try {\n      val areas = rs.map((r) => {math.Pi * r.toDouble * r.toDouble})\n      val divs = areas.map(_.toInt\/d)\n      divs.sum \n    } catch {\n      case x: ArithmeticException => {\n        println(\"you cannot divide by \" + d)\n        0\n      }\n      case x: NumberFormatException => {\n        println(\"you must provide parseable numbers\")\n        0\n      }\n      case unknown: Throwable => {\n        println(\"Exception: \" + unknown)\n        0\n      }\n    }\n}<\/pre>\n\n\n\n<p>This works, but presents numerous problems. For starters we return Int 0 in the case of an exception. But what if we want to ignore invalid strings and return a value for the valid input strings? We could do something like this,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def saferSumCirclesDiv(rs: List[String], d: Int = 1): Int = {\n    var acc = 0\n    for (r &lt;- rs) {\n      try {\n        val area = math.Pi * r.toDouble * r.toDouble\n        acc += area.toInt \/ d\n      } catch {\n        case x: ArithmeticException => {\n          println(\"you cannot divide by \" + d)\n        }\n        case x: NumberFormatException => {\n          println(\"you must provide parseable numbers\")\n        }\n        case unknown: Throwable => {\n          println(\"Exception: \" + unknown)\n        }\n      }\n    }\n    acc\n}<\/pre>\n\n\n\n<p>This will work for lists that contain a mix of valid and invalid string inputs, but it&#8217;s not exactly idiomatic, the function is not pure, and it&#8217;s an unwarranted assumption that invalid inputs should result in a zero. A result of zero should not be indicative of input errors.<\/p>\n\n\n\n<h2>Option<\/h2>\n\n\n\n<p>In Scala (as well as in Functional Java libraries) we can use the <a rel=\"noreferrer noopener\" href=\"https:\/\/www.scala-lang.org\/api\/current\/scala\/Option.html\" target=\"_blank\">Option<\/a> monad. This is built-in to Scala, although as we&#8217;ll see, it is fairly easy to implement directly.<\/p>\n\n\n\n<p>For starters, our function signature should not lie (that is, if we can&#8217;t guarantee an Int, we shouldn&#8217;t promise an Int). We should instead return an Option of an Int, e.g., <\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def optionSumCirclesDiv(rs: List[String], d: Int = 1): Option[Int] = ...<\/pre>\n\n\n\n<p>In this monadic approach, we should get the following results,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">scala> optionSumCirclesDiv(List(\"1.5\", \"4.2\"))\nres0: Option[Int] = Some(62)\n\nscala> optionSumCirclesDiv(List(\"1.5\", \"4.2\"), 0)\nres1: Option[Int] = None\n\nscala> optionSumCirclesDiv(List())\nres2: Option[Int] = None\n\nscala> optionSumCirclesDiv(List(\"0.0\"))\nres3: Option[Int] = Some(0)\n\nscala> optionSumCirclesDiv(List(\"1.0\", \"foobar\"))\nres4: Option[Int] = Some(3)<\/pre>\n\n\n\n<p>When using <a rel=\"noreferrer noopener\" href=\"https:\/\/www.scala-lang.org\/api\/current\/scala\/Option.html\" target=\"_blank\">Option<\/a> in Scala, we have either Some or None, and importantly, Some(0) is not the same as None. Let&#8217;s implement this in pieces, and do so in a way that keeps our functions pure. For starters, we have an input List of strings that could contain invalid inputs (like &#8220;foobar&#8221; in the above example). We&#8217;ll want to filter out all entries that cannot be cast to a number. You can do this explicitly with a List.filter expression, but we can also do so by using Option.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def optionDouble(s: String): Option[Double] =\n  try { Some(s.toDouble) } catch { case _: Throwable => None }<\/pre>\n\n\n\n<p>With this function, we can map over a list and then flatten it (removing the Some and None wrappers), which is the same as using a flatMap(),<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">scala> List(\"1.1\", \"foo\", \"4.2\").map(optionDouble)\nres0: List[Option[Double]] = List(Some(1.1), None, Some(4.2))\n\nscala> List(\"1.1\", \"foo\", \"4.2\").map(optionDouble).flatten\nres1: List[Double] = List(1.1, 4.2)\n\nscala> List(\"1.1\", \"foo\", \"4.2\").flatMap(optionDouble)\nres2: List[Double] = List(1.1, 4.2)\n<\/pre>\n\n\n\n<p>We can now filter out all invalid inputs, and be guaranteed a List with zero or more valid inputs. Using this same approach, we can define other functions that use Some and None,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def circled(r: Double) = Some(math.Pi * r * r)\n\ndef divN(y: Int)(x: Double): Option[Int] =\n  try { Some(x.toInt \/ y) } catch { case _: Throwable => None }<\/pre>\n\n\n\n<p>The circled function is obvious, but why create a divN? In this case, we can <a rel=\"noreferrer noopener\" aria-label=\"curry (opens in a new tab)\" href=\"https:\/\/docs.scala-lang.org\/tour\/currying.html\" target=\"_blank\">curry<\/a> divN (within a flatMap) as follows,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">scala> List(2.0, 4.0, 6.0).flatMap(divN(2))\nres0: List[Int] = List(1, 2, 3)\n\nscala> List(2.0, 4.0, 6.0).flatMap(divN(3))\nres1: List[Int] = List(0, 1, 2)<\/pre>\n\n\n\n<p>If we chain together these functions in flatMaps, we&#8217;ll have exactly what we need to implement optionSumCirclesDiv, i.e.,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def optionSumCirclesDiv(rs: List[String], d: Int = 1): Option[Int] = {\n  rs.flatMap(optionDouble).flatMap(circled).flatMap(divN(d)) match {\n    case Nil => None\n    case x => Some(x.sum)\n  }\n}<\/pre>\n\n\n\n<p>And because we have a chain of flatMaps, we can use a for-comprehension, e.g.,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">rs.flatMap(optionDouble).flatMap(circled).flatMap(divN(d))\n\n\/\/ is equivalent to..\n\nfor {\n      s  &lt;- rs\n      r  &lt;- optionDouble(s)\n      a  &lt;- circled(r)\n      ad &lt;- divN(d)(a)\n    } yield ad<\/pre>\n\n\n\n<h2>Either \/ Try<\/h2>\n\n\n\n<p>The only potential problem with using the <a rel=\"noreferrer noopener\" href=\"https:\/\/www.scala-lang.org\/api\/current\/scala\/Option.html\" target=\"_blank\">Option<\/a> monad is that we&#8217;re swallowing our errors. Maybe we want to know what specific exception occurred in our input list. A useful (albeit ugly) approach is to use the <a rel=\"noreferrer noopener\" href=\"https:\/\/www.scala-lang.org\/api\/current\/scala\/util\/Either.html\" target=\"_blank\">Either<\/a> data type. Rather than Some or None, Either can be Left or Right. By convention, Left is used for errors and Right is used for success, e.g.,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def eitherDouble(s: String) = \n  try { Right(s.toDouble) } \n  catch { case _: Throwable => Left(\"Cannot parse \" + s) }\n    \ndef circled(r: Double) = Right(math.Pi * r * r)\n    \ndef divN(y: Int)(x: Double) =\n  try { Right(x.toInt \/ y) } \n  catch { case _: Throwable => Left(\"Cannot divide by \" + y.toString) }<\/pre>\n\n\n\n<p>The return type for these functions is Either[String, Int], and we can use them as follows,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def eitherSumCirclesDiv(rs: List[String], d: Int = 1): Either[String, Int] = {\n    val list = rs.map(eitherDouble)\n                 .map(_.flatMap(circled))\n                 .map(_.flatMap(divN(d)))\n    list.collect{ case Left(x) => x }.map(println) \/\/side effect!\n    list.collect{ case Right(x) => x } match {\n      case Nil => Left(\"No parseable numbers to compute\")\n      case x => Right(x.sum)\n    }\n}<\/pre>\n\n\n\n<p>A more idiomatic approach to this problem is to use the <a rel=\"noreferrer noopener\" aria-label=\"Try (opens in a new tab)\" href=\"https:\/\/www.scala-lang.org\/api\/current\/scala\/util\/Try.html\" target=\"_blank\">Try<\/a> monad. This combines traditional exceptions with a data structure that is similar to Either, but instead of Left\/Right we use the more intuitive Failure\/Success, e.g.,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import scala.util.{Try,Success,Failure}\n     \ndef tryDivArea(d: Int = 1)(s: String) =\n  Try({\n    val r = s.toDouble        \n    val a = math.Pi * r * r\n    a.toInt \/ d\n  }) <\/pre>\n\n\n\n<p>By simply returning a Try() structure, we&#8217;ll get either Success() or a Failure() object that wraps the corresponding exception (e.g., NumberFormatException). We can use this function as follows,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def trySumCirclesDiv(rs: List[String], d: Int = 1): Try[Int] = {    \n    val all = rs.map(tryDivArea(d))\n    all.filter(_.isFailure).map(println) \/\/side effect!\n    all.filter(_.isSuccess).map(_.get) match {\n      case Nil => Failure(new Exception(\"Empty List\"))\n      case x => Try(x.sum)\n    }\n}<\/pre>\n\n\n\n<p>In the above cases we&#8217;re simply printing the exception messages (a side effect to what is otherwise a pure function), and in practice we would not want to do that; keeping our functions pure will allow for proper exception logging as well as <a rel=\"noreferrer noopener\" aria-label=\"parallel streams (opens in a new tab)\" href=\"https:\/\/github.com\/scala\/scala-parallel-collections\" target=\"_blank\">parallel streams<\/a>.<\/p>\n\n\n\n<p>All of the above code can be found on my <a rel=\"noreferrer noopener\" aria-label=\"github (opens in a new tab)\" href=\"https:\/\/github.com\/timwarnock\/experimental-scala\/blob\/master\/redbook\/src\/main\/scala\/errorhandling\/Try.sc\" target=\"_blank\">github<\/a>, and should run correctly in <a rel=\"noreferrer noopener\" aria-label=\"Scastie (opens in a new tab)\" href=\"https:\/\/scastie.scala-lang.org\/\" target=\"_blank\">Scastie<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I would like to handle errors in pure FP (functional programming), something more idiomatic and easier to read than nested try\/catch blocks. Let&#8217;s say we have the following procedure, This is pretty straightforward. Given a list of numbers (represented as strings), compute the areas of these as circles, and then divide by a common divisor [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[23,22],"tags":[],"_links":{"self":[{"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/posts\/1138"}],"collection":[{"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/comments?post=1138"}],"version-history":[{"count":10,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/posts\/1138\/revisions"}],"predecessor-version":[{"id":1167,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/posts\/1138\/revisions\/1167"}],"wp:attachment":[{"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/media?parent=1138"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/categories?post=1138"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/tags?post=1138"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}