onsdag 3. november 2010 Clojure
Nå har jeg endelig fått testet ut ClojureCLR, dvs. Clojure kjørende på .Net-rammeverket. Det er kjempeenkelt å komme igang – du bare laster ned en zip med bits herfra (anbefaler clojure-clr 1.2.0), pakker det ut et sted, starter et command-vindu og eksekverer Clojure.Main.exe. Hvis du ikke sender med en clojurekildefil som argument startes den interaktive REPL'en (Read Eval Print Loop) hvor du kan leke deg med språket.
Jeg jobber mye med MSMQ, og da jeg skulle teste ut Clojure i .NET føltes det derfor naturlig å starte med det. Denne blogposten består av noen eksempler på hvordan man kan bruke System.Messaging og meldingskøer fra ClojureCLR – og illustrerer dermed .NET-interopen, og hvordan den eventuelt skiller seg fra Java-interopen i "vanlig" Clojure.
Jeg begynner med en tom fil jeg kaller testqueue1.clj. Det første jeg må gjøre er å laste System.Messaging dll'en, og gjøre klassene jeg har tenkt å bruke tilgjengelige. Det gjør jeg på denne måten:
Jeg har allerede en lokal, transaksjonell kø som heter testqueue, og jeg vil bruke denne fra koden min. Jeg definerer symbolet *queue* til å være en instans av MessageQueue, og setter også Formatter-propertien til en XmlMessageFormatter som håndterer strenger:
Opprettelse av objekter og kall av metoder er akkurat som i Clojure for JVM. Der er ingen spesiell syntaks for properties, i stedet bruker man funksjonene som ligger bak propertiene (get_PropertyName og set_PropertyName). I linje 9 ser du også hvordan jeg konverterer en PersistanceVector om til et array av typen System.Type, som er det konstruktøren til XmlMessageFormatter trenger.
Og nå kan jeg opprette min første funksjon. Denne er en ganske enkel en som returnerer antall meldinger i køen:
Deretter vil jeg lage én metode for å sende en melding og én metode for å hente ut en melding. Felles for disse to er at jeg trenger å opprette og bruke en transaksjon. For å unngå kodeduplisering lager jeg en "høyereordens funksjon" jeg kaller with-transaction. Den tar som innput en lambda som definerer hva som skjer i transaksjonen.
Når jeg utvikler i Clojure bruker jeg den interaktive REPL'en ganske flittig. Typisk jobber jeg i en eller flere tekstfiler samtidig som jeg har en REPL kjørende. I den kan jeg laste filene, og reloade dem når jeg har gjort endringer. REPL'en lar meg test/kjøre funkjonene jeg definerer med ulike innput, eller teste ut kode før jeg skriver den i kildekodefilen. Jeg kan til og med re-definere enkeltfunksjoner underveis om jeg ønsker det.
Nedenfor er et lite eksempel på hvordan jeg kan bruke REPL'en med testqueue1.clj som jeg nettopp har laget. Først laster jeg skriptet. Deretter kaller jeg funksjonene for å telle meldinger, sende melding, og hente ut melding. Legg merke til at jeg velger å lese ut Body-propertien på meldingen jeg leser, selv om receive-one returnerer et Message-objekt.
Det som skjedde i virkeligheten var at jeg gjorde disse tingene mens jeg definerte funksjonene. Og mellom hver gang jeg gjorde en endring kjørte jeg (require 'testqueue1 :reload) slik at REPL'en ble oppdatert med mine endringer. For et bra innblikk i hvordan det er å jobbe på denne måten anbefaler jeg blogposten Interaktiv programmering: utforsking, læring og produktivitet – skrevet av Thomas Kjeldal Nilsson.
En annen ting jeg føler jeg bør vise er hvordan man bruker eventer i ClojureCLR. Når man skal bruke meldingskøenes asynkrone funskjonalitet trenger man å sette opp eventhandlere for å motta meldinger. ClojureCLR har en macro som heter gen-delegate for å opprette delegater. I linje 12 nedenfor bruker jeg den til å opprette on-receive-completed. Og i linje 26 ser du hvordan jeg kobler delegatet på eventet. Følgende er plassert i en fil jeg kalte testqueue2.clj:
Nedenfor ser du hvordan jeg bruker to REPL'er til å teste ut testqueue2. Det øverste vinduet bruker jeg til å sende endel meldinger. I det nederste vinduet setter jeg opp async-receive til å printe ut alle meldinger som kommer inn. Funksjonskallet avslutter øyeblikkelig. Deretter kommer meldingene fortløpende etterhvert som jeg sender dem.
For flere detaljer, se CLR-Interop wikisiden på GitHub. Clojure er suverent, og etter å ha kjørt det på Java-plattformen i noen måneder føles det nå som å komme hjem når jeg endelig får benytte .NET-rammeverket. :)