David Portabella David Portabella - 1 month ago 17
Scala Question

In Scala, process a list of items with a Try operation, and keep the original item to report the possible failure

I have a list of strings, and I want to make several transformation to each item. I want to keep the original string, so that I can show it if it was invalid. Example as follows:

import scala.util.Try

val list = List("<entry id='1'/>", "haha", "<entry id='hehe'/>")

def parseXML(str: String) = Try { xml.XML.loadString(str) }

list
.map(parseXML)
.map(tryEntry => tryEntry.map(entry => (entry \ "@id").text))
.map(tryId => tryId.flatMap(id => Try(id.toInt)))

// here I lose the original string
res17: List[Try[Int]] = List(
Success(1),
Failure(org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.),
Failure(java.lang.NumberFormatException: For input string: "hehe")
)

// here I keep a copy of the original string, so I can report the invalid entry to the user
list
.map(l => (l, parseXML(l)))
.map { case(line, tryEntry) => (line, tryEntry.map(entry => (entry \ "@id").text)) }
.map { case(line, tryId) => (line, tryId.flatMap(id => Try(id.toInt))) }

res19: List[(String, Try[Int])] = List(
("<entry id='1'/>", Success(1)),
("haha", Failure(org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.)),
("<entry id='hehe'/>", Failure(java.lang.NumberFormatException: For input string: "hehe"))
)


In res19, I keep a copy of the original string, so I can report the error and the original string. However, I need to carry this information each time during a mapping operation, and this is ugly. Is there a better way? (maybe using ScalaZ
State
and
for
?)

Answer

This could elegantly be solved by adding an extra trait ParseContext[T], which, apart from the original context, such as line number and 'original' text, is a Functor (.map(f: T => T)) and a Monad (.flatMap(f: T => ParseContext[T])). You could optionally make two instances of ParseContext, named Success and Failure, to indicate a possible failure during parsing.

Basically, you extend the Try trait with some extra context information.

Comments