为什么这个免费的 Monad 解释器不能将 String 解析为 Id[A]
Why does this Free Monad Interpreter not resolve String to Id[A]
我一直在玩 Free Monads of Cats。我写了一个 DSL 来处理 CSV 记录。原始操作是处理 CSV 记录,我自己编写了一个辅助 sequence
和 map2
函数用于 processCSVRecords
操作。我希望 case class 的 return 类型是通用类型 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
用 Free
处理 Stream
的正确方法是什么?
编辑:
我发现了一个技巧。在 class.
的情况下,我采用 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)
}
}
以上方法有效,但我希望有更好的解决方案而不是这个 hack。
由于CSVRecord
与A
无关,编译器没有证据表明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]
。
我一直在玩 Free Monads of Cats。我写了一个 DSL 来处理 CSV 记录。原始操作是处理 CSV 记录,我自己编写了一个辅助 sequence
和 map2
函数用于 processCSVRecords
操作。我希望 case class 的 return 类型是通用类型 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
用 Free
处理 Stream
的正确方法是什么?
编辑:
我发现了一个技巧。在 class.
的情况下,我采用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)
}
}
以上方法有效,但我希望有更好的解决方案而不是这个 hack。
由于CSVRecord
与A
无关,编译器没有证据表明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]
。