k47.cz  — každý den dokud se vám to nezačne líbit
foto Praha výběr povídky kultura
twitter FB RSS

Scala - kratší implicitní konverze

25. 1. 2011 — k47 (CC by-sa) (♪)

Když ve Scale im­pli­cit­ními kon­ver­zemi při­dá­váme nové metody k již exis­tu­jí­cím typům (stej­nou věc jako ex­tension me­thods v C#), ty­picky de­fi­nu­jeme wrap­per W, třídu jejíž kon­struk­tor při­jímá jeden pa­ra­metr typu T, a pak im­pli­citní funkci, která se po­stará o kon­verzi z T na W.


Na­pří­klad:

class StringWrapper(s: String) {
  def uc = s.toUpperCase
  def lc = s.toLowerCase
  def ucFirst = if (s.isEmpty) s else s.head.toUpper + s.tail
  def lcFirst = if (s.isEmpty) s else s.head.toLower + s.tail
}

implicit def wrapString(s: String) = new StringWrapper(s)

De­fi­no­vat wrap­per není ne­zbytně nutné, exis­tuje o něco málo kratší zápis. Im­pli­citní funkce může jed­no­duše vrátit ano­nymní objekt, který má všechny po­ža­do­vané metody:

implicit def wrapString(s: String) = new {
  def uc = s.toUpperCase
  def lc = s.toLowerCase
  def ucFirst = if (s.isEmpty) s else s.head.toUpper + s.tail
  def lcFirst = if (s.isEmpty) s else s.head.toLower + s.tail
}

Jak jsem tenhle kon­cept pro­zkou­má­val dál, na strán­kách Scaly jsem na­ra­zil na ma­lič­kou ukázku roz­ši­řo­vání za­bu­do­va­ných typů im­pli­citní kon­verzí. In­te­geru se při­dala metoda !, která re­kur­zivně po­čí­tala fak­to­riál. Ty­pická im­ple­men­tace: re­kur­zivní funkce, wrap­per a im­pli­citní kon­verze.

def fact(n: Int): Int = if (n == 0) 1 else fact(n-1) * n
class Factorizer(n: Int) {
  def ! = fact(n)
}
implicit def int2fact(n: Int) = new Factorizer(n)

Na­padlo mě, že by se celý tenhle kód dal zre­du­ko­vat do jediné metody:

implicit def int2fact(n: Int) = new {
  def ! : Int = if (n == 0) 1 else ((n-1)!) * n
}

Bo­hu­žel, takhle jed­no­duše to ne­fun­go­valo.

error: value ! is not a member of Int

Note: implicit method int2fact is not applicable here because it comes after the application point and it lacks an explicit result type

Kom­pi­lá­tor si stě­žo­val, že Int nemá metodu ! s po­znám­kou, že nemůže použít im­pli­citní kon­verzi int2fact a vy­ža­duje ná­vra­tový typ re­kur­zivní funkce int2fact. Ta je re­kur­zivní zvlášt­ním způ­so­bem, ne kla­sicky, že volá sama sebe, ale ne­přímo: vy­tváří objekt v němž se sama volá.

Ex­pli­citní za­vo­lání kon­verze vyřeší jenom po­lo­vinu pro­blému. Pořád je tu ne­známý ná­vra­tový typ. Ale jaký je typ ano­nymní třídy? V Javě nebo C# ne­známý. Ve Scale je to struk­tu­rální typ. V tomhle pří­padě { def ! : Int}, tedy li­bo­volný objekt, který má metodu !.

Fi­nální podoba kódu se může zdát lekce kryp­tická, ale fun­guje.

implicit def int2fact(n: Int): { def ! : Int} = new {
  def ! : Int = if (n == 0) 1 else ((n-1)!) * n
}

Krása, co?


A na závěr jedna há­danka. Co dělá ná­sle­du­jící kód? Kdo uhodne, do­stane blu­diš­ťáka.

implicit def wi(i: Int) = (_: Int) + i
píše k47 & hosté, ascii@k47.cz