2017-08-26 5 views
2

Je joue avec Monades gratuit de chats. J'ai écrit un DSL pour traiter les enregistrements CSV. L'opération primitive consiste à traiter CSV enregistrement et j'ai écrit des fonctions d'assistance sequence et map2 pour processCSVRecords opération moi-même. Je veux que le type de retour de la classe de cas soit le type générique R. Voici le code que j'utilise.Pourquoi ce libre Monad interprète pas résoudre Chaîne à 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) 
    } 
    } 
} 

Maintenant, lorsque je tente de compiler le code ci-dessus, je reçois l'erreur ci-dessous pour mon interprète:

[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 

Quelle est la bonne façon de gérer Stream avec Free?

Editer:

J'ai trouvé un hack. Je prends un param de type R dans la classe case.

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

Dans l'interpréteur, je donne explicitement le type correspondant au résultat.

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

Ce qui précède fonctionne, mais j'aurais souhaité qu'il y ait une meilleure solution à la place de ce hack.

+0

Utilisez-vous réellement 'R' pour la classe de cas? Parce que dans votre exemple, cela semble redondant. –

+0

Oui. Je l'utilise dans mon programme (pour la compréhension). J'écris des scripts d'analyse CSV et le modèle est généralement le même. Donc, je voulais réutiliser ma classe de cas et écrire seulement les programmes et les interprètes pour mes scripts. – arjunswaj

Répondre

2

Depuis CSVRecord n'a rien à voir avec A, le compilateur n'a aucune preuve que String <:< A. Si cela se compile, vous pourrez créer un ProcessCSV[Int] et le passer à StringInterpreterOfCSV.apply, ce qui n'a aucun sens.

Si CSVRecord avait un paramètre de type R et get(2) retourné R, il travaillerait alors:

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

Sinon, vous pouvez csvRecord.get(2).asInstanceOf[A].