LINQ-macro i Clojure
- Monday, August 23rd, 2010
- Skriv en kommentar
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:
“Frank” “Everett”
“Albert” “George”
“Harris” “David”))
(def query
(from n in names
where (= (. n length) 5)
orderby n
select (. n toUpperCase)))
(doseq [n query]
(println n))
BURKE
DAVID
FRANK
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.
[var _ coll _ condition _ ordering _ desired_map]
`(map (fn [~var] ~desired_map)
(sort-by (fn [~var] ~ordering)
(filter (fn [~var] ~condition)
~coll))))
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.
Flere eksempler
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):
{:name “Guangzhou” :country “China” :population 14000000}
{:name “Shanghai” :country “China” :population 10000000}
{:name “Bogota” :country “Colombia” :population 32000000}
{:name “Delhi” :country “India” :population 4000000}
{:name “Los Angeles” :country “USA” :population 9000000}
{:name “London” :country “United Kingdom” :population 24000000}
{:name “Tianjin” :country “China” :population 37000000}
{:name “Wuhan” :country “China” :population 29000000}
{:name “Lagos” :country “Nigeria” :population 25000000}))
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.
(from c in cities
where (and (= (:country c) “China”)
(>= (:population c) 15000000))
orderby (* (:population c) -1)
select [(:name c) (:population c)]))
(doseq [c chinese-cities-more-than-15-million]
(println c))
[Tianjin 37000000]
[Wuhan 29000000]
[Beijing 19000000]
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.
(distinct (from c in cities
where (>= (:population c) 25000000)
orderby (:country c)
select (:country c))))
(doseq [c countries-with-extreme-cities]
(println c))
China
Colombia
Nigeria
Kategorier: LISP/Clojure.
RSS feed for kommentarene.
Tilbaketråkk.



August 23rd, 2010 at 3:12 pm
“lisp marko demostrasjon”. Stavekontroll? :)
August 23rd, 2010 at 8:03 pm
Takk. Hvis det var den eneste feilen så er jeg ganske fornøyd. Har alltid crowdsourcet stavekontrollen til mine lesere – det blir flere kommentarer på den måten ;)
August 23rd, 2010 at 8:42 pm
Bruker dere Clojure i noen av deres produksjonssystemer i dag? Hvis ikke, hvilke bruksområder ser du for deg i fremtiden?
August 23rd, 2010 at 8:59 pm
Nei, vi bruker ikke Clojure, og inntil videre lærer jeg meg Clojure utelukkende for min egen del. Det er langt fra sikkert jeg vil få bruk for språket direkte i jobbsammenheng heller – til det er det for fjernt fra det utviklere flest kjenner. Skulle det være aktuelt ville det kanskje være for å utvikle en eller annen form for DSL. Og det vil kanskje bli mer sannsynlig om/når Clojure blir klart på .net-plattformen.