Project Euler presents [problem 17](https://projecteuler.net/problem=17):

> 
> If the numbers 1 to 5 are written out in words: one, two, three,
>  four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in
>  total. If all the numbers from 1 to 1000 (one thousand) inclusive
>  were written out 
>  in words, how many letters would be used?
> 
> 
> 

Please review my answer. In particular, please comment on my usage of the Applicative.

```
import scalaz.syntax.applicative._
import scalaz.std.option._
import scala.util.matching.Regex
import scala.annotation.tailrec

object Problem17 {

    val oneDigit: Regex = "^[1-9]$".r
    val twoDigit: Regex = "^[1-9][0-9]$".r
    val threeDigit: Regex = "^[1-9][0-9][0-9]$".r 
    val fourDigit: Regex = "^[1-9][0-9][0-9][0-9]$".r

    private def isOneToFourDigitNum(x: String): Boolean = 
        x.matches(oneDigit.toString) |
        x.matches(twoDigit.toString) |
        x.matches(threeDigit.toString) |
        x.matches(fourDigit.toString)

    // If all the numbers from 1 to 1000 (one thousand) inclusive were written out 
    // in words, how many letters would be used?
    def runProblem: Option[Int] = {
        val oneToThousand: List[Int] = (1 to 1000).toList
        val numberList: List[String] = oneToThousand.map(_.toString)
        val wordLengths: List[Option[Int]] = numberList.map(getLengthOfMaybeNumberWord)
        wordLengths match {
            case Nil        => None
            case x :: xs    => xs.foldLeft[Option[Int]](x) { 
                (acc: Option[Int], elem: Option[Int]) => ^(acc, elem)(_ + _)
            }
        }
    }

    def getLengthOfMaybeNumberWord(number: String): Option[Int]= {
      if (isOneToFourDigitNum(number)) {
        val numberAsWord: Option[String] = getNumberAsWord(number)
        val wordFiltered: Option[String] = numberAsWord.flatMap(a => Some(a.filter(x => x != ' ').filter(x => x != '-')))
        wordFiltered.map(_.length)
      }
      else None
    }

    def getNumberAsWord(num: String): Option[String] = {
        @tailrec
        def go(numbers: String)(acc: Option[String]): Option[String] = numbers.toList match {
            case Nil => acc
            case a :: b :: c :: d :: Nil =>  {
                val rest: String = b.toString + c.toString + d.toString
                val thousandWord = ^(convertSingleDigitOnes(a), Some(" thousand"))(_ ++ _)
                val newAcc = ^(acc, thousandWord)(_ + _)
                go(rest)(newAcc)
            }
            case b :: c :: d :: Nil => {
                val rest: String = c.toString + d.toString
                val hundredWord = convertSingleDigitHundred(b)
                val newAcc = ^(acc, hundredWord)(_ + _)
                go(rest)(newAcc)
            }
            case '0' :: '0' :: Nil => acc
            case c :: d :: Nil     => acc match {
                case Some("") => {
                    val twoDigitsWord = convertTwoDigits(c)(d)
                    val newAcc = ^(acc, twoDigitsWord)(_ + _)
                    go("")(newAcc)          
                }
                case Some(_: String) => {
                    val twoDigitsWord: Option[String] = convertTwoDigits(c)(d)          
                    val addingAnd: Option[String] = ^(Some(" and "), twoDigitsWord)(_ + _)
                    val newAcc = ^(acc, addingAnd)(_ + _)
                    go("")(newAcc)  
                }
                case None => None

            }
            case d :: Nil          => convertSingleDigitOnes(d)
            case _                 => None
        }        
        go(num)(Some(""))
    }

    private def convertTwoDigits(tens: Char)(ones: Char): Option[String] = (tens, ones) match {
        case ('0', _)  => convertSingleDigitOnes(ones)
        case ('1', _)  => convertTensWithOne(ones)
        case (_, '0')  => convertTens(tens)
        case (_, _)    => ^(convertTens(tens).map(_ ++ "-"), convertSingleDigitOnes(ones))(_ + _)
    }

    // Converts a 2-digit number to its word equivalent *where* "1" is in the tens column
    // Examples: 15, 12, 18, etc.
    private def convertTensWithOne(ones: Char): Option[String] = ones match {
        case '0' => Some("ten")
        case '1' => Some("eleven")
        case '2' => Some("twelve")
        case '3' => Some("thirteen")
        case '4' => Some("fourteen")
        case '5' => Some("fifteen")
        case '6' => Some("sixteen")
        case '7' => Some("seventeen")
        case '8' => Some("eighteen")
        case '9' => Some("nineteen")
        case _    => None
    }

    // Given a single Digit in the Ones column, return its corresponding word
    // Example: f(1) -> "one", f(2) -> "two"    
    private def convertSingleDigitOnes(x: Char): Option[String] = x match {
        case '1' => Some("one")
        case '2' => Some("two")
        case '3' => Some("three")
        case '4' => Some("four")
        case '5' => Some("five")
        case '6' => Some("six")
        case '7' => Some("seven")
        case '8' => Some("eight")
        case '9' => Some("nine")
        case '0' => Some("zero")
        case  _  => None
    }

        // Given a single Digit in the Ones column, return its corresponding word
    // Example: f(1) -> "one", f(2) -> "two"    
    private def convertSingleDigitHundred(x: Char): Option[String] = x match {
        case '1' => Some("one hundred")
        case '2' => Some("two hundred")
        case '3' => Some("three hundred")
        case '4' => Some("four hundred")
        case '5' => Some("five hundred")
        case '6' => Some("six hundred")
        case '7' => Some("seven hundred")
        case '8' => Some("eight hundred")
        case '9' => Some("nine hundred")
        case '0' => Some("")
        case  _  => None
    }

    // Given a single Digit in the Tens column, return its corresponding word.
    // Example: f(2) -> "twenty", f(5) = "fifty"
    // Due to 1's uniqueness, i.e. 15 = "fifteen", not "ten-five", it's handled in 
    // `convertTensWithOne`.
    private def convertTens(x: Char): Option[String] = x match {
        case '2' => Some("twenty")
        case '3' => Some("thirty")
        case '4' => Some("forty")
        case '5' => Some("fifty")
        case '6' => Some("sixty")
        case '7' => Some("seventy")
        case '8' => Some("eighty")
        case '9' => Some("ninety")
        case _   => None
    }
}

```