Scala - kratší implicitní konverze

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 }
Když jsem tento 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 !
rekurzivně počítající 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 }
Od verze Scaly 2.11 tuto roli lépe zastane implicitní třída a nehrozí, že by se strukturálně deklarované metody volaly prostřednictvím reflexe.
implicit class Int2fact(n: Int) { def ! : Int = if (n == 0) 1 else ((n-1)!) * n }
Krása, co?