2014-07-11 4 views
4

Si je veux implicitement convertir deux objets de l'un à l'autre, y a-t-il moyen de le faire en utilisant quelque chose comme une macro Iso?Macro Iso dans Scala

Par exemple, si j'ai ceci:

implicit def listToMap[A, B](l: List[(A, B)]): Map[A, B] = l.toMap 
implicit def mapToList[A, B](m: Map[A, B]): List[(A, B)] = m.toList 

Je veux simplement écrire:

implicit def[A, B] listMapIso = Iso[List[(A, B)], Map[A, B]] {_.toMap, _.toList} 

Note: Comme il est indiqué ci-dessous, je prévois de l'utiliser dans mon framework web où je convertir mes modèles de base de données pour les modèles middleware/frontaux.

+2

Je ne pense pas que vous vouliez vraiment convertir implicitement entre les types comme celui-ci, en particulier les types dans votre exemple. Chaque conversion implicite que vous écrivez est un trou que vous créez dans le typechecker. Exemple: Imaginez qu'après un refactoring, vous passez accidentellement la liste à des fonctions qui prennent Map. 'val m: Carte [Chaîne, Chaîne] = Liste (" a "->" A "," a "->" a ")' – drstevens

+0

C'était juste un exemple ... Je prévois d'utiliser ceci pour convertir et à partir des modèles qui sortent de ma base de données et de mes modèles de calques d'application – pathikrit

Répondre

4

Vous semblez confondre plusieurs concepts différents. Iso, les conversions implicites et les macros sont toutes très différentes les unes des autres.

Nous pouvons certainement définir un équivalent de Iso pour les types paramétrés, bien que la syntaxe devient un peu plus compliqué:

import scalaz._, Scalaz._ 
case class BiIso[F[_, _], G[_, _]](left: F ~~> G, 
    right: G ~~> F) 

type PairList[A, B] = List[(A, B)] 
val listToMap = new (PairList ~~> Map) { 
    def apply[A, B](l: PairList[A, B]) = l.toMap 
} 
val mapToList = new (Map ~~> PairList) { 
    def apply[A, B](m: Map[A, B]) = m.toList 
} 

val listMapIso = BiIso(listToMap, mapToList) 

Nous pouvons bien sûr faire partie de cette implicite, bien que ce soit une préoccupation orthogonale. Nous pouvons construire le BiIso implicitement:

implicit val listToMap = new (PairList ~~> Map) { 
    def apply[A, B](l: PairList[A, B]) = l.toMap 
} 
implicit val mapToList = new (Map ~~> PairList) { 
    def apply[A, B](m: Map[A, B]) = m.toList 
} 

implicit def biIso[F[_, _], G[_, _]](implicit left: F ~~> G, right: G ~~> F) = 
    BiIso(left, right) 

implicitly[BiIso[PairList, Map]] 

Et nous pouvons faire tout acte BiIso comme une conversion implicite, bien que je recommande contre. La seule partie délicate consiste à guider l'inférence de type correctement. Ceci est la plupart du chemin, mais pour une raison quelconque le paramètre ne PAB déduit (une correction serait la bienvenue):

sealed trait BiAny[F[_, _]] {} 
object BiAny { 
    implicit def any[F[_, _]] = new BiAny[F] {} 
} 

sealed trait ApplyBiIso[FAB, GAB] { 
    type A1 
    type B1 
    type F[_, _] 
    type G[_, _] 
    type Required = BiIso[F, G] 

    val unapplyL: Unapply2[BiAny, FAB] { 
    type A = A1; type B = B1; 
    type M[C, D] = F[C, D] 
    } 
    val unapplyR: Unapply2[BiAny, GAB] { 
    type A = A1; type B = B1; 
    type M[C, D] = G[C, D] 
    } 

    def liftBI(bi: Required): Iso[FAB, GAB] = 
    Iso({ fab: FAB => 
     val f: F[A1, B1] = Leibniz.witness(unapplyL.leibniz)(fab) 
     val g: G[A1, B1] = bi.left(f) 
     Leibniz.witness(Leibniz.symm[⊥, ⊤, GAB, G[A1, B1]](unapplyR.leibniz))(g): GAB 
    }, 
     { gab: GAB => 
     val g: G[A1, B1] = Leibniz.witness(unapplyR.leibniz)(gab) 
     val f: F[A1, B1] = bi.right(g) 
     Leibniz.witness(Leibniz.symm[⊥, ⊤, FAB, F[A1, B1]](unapplyL.leibniz))(f): FAB 
     } 
    ) 
} 

object ApplyBiIso { 
    implicit def forFG[FAB, A2, B2, GAB, A3, B3](
    implicit u1: Unapply2[BiAny, FAB] { type A = A2; type B = B2 }, 
    u2: Unapply2[BiAny, GAB] { type A = A3; type B = B3 }) = new ApplyBiIso[FAB, GAB] { 
    type A1 = A2 
    type B1 = B2 
    type F[C, D] = u1.M[C, D] 
    type G[C, D] = u2.M[C, D] 

    //Should do the conversion properly with Leibniz but I can't be bothered 
    val unapplyL = u1.asInstanceOf[Unapply2[BiAny, FAB] { 
     type A = A1; type B = B1; 
     type M[C, D] = F[C, D] 
    }] 
    val unapplyR = u2.asInstanceOf[Unapply2[BiAny, GAB] { 
     type A = A1; type B = B1; 
     type M[C, D] = G[C, D] 
    }] 
    } 
    type Aux[FAB, GAB, Required1] = ApplyBiIso[FAB, GAB] { type Required = Required1 } 
    def apply[FAB, GAB](implicit abi: ApplyBiIso[FAB, GAB]): Aux[FAB, GAB, abi.Required] = abi 
} 

sealed trait AppliedBiIso[FAB, GAB] { 
    val iso: Iso[FAB, GAB] 
} 
object AppliedBiIso { 
    implicit def applyAndIso[FAB, GAB, Required1](
    implicit ap: ApplyBiIso.Aux[FAB, GAB, Required1], 
    iso1: Required1) = new AppliedBiIso[FAB, GAB] { 
    //Should do the conversion properly with Leibniz but I can't be bothered 
    val iso = ap.liftBI(iso1.asInstanceOf[BiIso[ap.F, ap.G]]) 
    } 
} 

implicit def biIsoConvert[FAB, GAB](
    f: FAB)(implicit ap: AppliedBiIso[FAB, GAB]): GAB = 
    ap.iso.left(f) 

val map: Map[String, Int] = Map("Hello" -> 4) 

val list: PairList[String, Int] = 
    biIsoConvert[Map[String, Int], PairList[String, Int]](map) 

Je ne doute pas qu'il est possible de faire ce travail correctement.

Cela laisse encore des macros, qui sont encore une préoccupation plus ou moins orthogonale. Un endroit que je peux voir qu'ils pourraient être pertinents est qu'il est impossible d'abstraire sur kind dans Scala sans utiliser de macros. Voulez-vous un équivalent d'Iso qui fonctionne pour n'importe quelle "forme", pas seulement F[_, _]? Ce serait un bon cas d'utilisation pour une macro - bien qu'ayant écrit ce genre de macro avant que je n'envie quiconque essayant de l'implémenter.

+0

Oui, je me suis dit que nous avions besoin d'une macro pour travailler pour n'importe quelle forme ... – pathikrit