+ muscians.db + test/scala/{Musicians, testMusicians}.scala
musicians.db simplified; readFromFile now returns Iterable[Checked...] instead of tuple; runs all elements contain 'unrecognised genre' even though recognised, and unrecognised instrument, None.get

musicians.db
-Wonder, Stevie, (pop), (voice, piano)
+  Stevie Wonder,            pop, voice, piano
and similar

Musicians
+import io.Source
+  type R = String
   object Instrument extends Enumeration {
-    val panPipes, sax, trumpet, voice = Value
+    val panPipes = Value("pan pipes")
+    val sax, trumpet, voice = Value

-  case class Musician(   surname: String,
-                       firstname: String,
-                           genre: Genre,
-                      instrument: Instrument)
+  case class Musician(       name: String,
+                            genre: Genre,
+                      instruments: Iterable[Instrument])

+  object Musician {

-  def readFromFile(name: String): (Iterable[Musician], Iterable[String]) = {
+  def readFromFile(fname: String): Iterable[Checked[Musician, Iterable[String]]] = for {

testMusicians
-  val (musicians, errors) = readFromFile("musicians.db")
-  musicians foreach println
-  errors foreach println
+  readFromFile("musicians.db") foreach println
Musicians: obvious fixes applied - almost there!

   object Genre extends Enumeration {
-    val easyListening, jazz, pop = Value
+    val easyListening = Value("easy listening")
+    val jazz, pop = Value

       def genre(s: String) = try {
-        Genre.withName(s) match {
+        Genre.withName(s.trim) match {

       def instruments(ss: Iterable[String]) = try {
-        val instruments = for (s <- ss) yield Instrument.withName(s)
+        val instruments = for (s <- ss) yield Instrument.withName(s.trim)

Reason(List(unrecognised instrument: None.get))
Reason(List(unrecognised instrument: None.get))
Okay(Musician(Michael Brecker,jazz,ArrayBuffer(sax)))
Okay(Musician(  Randy Brecker,jazz,ArrayBuffer(trumpet)))
Okay(Musician( Charlie Parker,jazz,ArrayBuffer(sax)))
Reason(List( easy listening, includes pan pipes))
Reason(List( easy listening))
Reason(List( easy listening, unrecognised instrument: None.get))
Musicians: final fixes - now fully working!

   object Instrument extends Enumeration {
-    val sax, trumpet, voice = Value
+    val piano, sax, trumpet, voice = Value

     object checks {
       def genre(s: String) = try {
-        Genre.withName(s.trim) match {
+        Genre.withName(s) match {

       def instruments(ss: Iterable[String]) = try {
-        val instruments = for (s <- ss) yield Instrument.withName(s.trim)
+        val instruments = for (_s <- ss; s = _s.trim) yield try {
+          Instrument.withName(s)
+        } catch {
+          case ex: NoSuchElementException =>
+            throw new NoSuchElementException("unrecognised instrument: "+ s)
+        }

       catch {
-        case ex: NoSuchElementException => Reason("unrecognised instrument: "+ ex.getMessage)
+        case ex: NoSuchElementException => Reason(ex.getMessage)

-    fs((apply _).curried) <*> name(tokens(0)) <*>
+    fs((apply _).curried) <*> name(tokens(0).trim) <*>
-                              genre(tokens(1)) <*>
+                              genre(tokens(1).trim) <*>
testMusicians: comment added showing input and resulting output
!{Musicians, testMusicians}: "includes " => "plays the "

Musicians
-        if (instruments exists(_ == panPipes)) Reason("includes "+ panPipes)
+        if (instruments exists(_ == panPipes)) Reason("plays the "+ panPipes)

testMusicians
-// Reason(List(easy listening, includes pan pipes))
+// Reason(List(easy listening, plays the pan pipes))
Musician: now using toOkay and toReason, although not much in it

-      def name(s: String) = if (s.length > 0) Okay(s) else Reason("empty name")
+      def name(s: String) = if (s.length > 0) s toOkay else "empty name" toReason

-          case `easyListening` => Reason(s)
+          case `easyListening` => s toReason

-          case other => Okay(other)
+          case other => other toOkay

-        else Okay(instruments)
+        else instruments toOkay

-        case ex: NoSuchElementException => Reason(ex.getMessage)
+        case ex: NoSuchElementException => ex.getMessage toReason
testMusicians renamed printMusicians
+ printMusicians2
Musician: now takes a Set of instruments (instead of an Iterable); printInstruments2 now looks complete

   case class Musician(       name: String,
                             genre: Genre,
-                      instruments: Iterable[Instrument])
+                      instruments: Set[Instrument])

   object Musician {
     object checks {

      def instruments(ss: Iterable[String]) = try {

         if (instruments exists(_ == panPipes)) Reason("plays the "+ panPipes)
-        else instruments toOkay
+        else instruments.toSet.toOkay

printMusicians2
   val okayMusos = for {
     checkedMuso <- checkedMusos
     if checkedMuso.isOkay
-  } yield checkedMuso
+    okayMuso = checkedMuso.get
+  } yield okayMuso

+  def toText(muso: Musician) = {
+  def toCheckedText(chMuso: Checked[Musician, Iterable[String]]) = chMuso match {
+  val checkedFormattedMusos = checkedMusos map toCheckedText
+  checkedFormattedMusos foreach println
+  val checkedFormattedMusos2 = checkedMusos map { _ map toText }
+  checkedFormattedMusos2 foreach println
+  // Okay(Stevie Wonder plays pop piano and sings.)
+  // ...
TestsInvolvingOption: added four more, this time using map (which is more concise)

+  // however, this my be done more concisely using map:
+  def toHead(ss: Seq[String]) = for {
+  test("map, Seq non-empty, head non-empty 3") {
+  test("map, Seq non-empty, head empty 3") {
+  test("map, Seq empty 3") {
+  test("map, Reason 3") {
printMusicians2 now subsumed into printMusicians
Musicians: readFromFile: return-type now inferred; header and doc-commented added; printMusicians: header added