2015-06-24 62 views
5

Hata türü toplama için shapeless 'Coproduct kullanmaya çalışıyorum. Kendimi tekrarlamak zorunda kalmadanTip bildirimlerini genişletmenin bir yolu var mı?

import shapeless._ 

case object F1 
case object F2 
type F12 = F1.type :+: F2.type :+: CNil 

case object F3 
case object F4 
type F34 = F3.type :+: F4.type :+: CNil 

type F1234 = F1.type :+: F2.type :+: F3.type :+: F4.type :+: CNil 

def custom(f: Either[F12, F34]): F1234 = // how can I declare the resulting type? 
    f.fold(_.extendRightBy[F34], _.extendLeftBy[F12]) 

object F1234Handler extends Poly1 { 
    implicit def caseF1 = at[F1.type](_ => "got F1") 
    implicit def caseF2 = at[F2.type](_ => "got F2") 
    implicit def caseF3 = at[F3.type](_ => "got F3") 
    implicit def caseF4 = at[F4.type](_ => "got F4") 
} 

custom(Left(Coproduct[F12](F2))).fold(F1234Handler) // got F2 

Nasıl özel kat sonuç tanımlayabilir: Aşağıdaki Şu anda var sorunu yalıtmak için bir girişimdir? İdeal olarak, F1234'ü benim yaptığım şekilde beyan etmek istemiyorum, sadece mevcut iki tip bildirge olan F12 ve F34'ün bir birleşimini yaparak beyan etmek istiyorum. Bu şekilde, bu bildirimlerin herhangi birine başka bir hata türü eklediğimde F1234 bildirimini güncellemem gerekmez. F1234 = F1.type: +: F2.type: +: F34 türünü bildirebilirim ancak F1234 tipi F1234 tipi F1234 = F12: F34 türünde, extendBy işlemleri tarafından bırakılan CNil kuyruğu nedeniyle bildirimde bulunamıyorum.

cevap

5

Durum, lmm'nin yanıtının önerdiği kadar kötü değil çünkü kısmen Shapeless, ExtendLeftBy ve ExtendRightBy'u paketleyen bir ExtendBy tür sınıfı sağlıyor. Eğer gerçekten kendinizi hesaplamak ve elle dışarı yazmadım custom için bir dönüş türü istiyorsa Yani, ExtendBy kullanabilirsiniz:

import shapeless._, ops.coproduct.ExtendBy 

case object F1 
case object F2 
type F12 = F1.type :+: F2.type :+: CNil 

case object F3 
case object F4 
type F34 = F3.type :+: F4.type :+: CNil 

def custom(f: Either[F12, F34])(implicit ext: ExtendBy[F12, F34]): ext.Out = 
    f.fold(ext.right(_), ext.left(_)) 

, sen ikna doğrudan ExtendLeftBy ve ExtendRightBy kullanmaya gerek yoktu bile derleyici, aynı çıkış türüne sahip olduklarından Aux türleri ve tek bir paylaşılan tip parametresi ile biraz daha temizdirler. Bunun yerine bu (LMM kodunun tam çalışma versiyonu):

import ops.coproduct.{ ExtendLeftBy, ExtendRightBy } 

def custom[ERO, ELO](f: Either[F12, F34])(implicit 
    el: ExtendRightBy[F12, F34] { type Out = ELO }, 
    er: ExtendLeftBy[F12, F34] { type Out = ERO }, 
    w: ELO =:= ERO 
): ERO = f.fold(l => w(el(l)), er(_)) 

Sadece yazarsınız: Çoğu durumda

def custom[Out <: Coproduct](f: Either[F12, F34])(implicit 
    extL: ExtendRightBy.Aux[F12, F34, Out], 
    extR: ExtendLeftBy.Aux[F12, F34, Out] 
): Out = f.fold(extL(_), extR(_)) 

Eğer statik olarak girdi türlerini biliyorsanız, yine de, şimdi etsen sadece iade türünü kendiniz yazınız ve örtük parametre işini tamamen atlayınız. Bu herhangi iki birlikte-ürünleri, sadece F12 ve F34 için çalışır

def custom[A <: Coproduct, B <: Coproduct](f: Either[A, B])(implicit 
    ext: ExtendBy[A, B] 
): ext.Out = f.fold(ext.right(_), ext.left(_)) 

: böyle, jenerik türleriyle çalışırken örtük kanıt gereklidir.

+0

Merhaba Travis, cevabınız için teşekkürler. Extend'i bulmuştum ama nasıl işe yaradığını anlayamadım. – sparkle

3

Scala'ya bağlı türler her zaman biraz tuhaf ve hantaldır. Tip düzeyinde işlevler, sonuç olarak tür üyeler olarak örtük olarak kodlanır, ancak scala derleyicisi söz konusu olduğunda, yalnızca bir tür üye olur. Yani böyle bir şey yapmak zorunda: derleyici extendLeftBy[F12] çıktısı aynı türü olduğunu söyleyemem çünkü

def custom(f: Either[F12, F34])(implicit er: ExtendRight[F12, F34]): er.Out 
    = f.fold(_.extendRightBy[F34], _.extendLeftBy[F12]) 

Maalesef bu işe yaramaz. İki türün her zaman aynı olacağını biliyoruz, ancak derleyici öyle değil, bu yüzden bir tanığa ihtiyacımız var (aslında her zaman mevcut olacak). gibi bir şey: Bizim türü parametreleri aynı listeden tip parametrelere bağlı izin verilmez çünkü

def custom(f: Either[F12, F34])(implicit er: ExtendRight[F12, F34], 
    el: ExtendLeft[F34, F12])(implicit w: er.Out =:= el.Out): er.Out 
    = f.fold(_.extendRightBy[F34], w(_.extendLeftBy[F12])) 

Ne yazık bile çalışmıyor ve biz sadece bir örtük listesi olabilir. Bu yüzden "asansör" bu tür yerine türü parametreleri olmak zorunda: Ben, hantal Dediğim gibi

def custom[ERO, ELO](f: Either[F12, F34])(
    implicit er: ExtendRight[F12, F34]{type Out = ERO}, 
    el: ExtendLeft[F34, F12]{type Out = ELO}, w: ELO =:= ERO): ELO 
    = f.fold(_.extendRightBy[F34], w(_.extendLeftBy[F12])) 

, ancak çalışması gerekir. (ExtendRight ve ExtendLeft, extendRightBy ve extendLeftBy yönteminde kullanılan türlerdir - benzer şekilde bağımlı bir şekilde yazılan işlev muhtemelen benzer "yardımcı" türlere sahip olacaktır).

+0

Umarız, önerilerim bir yoruma uymayacağından kendi yanıtımı ekledim. –

+0

Hiç de değil - mümkün olan en iyi cevabı almalıyız. Ben de sizin kadar shapeless bilmiyorum ama kesinlikle bir 'Out' tipi parametre kullanabileceğimizi ve' =: = ''ye ihtiyaç duyduğumuzdan emin olmalıydım. ('Aux' sadece bir IIRC takma adıdır). – lmm

+0

Merhaba Imm, cevabınız için teşekkürler! Umarım Travis'in cevabını seçtiğimi umursamazsın, siz ikiniz de sorunu çok iyi açıkladınız. – sparkle

İlgili konular