LINQ-macro i Clojure

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:

(def names (“Burke” “Connor”
             “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))

Output:
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.

(defmacro from
  [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):

(def cities ({:name “Beijing” :country “China” :population 19000000}
              {: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.

(def chinese-cities-more-than-15-million
     (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))

Output:
[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.

(def countries-with-extreme-cities
     (distinct (from c in cities
                     where (>= (:population c) 25000000)
                     orderby (:country c)
                     select (:country c))))

(doseq [c countries-with-extreme-cities]
       (println c))

Output:
China
Colombia
Nigeria

Kategorier: LISP/Clojure.
RSS feed for kommentarene. Tilbaketråkk.

4 kommentarer til “LINQ-macro i Clojure”

  1. bug Says:

    “lisp marko demostrasjon”. Stavekontroll? :)

  2. Torbjørn Says:

    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 ;)

  3. trydis Says:

    Bruker dere Clojure i noen av deres produksjonssystemer i dag? Hvis ikke, hvilke bruksområder ser du for deg i fremtiden?

  4. Torbjørn Says:

    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.

Skriv en kommentar

Tillatte tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Einar W. Høst: Det er jo læringen som gjør det morsomt! Se også http://norvig.com/21-days...

Pagliacci: OBS! tl;wr. Det er vel akuratt det jeg sliter med med min læring innenfor pr...

Torbjørn: La oss anta to ulike definisjoner av Template Method pattern - de to ytterpunkte...

Lars-Petter: Hei igjen. Siden du inviterer til å ta diskusjonen i bloggen, og har tatt deg t...

Torbjørn: Lars-Petter: Det er én måte å se det på. Det er absolutt fortsatt Template M...

Lars-Petter: Hei. Har du ikke i prinsippet her gått over fra Template Method (arv) til Strat...

Christian Abildsø: I alle fall i C#, så føles dette som kode som blir mer fleksibel men vanskelig...

Torbjørn: Hei Henrik, og takk for tilbudet. Ble oppmerksom på Rasberry Pi for under en uk...

Henrik Sandaker Palm: Ang. større hobby prosjekt. Du er som er en slik rakker på programmering har j...

Øivind Nilsen: Slutt å bruke mobilnummeret mitt som eksempel !...

Mulig relaterte linker

 Hold deg oppdatert

Søk i bloggen

Ferske innlegg

  • En historie om programmering
  • Template Method del 4: Multippel arv
  • Template Method Intermesso
  • Template Method del 3: Bare funksjoner
  • Kategorier

  • .net ninja (37)
  • Bøker (17)
  • Diverse prosjekter (35)
  • DSL (10)
  • Erlang (10)
  • F# (5)
  • Hardware (1)
  • Jobb (77)
  • Julekalender (51)
  • kjempekjekt.com (23)
  • LISP/Clojure (33)
  • NNUG / community (60)
  • O/RM & databaser (10)
  • Off topic (116)
  • OO-design/clean code (30)
  • Podcasts (14)
  • Polyglot (77)
  • Ruby (27)
  • Silverlight / RIA (3)
  • Software/verktøy (20)
  • Softwareutvikling (21)
  • Testing / TDD (30)
  • the contiki strip (13)
  • User experience (3)
  • WCF (3)
  • Webutvikling (32)
  • WPF (9)
  • WTF (12)
  • Last ned Wallpaper

    Programmeringsbloggens tøffe skrivebordsbakgrunn med snippets fra ulike språk laster du ned her!

    Abonner via epost

    Om du vil kan du få alle nye blogposter tilsendt til din epost. Abonner nå, det er kjempeenkelt!

    Meta