为什么这个Free Monad Interpreter没有将字符串解析为Id [A]

时间:2017-08-26 07:57:46

标签: scala csv functional-programming scala-cats free-monad

我一直在玩免费Monads of Cats。我编写了一个DSL来处理CSV记录。原始操作是处理CSV记录,我自己为sequence操作编写了帮助map2processCSVRecords函数。我希望case类的返回类型是泛型类型R。以下是我正在使用的代码。

import cats.data.Coproduct
import cats.free.Free.inject
import cats.free.{Free, Inject}
import cats.{Id, ~>}
import org.apache.commons.csv.CSVRecord

object ProcessCSVActions {

  sealed trait ProcessCSV[A]

  case class ProcessCSVRecord[R](csvRecord: CSVRecord) extends ProcessCSV[R]

  class ProcessCSVs[F[_]](implicit I: Inject[ProcessCSV, F]) {

    private def sequence[S[_], A](fs: Stream[Free[S, A]]): Free[S, Stream[A]] =
      fs.reverse.foldLeft[Free[S, Stream[A]]](Free.pure[S, Stream[A]](Stream()))((b, a) => map2(a, b)(_ #:: _))

    private def map2[S[_], A, B, C](ra: Free[S, A], rb: Free[S, B])(f: (A, B) => C): Free[S, C] = for {
      a <- ra
      b <- rb
    } yield f(a, b)

    def processCSVRecord[R](csvRecord: CSVRecord): Free[F, R] =
      inject[ProcessCSV, F](ProcessCSVRecord[R](csvRecord))

    def processCSVRecords[R](csvRecords: Stream[CSVRecord]): Free[F, Stream[R]] = {
      val res: Stream[Free[F, R]] = for {
        csvRecord <- csvRecords
      } yield processCSVRecord[R](csvRecord)
      sequence[F, R](res)
    }


  }

  object ProcessCSVs {
    def apply[F[_]](implicit I: Inject[ProcessCSV, F]): ProcessCSVs[F] = new ProcessCSVs[F]
  }

  object StringInterpreterOfCSV extends (ProcessCSV ~> Id) {
    override def apply[A](fa: ProcessCSV[A]): Id[A] = fa match {
      case ProcessCSVRecord(csvRecord) => csvRecord.get(2)
    }
  }
}

现在当我尝试编译上面的代码时,我的解释器出现了以下错误:

[scalac-2.11]  found   : String
[scalac-2.11]  required: cats.Id[A]
[scalac-2.11]     (which expands to)  A
[scalac-2.11]       case ProcessCSVRecord(csvRecord) => csvRecord.get(2)
[scalac-2.11]                                                        ^
[scalac-2.11] one error found

使用Stream处理Free的正确方法是什么?

编辑:

我发现了一个黑客。我在案例类中采用R类型的参数。

  case class ProcessCSVRecord[R](csvRecord: CSVRecord, a:Option[R]) extends ProcessCSV[R]
  def processCSVRecord[R](csvRecord: CSVRecord): Free[F, R] =
      inject[ProcessCSV, F](ProcessCSVRecord[R](csvRecord, None))

在翻译中,我明确给出了与结果匹配的类型。

  object StringInterpreterOfCSV extends (ProcessCSV ~> Id) {
    override def apply[A](fa: ProcessCSV[A]): Id[A] = fa match {
      case ProcessCSVRecord(csvRecord, _: Option[String]) => csvRecord.get(2)
    }
  }

上述工作,但我希望有一个更好的解决方案,而不是这个黑客。

1 个答案:

答案 0 :(得分:2)

由于CSVRecordA无关,因此编译器没有String <:< A的证据。如果这样编译,您就可以创建ProcessCSV[Int]并将其传递给StringInterpreterOfCSV.apply,这是没有意义的。

如果CSVRecord有一个类型参数R,而get(2)返回R,那么它会起作用:

case class ProcessCSVRecord[R](csvRecord: CSVRecord[R]) extends ProcessCSV[R]

否则你可以csvRecord.get(2).asInstanceOf[A]