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


Scala - kratší implicitní konverze 📷

publikováno 25. 1. 2011 od k47

Když ve Scale implicitními konverzemi přidáváme nové metody k již existujícím typům (stejnou věc jako extension methods v C#), typicky definujeme wrapper W, třídu jejíž konstruktor přijímá jeden parametr typu T, a pak implicitní funkci, která se postará o konverzi z T na W.


Napří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)

Definovat wrapper není nezbytně nutné, existuje o něco málo kratší zápis. Implicitní funkce může jednoduše vrátit anonymní objekt, který má všechny požadované 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 koncept prozkoumával dál, na stránkách Scaly jsem narazil na maličkou ukázku rozšiřování zabudovaných typů implicitní konverzí. Integeru se přidala metoda !, která rekurzivně počítala faktoriál. Typická implementace: rekurzivní funkce, wrapper a implicitní konverze.

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)

Napadlo mě, že by se celý tenhle kód dal zredukovat do jediné metody:

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

Bohužel, takhle jednoduše to nefungovalo.

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

Kompilátor si stěžoval, že Int nemá metodu ! s poznámkou, že nemůže použít implicitní konverzi int2fact a vyžaduje návratový typ rekurzivní funkce int2fact. Ta je rekurzivní zvláštním způsobem, ne klasicky, že volá sama sebe, ale nepřímo: vytváří objekt v němž se sama volá.

Explicitní zavolání konverze vyřeší jenom polovinu problému. Pořád je tu neznámý návratový typ. Ale jaký je typ anonymní třídy? V Javě nebo C# neznámý. Ve Scale je to strukturální typ. V tomhle případě { def ! : Int}, tedy libovolný objekt, který má metodu !.

Finální podoba kódu se může zdát lekce kryptická, ale funguje.

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ásledující kód? Kdo uhodne, dostane bludišťáka.

implicit def wi(i: Int) = (_: Int) + i

vstoupit do diskuze    sdílet na facebooku, twitteru, google+

příbuzné články:
Scala - dynamický jazyk 📷
Scala - operátor mocniny 📷
Scala - Zřetězené porovnávání 📷
Scala - postfixový if 📷
Scala - metody ála Smalltalk 📷
Scala - tranzitivita implicitních konverzí 📷

sem odkazují:
Scala - scalable language

píše k47 & hosté, kontakt: ascii@k47.cz