mandag 23. august 2010 Clojure Macro
En av de tingene som gjør Clojure og andre Lisper unike i forhold til andre språk er makroer. Det finnes andre språk som har makro-features, men ikke så geniale som de i Clojure/Lisp. Makroer gjør det blant annet ganske enkelt å lage interne DSL'er. Her er et eksempel hentet fra en introduksjon til lisp-makroer på slideshare, hvor man legger tilrette for Linq-lignende spørringer med bare noen få linjer kode.
La oss først se på hvordan det tar seg ut i bruk:
Bortsett fra Clojure-koden for å spesifisere lengde på 5, og å gjøre navnene uppercase, er denne spørringen svært så leselig, og ikke ulik det man får i .net med Linq.
Og her følger alt vi trengte å definere for å kunne skrive spørringen. From-makroen tar imot argumentene og gjør det om til list-prosesserende kall som filter, sort-by og map. Nøkkelordene in, where, orderby og select ser den faktisk bare bort fra – de er der kun for lesbarhet i selve spørringen. Når jeg kjører programmet vil spørringen min bli byttet ut med den templaten som makroen definerer.
Med makroer kan vi altså sømløst utvide språket med ting som i mange andre språk ville krevd en endring av selve kompilatoren. For en grei innføring i makroer kan du ta en titt her.
Jeg eksperimenterte litt videre med makroen fra demonstrasjonen, og lagde noen spørringer på en liste med storbyer for å illustrere kraften i DSL'en bedre. Her er først byene, opprettet som en liste av hashes (disctionaries):
Så kan jeg for eksempel lage en spørring som henter ut alle byene i Kina med 15 millioner innbyggere eller mer. Jeg spesifiserer også synkende sortering på innbyggertall ved å gange det med –1. Til slutt selekterer jeg kun ut navnet på byen og innbyggertallet.
I neste eksempel er jeg interessert i alle land som har byer med mer enn 25 millioner innbyggere. Jeg sorterer på land, og selekterer kun landet. I tillegg bruker jeg distinct, som er "innebygd" i clojure, for å unngå at Kina kommer med to ganger.