2016-03-07 13 views
9

Ho la seguente situazione, dato due tipi MA e MB, vorrei essere in grado di dimostrare che entrambi hanno non solo uno Applicative ma anche che entrambi hanno la stessa forma sottostante. Ho provato a fare quanto segue:Utilizzare Unapply per estrarre le classi di tipi identici

type UnapplyM[TC[_[_]], MA, M0[_]] = Unapply[TC, MA]{ type M[X] = M0[X] } 

implicit def thing[MA, MB, M[_]](implicit un: UnapplyM[Applicative,MA,M], un2: UnapplyM[Applicative,MB,M]) = ... 

ma continuare a correre in impliciti divergenti (vale a dire questo non funziona.) Cose simili possono essere fatte con una proiezione di tipo sul A tipo param di Unapply e lavoro.

Questo è un modo per prendere questi due tipi ed essere in grado di dimostrare che sono, infatti, supportati dalla stessa istanza di classe tipo?

risposta

9

Inizierò col dire che una risposta completa sarebbe una storia molto lunga, e ne ho parlato gran parte in a blog post dall'estate scorsa, quindi ho intenzione di sfogliare alcuni dettagli qui e solo fornire un'implementazione funzionante di thing per Cats.

Un'altra nota introduttiva: questa macchina ora esiste in Scalaz e some of the "review" su my pull request aggiungendo che vi è una delle molte ragioni per cui sono felice che i gatti esistano. :)

prima per una classe di tipo totalmente opaco che non voglio nemmeno provare a motivare qui:

case class SingletonOf[T, U <: { type A; type M[_] }](
    widen: T { type A = U#A; type M[x] = U#M[x] } 
) 

object SingletonOf { 
    implicit def mkSingletonOf[T <: { type A; type M[_] }](implicit 
    t: T 
): SingletonOf[T, t.type] = SingletonOf(t) 
} 

Poi si può definire un IsoFunctor, dal momento che i gatti non sembrano avere una al momento :

import cats.arrow.NaturalTransformation 

trait IsoFunctor[F[_], G[_]] { 
    def to: NaturalTransformation[F, G] 
    def from: NaturalTransformation[G, F] 
} 

object IsoFunctor { 
    implicit def isoNaturalRefl[F[_]]: IsoFunctor[F, F] = new IsoFunctor[F, F] { 
    def to: NaturalTransformation[F, F] = NaturalTransformation.id[F] 
    def from: NaturalTransformation[F, F] = to 
    } 
} 

potremmo usare qualcosa di un po 'più semplice di IsoFunctor per quello che stiamo per fare, ma è un bel tipo classe di principio, ed è quello che ho usato in Scalaz, quindi mi bastone con esso qui .

successivo per una nuova, Unapply che mette insieme insieme due Unapply casi:

import cats.Unapply 

trait UnapplyProduct[TC[_[_]], MA, MB] { 
    type M[X]; type A; type B 
    def TC: TC[M] 
    type MA_ = MA 
    def _1(ma: MA): M[A] 
    def _2(mb: MB): M[B] 
} 

object UnapplyProduct { 
    implicit def unapplyProduct[ 
    TC[_[_]], MA0, MB0, 
    U1 <: { type A; type M[_] }, 
    U2 <: { type A; type M[_] } 
    ](implicit 
    sU1: SingletonOf[Unapply[TC, MA0], U1], 
    sU2: SingletonOf[Unapply[TC, MB0], U2], 
    iso: IsoFunctor[U1#M, U2#M] 
): UnapplyProduct[TC, MA0, MB0] { 
    type M[x] = U1#M[x]; type A = U1#A; type B = U2#A 
    } = new UnapplyProduct[TC, MA0, MB0] { 
    type M[x] = U1#M[x]; type A = U1#A; type B = U2#A 
    def TC = sU1.widen.TC 
    def _1(ma: MA0): M[A] = sU1.widen.subst(ma) 
    def _2(mb: MB0): M[B] = iso.from(sU2.widen.subst(mb)) 
    } 
} 

Come nota a margine storica, UnapplyProduct esiste da quattro anni in Scalaz prima che avesse tutte le istanze utili.

E ora siamo in grado di scrivere qualcosa del genere:

import cats.Applicative 

def thing[MA, MB](ma: MA, mb: MB)(implicit 
    un: UnapplyProduct[Applicative, MA, MB] 
): Applicative[un.M] = un.TC 

E poi:

scala> import cats.data.Xor 
import cats.data.Xor 

scala> thing(Xor.left[String, Int]("foo"), Xor.right[String, Char]('a')) 
res0: cats.Applicative[[x]cats.data.Xor[String,x]] = [email protected] 

E abbiamo parlato con successo il compilatore ad identificare come abbattere questi tipi Xor in tale in modo che possa vedere l'istanza rilevante Applicative (che restituiamo).

+1

Come nota a piè di pagina, il 100% della colpa per l'idea di "SingletonOf' cade su Miles Sabin (ovviamente): https://gist.github.com/milessabin/cadd73b7756fe4097ca0 –

+0

Ugh, a volte davvero odio assolutamente Scala per facendoci andare a tanto. – wheaties

Problemi correlati