Jader Martins
Jader Martins

Reputation: 789

traverse a string in cats/scalaz

I want to traverse a String in the following way:

import cats.implicits._

object RnaTranscription {
  val mMap: Map[Char, Option[Char]] =
    Map('G' -> Some('C'),
        'C' -> Some('G'),
        'T' -> Some('A'),
        'A' -> Some('U')).withDefaultValue(None)

  def toRna(dna: String): Option[String] = {
    dna.toList.traverse(mMap).map(_.mkString)
  }
}

But it has extra steps, I need to cast to a List[Char] and then mkString again, is there a way in cats or scalaz to traverse a String without casting to list?

Upvotes: 2

Views: 146

Answers (2)

Yaneeve
Yaneeve

Reputation: 4779

As @BogdanVakulenko implied in his answer, String is not a Functor (F[_]).

The Traverse typeclass in cats had the following declaration:

@typeclass trait Traverse[F[_]] extends Functor[F] with Foldable[F] with UnorderedTraverse[F] { self => ... }

The way that you have solved it with toList and mkString is fine by me, but, if you'd like a plain vanilla Scala version that works is:

  def toRnaScala(dna: String): Option[String] = {
    val maybeChars: immutable.Seq[Option[Char]] = dna.map(mMap)
    maybeChars.foldLeft(Option("")) {
      case (acc, Some(c)) => acc.map(_ + c)
      case (_, None) => None
    }
  }

Upvotes: 4

Bogdan Vakulenko
Bogdan Vakulenko

Reputation: 3390

Maybe something like this:

def toRna(dna: String): Option[String] = {
  Some(dna.map(mMap).flatten.mkString)
}

There is no way to use traverse on string directly as string is native java structure. There is an implicit conversion inside cats/scalaz that adds traverse method to collections. This implicit can be applied only for types with one type parameter ((* -> *) or F[_]). String is just a T, so scala cant apply this implicit conversion.

implicit def toTraverseOps[F[_], C](target : F[C])
            (implicit tc : cats.Traverse[F]) : Traverse.Ops[F, C]

Upvotes: 2

Related Questions