Jeg leker litt med Clojure i .NET
- November 3rd, 2010
- Permalink
- Ingen kommentarer
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:
2 (import ‘(System.Messaging MessageQueue
3 MessageQueueTransaction
4 XmlMessageFormatter))
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:
7 (doto (MessageQueue. “.\\private$\\testqueue”)
8 (.set_Formatter (XmlMessageFormatter.
9 (into-array Type [String])))))
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:
12 (-> *queue*
13 (.GetAllMessages)
14 (.Length)))
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.
17 (let [transaction (new MessageQueueTransaction)]
18 (.Begin transaction)
19 (let [result (f transaction)]
20 (.Commit transaction)
21 result)))
22
23 (defn send-to-queue [message]
24 (with-transaction
25 #(.Send *queue* message %)))
26
27 (defn receive-one []
28 (with-transaction
29 #(.Receive *queue* (TimeSpan. 0 0 1) %)))
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.
.NET events i ClojureCLR
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:
2 (import ‘(System.Messaging MessageQueue
3 MessageQueueTransaction
4 XmlMessageFormatter
5 ReceiveCompletedEventHandler))
6
7 (declare handler) ; handler will point to a function later..
8
9 ; generate a delegate of type ReceiveCompletedEventHandler
10
11 (def on-receive-completed
12 (gen-delegate ReceiveCompletedEventHandler
13 [source async-result]
14 (let [message (.EndReceive source
15 (.AsyncResult async-result))
16 result (handler message)]
17 (.BeginReceive source)
18 result)))
19
20 ; *queue* will have a ReceiveCompleted handler set..
21
22 (def *queue*
23 (doto (MessageQueue. “.\\private$\\testqueue”)
24 (.set_Formatter (XmlMessageFormatter.
25 (into-array Type [String])))
26 (.add_ReceiveCompleted on-receive-completed)))
27
28 ; function to start receiving, given a specific handler function..
29
30 (defn async-receive [handler-fn]
31 (def handler handler-fn)
32 (.BeginReceive *queue*))
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. :)


Thinking about work 24/7 is also the reason a lot of programmers burn out :-( We are sitting on the buttocks all day, drink lots of coffee, working in the evenings, getting little sleep, little physical activity; a perfect combination for a burnout.
En “profesjonell utvikler”, slik Uncle Bob ville ha sagt det, sørger for å få nok søvn – uten det kan han ikke konsentrere seg godt nok til å skrive god kode. En profesjonell utvikler sørger også for å holde seg i form – om kroppen ikke har det bra vil konsentrasjonen også lide. Og det hjelper lite med søvn og trening om man har et dårlig kosthold. Disse tre punktene er et minstekrav, og kutter du ut ett av dem mister du effekten av de to andre.
Utviklere sitter mye i ro mens de jobber, og det er ikke kroppen laget for. Vi må derfor kompensere med ekstra, fysisk aktivitet. Personlig går jeg en times tur med hunden hver dag, og trener i alle fall én gang i uka på treningssenter. Dessuten er jeg pappa til en smårolling, og det krever jo sitt. Har du en kontorjobb og ikke gjør noe tilsvarende kan du ikke regne med at kroppen holder så veldig lenge. I stedet kan du forvente deg vond rygg og en rekke, andre plager som vil gå ut over både din evne til å yte på jobb og din livskvalitet forøvrig.
Det kan kanskje virke som om jeg får tid til usannsynlig mye. En av hemmelighetene er at man orker mer når man er opplagt – ref søvn, trening og kosthold. Men det er også snakk om enkelte prioriteringer. De fleste har tidshull i livet sitt som suger klokketimer uten at man får særlig mye igjen.
“IMO Burnout happens when you think your work is more important than it really is…”
run-program illustrerer hvordan tail-recursion fungerer i Clojure. I kallet til loop (linje 93) defineres det en verdi (variabel) kalt state, som i utgangspunktet er et tomt array – dette er handlevognen. Det gjøres så et kall til get-and-run-command, hvor vi sender inn state. Tilbake får vi returnert verdien vi kaller new-state, den oppdaterte handlevognen.![CropperCapture[63] CropperCapture[63]](http://blog.kjempekjekt.com/wp-content/uploads/2010/04/CropperCapture63.jpg)
Programmeringsspråk utvikler seg hele tiden. De som ikke gjør det er i praksis døde. Og samtidig som dynamiske språk som Perl, Python og Ruby har blitt mer populære, har andre språk beveget seg i en mer dynamisk reting.


