# Naming a not-quite-flatMap method

I recently created Argyle, a small library for parsing command-line arguments in Scala. I'd be happy for anyone to take a look and provide general feedback on it. But there is also one specific issue that sparked some discussion when I posted on reddit, so I would be interested to hear the thoughts of folks here also. Basically, I have the following trait:

trait Arg[+A] {
def parse(xs: List[String], mode: ArgMode): Try[A]
def flatMap[B](f: A => Try[B]): Arg[B]
}


The idea is that an Arg[A] handles parsing (and potentially failing to parse) a list of strings into an A. The issue is over the name of the flatMap method. It's a useful method, for instance, converting an Arg[String] to Arg[Int] by .flatMap(s => Try(s.toInt)). But it's not a valid FP Monad flatMap since the parameter type is A => Try[B] and not A => Arg[B].

If you view Arg as a function List[String] => Try[A], then it is actually pretty identical to .andThen(_.flatMap(f)). But I don't know if there's a canonical FP name for an operation like this.

In my mind, pros of naming it flatMap:

• Similar enough to other flatMap methods that it gives some immediate understanding of how it should function
• Enables for-comprehension with chains of Trys and such, that work as expected

Cons:

• Confusing for those who expect flatMap always implies monad, since Arg is not a monad.
• Potentially confusing that for-comprehensions can't use chains of Args, but that seems nonsensical anyway, and probably shouldn't be allowed.

Any other thoughts, or suggestions for a better name? There is also a map method to deal with.

I would consider creating an Arrow type from Arg[A] to Arg[B], something like:

import scalaz.Arrow // Also available in Cats

case class ArgArrow[A, B](run: Arg[A] => Arg[B])

object ArgArrow {
implicit val arrowInstance: Arrow[ArgArrow] = new Arrow[ArgArrow] {
def arrTry[A, B](f: A => Try[B]): ArgArrow[A, B] = ???
def arrOption[A, B](f: A => Option[B]): ArgArrow[A, B] = ???
// implement other required functions
}
}


With Arrow, it will be very easy to assemble different things together, for example, you can easily combine an A => B, an A => Try[C], and an A => Option[D] into an ArgArrow[A, (B, C, D)].

I would be inclined to move the mapping behavior into the Arg for composability

import scala.util.Try
trait ArgMode

trait ArgParse[I, O] {
def parse(x: I, mode: ArgMode): Try[O]

final def andThen[B](that: ArgParse[O, B]): ArgParse[I, B] = {
ArgParse((x, mode) => parse(x, mode).flatMap((that.parse(_, mode))))
}
}

object ArgParse {

def apply[I, O](f: (I, ArgMode) => Try[O]) = new ArgParse[I, O] {
override def parse(x: I, mode: ArgMode): Try[O] = f(x, mode)
}

def stringsToInt = ArgParse[List[String], Int]((xs, _) => Try(xs.head.toInt))

def intToInts = ArgParse[Int, Seq[Int]] ((x, _) => Try(1 to x map (_ => x)))

}


then compose away

scala> ArgParse.stringsToInt.andThen(ArgParse.intToInts).parse(List("5"), new ArgMode {})
res1: scala.util.Try[Seq[Int]] = Success(Vector(5, 5, 5, 5, 5))