2015-12-20 14 views
5

Sto tentando di implementare un'istanza di gatti Monad per un tipo con più parametri di tipo. Ho visto l'istanza dei gatti Either per vedere come è stata eseguita lì. Parte del codice EitherMonad esempio dai gatti viene copiato di seguito:Qual è il? genere?

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

Non riesce a compilare con l'errore: error: not found: type ?

Qual è il tipo ? e come posso usarlo per la creazione di istanze per la mia tipi?

+1

'?' È un simbolo valido, è proprio come 'A' in questo caso. –

risposta

8

È una sintassi speciale per il cosiddetto tipo lambda che viene aggiunto dallo kind projector plugin.

Either[A, ?] 

è una scorciatoia per

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

L'intero codice desugars per

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

tipo lambda aspetto spaventoso, ma sono essenzialmente un concetto molto semplice. Hai una cosa che prende due parametri di tipo, come Either[A, B]. Si desidera fornire un'istanza monad per Either, ma lo standard trait Monad[F[_]] richiede un solo parametro di tipo. Ma in linea di principio va bene, dal momento che la tua istanza monad riguarda solo il secondo (il "giusto") argomento di tipo comunque. Un tipo lambda è solo un modo per "sistemare" il primo argomento di tipo in modo da avere la forma giusta.

Se si farebbe la stessa cosa a livello di valore, non ci si potrebbe nemmeno pensare due volte. Hai una funzione di due argomenti

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

E qualcosa che si desidera passare f a, che richiede solo 1 argomento

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

L'unico modo per fare le cose in forma è di fissare uno degli argomenti

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

E questo è esattamente ciò che fa un tipo lambda a livello di tipo.