2017-08-26 1 views
2

Ich habe mit Free Monads of Cats gespielt. Ich habe eine DSL geschrieben, um CSV Records zu verarbeiten. Die primitive Operation ist CSV-Datensatz zu verarbeiten und ich habe einen Helfer sequence und map2 Funktionen für processCSVRecords Betrieb selbst geschrieben. Ich möchte den Rückgabetyp der Fallklasse als generischen Typ R. Unten ist der Code, den ich verwende.Warum löst dieser Free Monad Interpreter String nicht zu ID [A]

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) 
    } 
    } 
} 

Jetzt, wenn ich versuche, den obigen Code zu kompilieren, erhalte ich die folgenden Fehler für meinen Dolmetscher:

[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 

Was ist der richtige Weg ist Stream mit Free zu behandeln?

Bearbeiten:

Ich habe einen Hack gefunden. Ich nehme einen Parameter vom Typ R in der Fallklasse.

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)) 

Im Interpreter gebe ich explizit den Typ, der dem Ergebnis entspricht.

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

Das obige funktioniert, aber ich wünschte, es gäbe eine bessere Lösung anstelle dieses Hacks.

+0

Verwenden Sie tatsächlich 'R' für die Fallklasse? Weil es in Ihrem Beispiel überflüssig erscheint. –

+0

Ja. Ich benutze es in meinem Programm (zum Verständnis). Ich schreibe CSV-Parsing-Skripte und normalerweise ist das Muster dasselbe. Also wollte ich meine Fallklasse wiederverwenden und nur die Programme und Interpreter für meine Skripte schreiben. – arjunswaj

Antwort

2

Da CSVRecord nichts mit A zu tun hat, hat der Compiler keinen Beweis, dass String <:< A. Wenn dies kompiliert wird, können Sie ProcessCSV[Int] erstellen und an StringInterpreterOfCSV.apply übergeben, was keinen Sinn ergibt.

Wenn CSVRecord hatte einen Typparameter R und get(2) zurück R, dann würde es funktionieren:

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

Ansonsten können Sie csvRecord.get(2).asInstanceOf[A].

Verwandte Themen