2015-12-20 19 views
5

Ich versuche, eine cats Monad Instanz für einen Typ zu implementieren, der mehrere Typparameter hat. Ich schaute auf die Katzen Either Instanz, um zu sehen, wie es dort gemacht wurde.Was ist das? Art?

import cats.Monad 

object EitherMonad { 
    implicit def instance[A]: Monad[Either[A, ?]] = 
    new Monad[Either[A, ?]] { 
     def pure[B](b: B): Either[A, B] = Right(b) 

     def flatMap[B, C](fa: Either[A, B])(f: B => Either[A, C]): Either[A, C] = 
     fa.right.flatMap(f) 
    } 
} 

es mit dem Fehler zu kompilieren schlägt fehl: Ein Teil des EitherMonad Beispiel-Code von Katzen ist unten kopiert error: not found: type ?

Was ist die ? Art und wie kann ich es verwenden, wenn Instanzen für meine eigene Schaffung Arten?

+1

'?' Ist ein gültiges Symbol, in diesem Fall ist es genau wie 'A'. –

Antwort

8

Es ist eine spezielle Syntax für sogenannte Lambda-Typen, die von kind projector plugin hinzugefügt wird.

Either[A, ?] 

ist eine Abkürzung für

({type L[X] = Either[A, X]})#L 

Der gesamte Code desugars zu

import cats.Monad 

object EitherMonad { 
    implicit def instance[A]: Monad[({type L[X] = Either[A, X]})#L] = new Monad[({type L[X] = Either[A, X]})#L] { 
    def pure[B](b: B): Either[A, B] = Right(b) 

    def flatMap[B, C](fa: Either[A, B])(f: B => Either[A, C]): Either[A, C] = 
     fa.right.flatMap(f) 
    } 
} 

Typ lambdas erschreckend aussehen, aber sie sind im Wesentlichen ein sehr einfaches Konzept. Sie haben eine Sache, die zwei Typparameter benötigt, wie Either[A, B]. Sie möchten eine Monad-Instanz für Beide bereitstellen, aber trait Monad[F[_]] benötigt nur einen Typparameter. Aber im Prinzip ist das in Ordnung, da sich Ihre Monaden-Instanz ohnehin nur mit dem zweiten (dem "richtigen") Argument beschäftigt. Ein Lambda-Typ ist nur eine Möglichkeit, das Argument des ersten Typs zu "reparieren", damit Sie die richtige Form haben.

Wenn Sie dasselbe auf Wertstufe tun würden, würden Sie nicht zweimal darüber nachdenken. Sie haben eine Funktion von zwei Argumente

val f: (Int, Int) => Int = ... 

Und etwas, das Sie f, übergeben wollen, das 1-Argument nur

def foo(x: Int => Int) = ... 

Der einzige Weg, die Dinge zu machen, passen ist eines der Argumente zu beheben

foo(x => f(1, x)) 

Und das ist genau das, was ein Typ Lambda auf Typenebene tut.