üye

2016-04-25 30 views
7

I genel olarak böyle bir sızdırmaz durumda sınıfı ailesi için bir kodek elde kolayca olabilir tip (mühürlü): Bir ekleme durumunda, ancaküye

import io.circe._ 
import io.circe.generic.auto._ 

sealed trait Base 
case class X(x: Int) extends Base 
case class Y(y: Int) extends Base 

object Test extends App { 
    val encoded = Encoder[Base].apply(Y(1)) 
    val decoded = Decoder[Base].apply(encoded.hcursor) 
    println(decoded) // Right(Y(1)) 
} 

bir mühürlü özelliği ile sınırlanan olsa bile ben artık yapamam temel sınıf, bir tür üyesi:

import io.circe._ 
import io.circe.generic.auto._ 

sealed trait Inner 
case class I1(i: Int) extends Inner 
case class I2(s: String) extends Inner 

sealed trait Base { type T <: Inner } 
case class X[S <: Inner](x: S) extends Base { final type T = S } 
case class Y[S <: Inner](y: S) extends Base { final type T = S } 

object Test extends App { 
    val encodedInner = Encoder[Inner].apply(I1(1)) 
    val decodedInner = Decoder[Inner].apply(encodedInner.hcursor) // Ok 
    println(decodedInner) // Right(I1(1)) 

    // Doesn't work: could not find implicit for Encoder[Base] etc 
    // val encoded = Encoder[Base].apply(Y(I1(1))) 
    // val decoded = Decoder[Base].apply(encoded.hcursor) 
    // println(decoded) 
} 

istediğimi elde edebilirsiniz bir yolu var mı? Değilse, benzer bir şey elde etmek için ne değiştirebilirim?

+0

Yardımcı model ile denediyseniz ne olur? Örneğin. 'type Aux [A <: Input] = Base {type T = A}' o zaman 'Aux'den uzar? Ayrıca, bir tür üye olmak için gerçekten ihtiyacınız var mı? – pyrospade

+0

Aslında, vaka sınıflarınız bir 'Inner' yerine bir 'S <: Inner' yerine argüman olarak alınabilir. – pyrospade

+0

Bir yanıt ekledim, ancak daha fazla ayrıntı ve açıklama yanı sıra daha iyi bir uygulama eklemek için gözden geçirdik. – pyrospade

cevap

1

Bu işe yaramazsa temel nedeni çalıştığınız çünkü esasen T ne tür demeden

Encoder[Base { type T }] 

yapmaktır. Bu, bu işlevi derlenmesini beklemekle benzerdir - türünüzü açıkça düzeltmeniz gerekir.

Buna yaklaşmanın bir yolu, Aux modelidir. Normal türetimi kullanamazsınız, çünkü bu örneği türetmeye çalışırken coproduct vermeyecektir (X ve Y sınıfları bir tür diğer adından genişletilemez). Bunun yerine, Aux olarak başka bir mühürlü özellik oluşturarak etrafımızdan kırılabilir ve vaka sınıflarımız bundan yararlanabilir.

Elveda davanız sınıflarının tüm Base itibaren Base.Aux yerine doğrudan uzanmaktadır gibi, sen suistimaller .asInstanceOf tip sistemini yatıştırmak için aşağıdakileri kullanabilirsiniz.

sealed trait Inner 
case class I1(i: Int) extends Inner 
case class I2(s: String) extends Inner 

sealed trait Base { type T <: Inner } 
object Base { 
    sealed trait Aux[S <: Inner] extends Base { type T = S } 
    implicit val encoder: Encoder[Base] = { 
    semiauto.deriveEncoder[Base.Aux[Inner]].asInstanceOf[Encoder[Base]] 
    } 
    implicit val decoder: Decoder[Base] = { 
    semiauto.deriveDecoder[Base.Aux[Inner]].asInstanceOf[Decoder[Base]] 
    } 
} 

val encoded = Encoder[Base].apply(Y(I1(1))) 
val decoded = Decoder[Base].apply(encoded.hcursor) 

Bunların çoğunun, aslında türlerinizi nasıl kullandığınıza bağlı olduğuna dikkat edin. Doğrudan Encoder[Base]'u aramaya güvenmeyeceğinizi ve bunun yerine import io.circe.syntax._'u kullanacağınızı ve .asJson uzantı yöntemini çağırdığınızı düşünürdüm. Bu durumda, kodlanan/kodu çözülen değere bağlı olarak çıkacak olan Encoder[Base.Aux[S]] örneğine güvenebilirsiniz. Aşağıdaki gibi bir şey kullanım durumunuz için .asInstanceOf hack başvurmadan yeterli olabilir.

implicit def encoder[S <: Inner : Encoder]: Encoder[Base.Aux[S]] = { 
    semiauto.deriveEncoder 
} 

Yine de, örneklerin nasıl kullanıldığına bağlı olarak değişir. Ben aslında Base içinde bir tür üyeye ihtiyacınız olduğuna şüpheliyim, bir genel param üzerine taşıdığınızda işler daha basit olurdu, böylece türet sizin için yan ürünü bulabilirdi.

+0

Tür üye kapsam dışı olan bir nedenle var, kesinlikle kaldırılabilir ancak her yerde çok sayıda basamaklı tip parametresiyle sözdizimini aşırı yükler. Kesinlikle farklı bir şekilde yapılabilir, ama tahmin ettiğim nokta bu değil. –

+0

Ayrıca, Aux'in yoldaşın bir üyesi olduğu Base.Aux'dan nasıl uzanırsınız? Aux sadece bir tür takma addır, bunu afaik uzatamazsınız, ben zaten fark görmüyorum –

+0

Ama yukarıdaki kodda, Base.Aux bir özellik değil, bir tür takma addır ... – Blaisorblade