0

This is a follow up to my previous question.
Suppose I need to write a function validate in order to make sure that a given list of strings consists of "a", "b", and one or more "c".

def validate(ss: List[String]): Either[NonEmptyList[MyError], Unit] = ???

Suppose I have three functions to check if a given string is "a", "b", or "c":

def validateA(str: String): Either[MyError, Unit] = ???
def validateB(str: String): Either[MyError, Unit] = ???
def validateC(str: String): Either[MyError, Unit] = ???

How to compose those functions to implement validate ?

One solution is a "parser combinators" approach. Define a monad instance for type Validator = Either[NonEmptyList[MyError], List[String]], combinators like oneOrMore similar to parser combinators etc.

I am wondering whether there is an easier solution.

Michael
  • 41,026
  • 70
  • 193
  • 341
  • 1
    Are they sorted? Can you rely on positions to do the validation? (e.g. first must be an `a`, second must be a `b` and then one or more `c`) – Federico Pellegatta Apr 04 '17 at 19:48
  • Instead to use ``Unit``, can you use a ADT ? One tip for your problem, take a loot to the function ``orElse``. – alifirat Apr 04 '17 at 20:04

2 Answers2

3

I suggest you to leverage cats Validated.

Let's define some helper methods, if you really don't want to change your validateT methods signatures:

def validateA_(str: String): ValidatedNel[MyError, Unit] = validateA(str).toValidatedNel
def validateB_(str: String): ValidatedNel[MyError, Unit] = validateB(str).toValidatedNel
def validateC_(str: String): ValidatedNel[MyError, Unit] = validateC(str).toValidatedNel

Then you can implement a validate_ helper function:

import cats.data.Validated.{ invalidNel, valid }

def validate_(ss: List[String]): ValidatedNel[MyError, Unit] = ss match {
  case a :: b :: c if c.nonEmpty =>
    validateA_(a) combine validateB_(b) combine c.traverseU_(validateC_)
  case _ => invalidNel(MyError(???)) //List too short
}

And finally implement your validate function as:

def validate(ss: List[String]): Either[NonEmptyList[MyError], Unit] = 
  validate_(ss).toEither

Assumptions: the input list is sorted and if it is shorter than 3 elements a specific error (e.g. list too short) is acceptable.

Federico Pellegatta
  • 3,977
  • 1
  • 17
  • 29
  • Thank you ! I am afraid though this solution is not generic enough. What if I need to check of the list consists of `one or more a` and then `one or more b` ? – Michael Apr 05 '17 at 06:23
2

Looks like you could make use of Scalactic, which allows one to accumulate errors without short-circuiting the whole validation process.

This example looks quite similar to what you are trying to do. Check it out!

Bianca Tesila
  • 807
  • 6
  • 8