2014-09-18 13 views
16

ile ardışık olarak vaka sınıflarını heterojen listelere dönüştürmeye çalışırken garip davranışlar Geçen gece bu Shapeless sorununu anlamaya çalışmak için çok geç kaldım ve korkarım ki, eğer bunu başaramazsam akşamımı yiyeceğimden korkuyorum Göğsüm, işte burada. Bu minimize versiyondaGarip davranışlar, Shapeless

Sadece heterogeneous listeler halinde yinelemeli durum sınıfları dönüştürecek bir tip sınıfını tanımlayan ediyorum:

import shapeless._ 

trait DeepHLister[R <: HList] extends DepFn1[R] { type Out <: HList } 

trait LowPriorityDeepHLister { 
    type Aux[R <: HList, Out0 <: HList] = DeepHLister[R] { type Out = Out0 } 

    implicit def headNotCaseClassDeepHLister[H, T <: HList](implicit 
    dht: DeepHLister[T] 
): Aux[H :: T, H :: dht.Out] = new DeepHLister[H :: T] { 
    type Out = H :: dht.Out 
    def apply(r: H :: T) = r.head :: dht(r.tail) 
    } 
} 

object DeepHLister extends LowPriorityDeepHLister { 
    implicit object hnilDeepHLister extends DeepHLister[HNil] { 
    type Out = HNil 
    def apply(r: HNil) = HNil 
    } 

    implicit def headCaseClassDeepHLister[H, R <: HList, T <: HList](implicit 
    gen: Generic.Aux[H, R], 
    dhh: DeepHLister[R], 
    dht: DeepHLister[T] 
): Aux[H :: T, dhh.Out :: dht.Out] = new DeepHLister[H :: T] { 
    type Out = dhh.Out :: dht.Out 
    def apply(r: H :: T) = dhh(gen.to(r.head)) :: dht(r.tail) 
    } 

    def apply[R <: HList](implicit dh: DeepHLister[R]): Aux[R, dh.Out] = dh 
} 

Şunu deneyelim! Önce bazı örnek sınıfları gerekir:

scala> DeepHLister[A :: HNil] 
res0: DeepHLister[A :: HNil]{ 
    type Out = (Int :: String :: HNil) :: HNil 
} = [email protected] 

scala> DeepHLister[B :: HNil] 
res1: DeepHLister[B :: HNil] { 
    type Out = (
    (Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil 
) :: HNil 
} = [email protected] 

scala> DeepHLister[C :: HNil] 
res2: DeepHLister[C :: HNil] { 
    type Out = (
    ((Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil) :: 
    (Int :: String :: HNil) :: 
    HNil 
) :: HNil 
} = [email protected] 

Şimdiye kadar: o zaman

case class A(x: Int, y: String) 
case class B(x: A, y: A) 
case class C(b: B, a: A) 
case class D(a: A, b: B) 

Ve (Ben tamamen okunmaz karmaşa olmaması bu uğruna tipi sözdizimi temizledim unutmayın) iyi. Ama sonra:

scala> DeepHLister[D :: HNil] 
res3: DeepHLister[D :: HNil] { 
    type Out = ((Int :: String :: HNil) :: B :: HNil) :: HNil 
} = [email protected] 

B dönüştürülür vermedi. Biz -Xlog-implicits açarsanız bu son mesajdır:

<console>:25: this.DeepHLister.headCaseClassDeepHLister is not a valid implicit value for DeepHLister[shapeless.::[B,shapeless.HNil]] because: 
hasMatchingSymbol reported error: diverging implicit expansion for type DeepHLister[this.Repr] 
starting with method headNotCaseClassDeepHLister in trait LowPriorityDeepHLister 
       DeepHLister[D :: HNil] 
         ^

duygusu headCaseClassDeepHListerDeepHLister[B :: HNil] gayet üretmek mümkün olmalıdır sonradır için yapmaz ve doğrudan sorarsanız öyle.

Bu, hem 2.10.4 hem de 2.11.2'de ve hem 2.0.0 sürümü hem de master ile gerçekleşir. Bunun bir hata olması gerektiğine eminim, fakat yanlış bir şey yaptığım olasılığını reddetmiyorum. Daha önce böyle bir şey gören oldu mu? Benim mantığım veya Generic eksikliğimdeki bazı kısıtlamalar var mı?

Tamam, dinlediğiniz için teşekkürler - belki şimdi bir kitap okuyabiliyorum.

+0

DeepLister [B :: HNil] 'ile benzer bir sorunum var. Kodu 5 dakika önce kopyaladım, böylece daha fazla araştırma yapacağım. – EECOLOR

+0

Bu, whitebox makroları ile oldukça kolay olurdu. –

+1

@ GuillaumeMassé, Doğru, kesinlikle, ama amaç Makroların kullanımını Shapeless tarafından sağlanan birkaç temel olanla sınırlamaktır. –

cevap

6

Bu şimdi son şekilsiz-2.1.0-SNAPSHOT kurar kullanarak daha fazla veya daha az olarak yazılır çalışır ve bu söz konusu numunenin yakın akrabası vardır example olarak eklenmiştir.

orijinal sorunu bir Generic her genleşme ilke olarak, ancak bazı daha karmaşık ilişkili olan bir HList türü üretmek olabilir, DeepHLister tip sınıfı örnekleri örtülü çözünürlük yeni bir HList tipi dahil eder ve olmasıdır Aynı çözünürlükte daha önce görülen tip. Bu durum uzaklaşma denetleyicisini gezer ve çözüm sürecini iptal eder.

bu D için değil C kaba bir yaklaşımla, Scala'nın typechecker uygulanması ayrıntılarda gizlenen ama için bunun neden tam ayrıntıları, farklılaştırıcı C için çözünürlük sırasında gördüğümüz olmasıdır B (daha büyük A'dan (daha küçük) önce, sapma denetleyicisi türlerimizin yakınlaşmasından mutlu olur; D çözünürlüğü sırasında tersine B (daha büyük) önce A (daha küçük) görüyoruz böylece divergence denetleyicisi (conservatif) kefalet eder.

Bunun için shapeless 2.1.0'daki düzeltme, yeni geliştirilmiş Lazy tipi yapıcı ve ilişkili örtülü makro altyapısıdır. Bu, uzaklaşma üzerinde çok daha fazla kullanıcı kontrolüne izin verir ve özyinelemeli türler için tip sınıf örneklerini otomatik olarak türetme yeteneği için çok önemli olan özyeterel örtülü değerleri oluşturmak için örtülü çözümün kullanılmasını destekler. Bunun pek çok örneği, özellikle adanmış tip sınıf türetme altyapısı ve artık özel makro desteğine ihtiyaç duymayan, ancak Generic ve Lazy temel koşullarına göre uygulanmış olan Boilerplate uygulamasındaki biçimsiz kod tabanında bulunabilir. Bu mekanizmaların çeşitli uygulamaları, şekilsiz örnek alt projesinde bulunabilir.

+0

Şekilsiz şekil 2.3.2 için değişiklikler var mı? Örnek, 2 kafanın * döndürme türlerinden şikayet ettiğinden dolayı derleme yapmıyor ... Ölçek kullanıyorum 2.11.11 – Damian

+0

2.11.11 ile bir hatayı yeniden üretebiliyorsanız, lütfen bu durumu, sorunu olmayan sorun izleyicide bildirin. –

6

Biraz farklı bir yaklaşım benimsedim.

trait CaseClassToHList[X] { 
    type Out <: HList 
} 

trait LowerPriorityCaseClassToHList { 
    implicit def caseClass[X](implicit gen: Generic[X]): CaseClassToHList[X] { 
    type Out = generic.Repr 
    } = null 
} 

object CaseClassToHList extends LowerPriorityCaseClassToHList { 
    type Aux[X, R <: HList] = CaseClassToHList[X] { type Out = R } 

    implicit def caseClassWithCaseClasses[X, R <: HList](
    implicit toHList: CaseClassToHList.Aux[X, R], 
    nested: DeepHLister[R]): CaseClassToHList[X] { 
    type Out = nested.Out 
    } = null 
} 

trait DeepHLister[R <: HList] { 
    type Out <: HList 
} 

object DeepHLister { 

    implicit def hnil: DeepHLister[HNil] { type Out = HNil } = null 

    implicit def caseClassAtHead[H, T <: HList](
    implicit head: CaseClassToHList[H], 
    tail: DeepHLister[T]): DeepHLister[H :: T] { 
    type Out = head.Out :: tail.Out 
    } = null 

    def apply[X <: HList](implicit d: DeepHLister[X]): d.type = null 
} 

aşağıdaki kodla test edilmiştir:

case class A(x: Int, y: String) 
case class B(x: A, y: A) 
case class C(b: B, a: A) 
case class D(a: A, b: B) 

object Test { 

    val z = DeepHLister[HNil] 
    val typedZ: DeepHLister[HNil] { 
    type Out = HNil 
    } = z 

    val a = DeepHLister[A :: HNil] 
    val typedA: DeepHLister[A :: HNil] { 
    type Out = (Int :: String :: HNil) :: HNil 
    } = a 

    val b = DeepHLister[B :: HNil] 
    val typedB: DeepHLister[B :: HNil] { 
    type Out = ((Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil) :: HNil 
    } = b 

    val c = DeepHLister[C :: HNil] 
    val typedC: DeepHLister[C :: HNil] { 
    type Out = (((Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil) :: (Int :: String :: HNil) :: HNil) :: HNil 
    } = c 

    val d = DeepHLister[D :: HNil] 
    val typedD: DeepHLister[D :: HNil] { 
    type Out = ((Int :: String :: HNil) :: ((Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil) :: HNil) :: HNil 
    } = d 
} 
+0

Hey @EECOLOR, teşekkürler - Buna daha fazla zaman harcamayı düşünüyordum ve ödülün süresinin dolduğunu anlamadım. Biraz zaman aldığımda başka bir ödül daha ekleyeceğim. –

İlgili konular