Jeg rotet meg litt bort i programmeringspråket Ruby for noen år siden, og har lovet meg selv å komme tilbake til det nå når IronRuby blir modent på .NET plattformen. Her kan du lese det jeg har skevet om dette..

En universell server (video)

Saturday, April 2nd, 2011
5 kommentarer

Har du lyst til å se meg kode? Jeg har laget en code cast video jeg håper du vil like, men først litt bakgrunn…

Joe Armstrongs foredrag om Message Passing Concurrency in Erlang på QCon London i fjor forandret meg! Han viste meg en måte å trylle med kode på som inntil da hadde vært helt fremmed for meg. Etter det har Erlang, og deretter Clojure, formet nye mønstre i hjernen min, om hvordan det er mulig å skape fantastiske ting med dynamisk kode.

I presentasjonen lagde Armstrong noe han kalte for en universell server – en server som kunne bli hva som helst, bare ved at man sendte den ulike kommandoer. I min video lager jeg en lignende server i Clojure. Jeg forventer ikke at min video skal gi noen den samme kvasireligiøse a-ha-opplevelsen som Joe gav meg, men kanskje den kan gi deg noen nye ideer…

Jeg hadde veldig lyst til å bruke Daft Punk’s TRON Legacy soundtrack som bakgrunnsmusikk, men jeg turde til slutt ikke å bruke musikk jeg ikke har rettighetene til. Musikken er derfor hentet fra Mevio’s Music Alley. Skru på høytalerne!

The Universal Server from Torbjørn Marø on Vimeo.

Vil gjerne høre hva du synes!

Script IIS Manager med IronRuby

Wednesday, March 23rd, 2011
3 kommentarer

Det er ikke ofte det går en hel måned mellom hver gang jeg blogger. Men den siste tiden har jeg slitt endel med nakkesmerter, så noe har desverre måttet vike. Energien er på vei tilbake, og jeg satser på å komme igang igjen med å blogge om PingLang og DSLer snart.

Men for å starte litt forsiktig kommer det her en liten blogpost om å automatisere konfigurering av Internet Information Server ved hjelp av IronRuby.

Jeg fikk nemlig nettopp behov for å laget et script som kunne endre den fysiske stien til en website i IIS. Vi har to ulike sites, la oss kalle dem thesite.com og thesite-staging.com. Den første adressen er produksjonsadressen, den andre bruker vi til testing.

På serveren har vi så to ulike, fysiske lokasjoner for disse sitene. Stiene kan for eksempel være c:\sites\thesite-blue\ og c:\sites\thesite-green\. Til enhver tid inneholder én av disse produksjonskoden, og den andre folderen brukes til å teste neste versjon. Når den nye vesjonen er godkjent bytter vi om de fysiske stiene til thesite.com og thesite-staging.com, slik at thesite.com nå inneholder den nye versjonen.

Dette kalles blue-green deployment.

For å kunne programmere slike ting kommer IIS med en assembly som heter Microsoft.Web.Administration – du finner den under katalogen Windows\System32\inetsrv.

The different logical objects available include sites, applications, application pools, application domains, virtual directories, and worker processes. You can use the API to obtain and work with the configuration and state of these objects and to perform such actions as creating a site, starting or stopping a site, deleting an application pool, recycling an application pool, and even unloading application domains.

Du kan bruke den i C#, fra PowerShell, eller hvilket som helst språk som kan kjøre på .net-plattformen. Jeg foretrekker IronRuby til denne typen oppgaver. Her er et script (et noe enklere script enn det jeg faktisk bruker) som henter ut den virtuelle katalogen til to IIS-sites og bytter fysisk sti mellom dem:

10 require 'c:\\Windows\\System32\\inetsrv\\Microsoft.Web.Administration.dll'
11 include Microsoft::Web::Administration
12 
13 # Monkey patch ServerManager: 
14 #   add get_vdir method to get the main virtual dir
15 class ServerManager
16   def get_vdir site
17     self.
18       sites[site].
19       applications['/'].
20       virtual_directories.
21       find {|vdir| vdir.path == '/'}
22   end
23 end
24 
25 server = ServerManager.new
26 
27 a = server.get_vdir 'thesite.com'
28 b = server.get_vdir 'thesite-staging.com'
29 
30 # Swapping to variables in Ruby is easy..
31 a.physical_path, b.physical_path = b.physical_path, a.physical_path
32 
33 server.commit_changes
34 server.dispose

Vanskeligere er det ikke!

PSWinCom Ruby Gem

Tuesday, February 8th, 2011
1 kommentar

pswincomgemI går slapp jeg en aldri så liten før-valentinegave til alle Ruby-utviklere. Med pswincom gem’en kan man på få sekunder implementere enkel sending av SMS i et hvilket som helst Ruby-prosjekt.

Versjon 0.1.1 inneholder kun én metode, og lar deg sende en SMS med PSWinCom’s SMS Gateway – gitt at du har en konto. Den vil bli utvidet med mer funksjonalitet om det viser seg at det er interesse for det i utviklermiljøet.

Her er en kjapp demo akkompagnert av litt raggae:

Dette er den første gem’en jeg har laget og publisert. Det viste seg å være en ganske enkel og smertefri prosess, og frister til gjentagelse. Nå håper jeg bare det er noen Ruby-utviklere som har behov for å sende SMS og som kan gi meg litt tilbakemelding.

For ordens skyld, gem’en er kraftig inspirert av Clickatell Ruby API, utviklet av Luke Redpath.

For mer info om API’ene våre kan du ta en titt på wiki.pswin.com.

Ping Ring del 3: Ruby

Tuesday, September 7th, 2010
1 kommentar

Dette er del 2 i artikkelserien Ping Ring hvor jeg implementerer et og samme program i et utall ulike programmeringsspråk – for å se om det er noe å lære gjennom å gjøre det. Introduksjonen kan du lese her.

Neste språk ut er Ruby. Denne implementasjonen er logisk sett identisk med C#-varianten fra del 2. TCP-logikken er ørlite enklere (jeg trenger ingen StreamReader/Writer), og Ruby’s kodeblokker gjør tråd-logikken mere elegant å sette opp enn med C#’s lambda-uttrykk pluss et kall til start(). Men i bunn og grunn er det liten forskjell i hvor enkelt det er å løse oppgaven.

Hovedforskjellen er syntaksen. Ruby’s dynamiske typing gjør at vi unngår endel av hva jeg vil kalle støy. Syntaksen føles mere “komprimert” i Ruby, og det ser man igjen i bl.a. færre linjer kode. Min Ruby-variant “klokker seg inn” på 59 linjer – mot C# sine 90.

1 require socket
2
3 class RingServer
4   def initialize
5     @self_port = ARGV.shift
6     @other_port = ARGV.shift
7     @max_delay = ARGV.shift.to_i
8     @should_send_startup_ping = ARGV.shift == true
9     @last_ping_time = Time.now
10     puts ** Ruby Ring Server (#{@self_port})
11   end
12   def start
13     send_delayed_ping if @should_send_startup_ping
14     start_missing_ping_alert_thread
15     start_ping_listener_thread
16     Thread.list.each { |t| t.join unless t == Thread.main }
17   end
18   def send_delayed_ping
19     Thread.new do
20       sleep 1
21       begin
22         client = TCPSocket.new(127.0.0.1, @other_port)
23         client.print Ping from #{@self_port}
24       rescue
25         puts *** Failed sending ping: #{$!}
26       else
27         client.close
28       end     
29     end
30   end
31   def start_missing_ping_alert_thread
32     Thread.new do
33       while true
34         sleep 5
35         ping_delay = Time.now – @last_ping_time
36         if ping_delay > @max_delay
37           puts *** ALERT, RING BROKEN! No ping in #{ping_delay} seconds.
38           send_delayed_ping # try to wake up ring
39         end
40       end 
41     end
42   end
43   def start_ping_listener_thread
44     Thread.new do
45       listener = TCPServer.new(127.0.0.1, @self_port)
46       while session = listener.accept
47         process_incoming_ping session.gets
48         session.close
49       end
50     end
51   end
52   def process_incoming_ping message
53     @last_ping_time = Time.now
54     puts Received #{message}
55     send_delayed_ping
56   end
57 end
58
59 RingServer.new.start

Det vil være riktig av deg å påpeke at jeg ikke bruker blanke linjer på samme måte i denne koden som jeg gjorde i C#, og at det derfor er naturlig at det blir færre linjer. Men ta en titt nedenfor, hvor jeg har satt opp de to implementasjonene side om side. Her har Ruby-varianten fått seg endel linjeskift, slik at metodene som gjør det samme starter på samme linjenummer.

pingring_csharp_vs_ruby

Det er ikke tvil om hvilken versjon jeg synes er mest elegant. Men elegansen ligger altså i at C#-syntaksen har for mye støy og staffasje, ikke at det var spesielt mye enklere å implemetere i Ruby.

Jeg må derimot få påpeke at Ruby gir meg større fleksibilitet i hvordan jeg ønsker å organisere koden. Jeg valgte å bruke den samme strukturen som C#-varianten denne gangen; én klasse med seks metoder som løste helt konkrete behov. Jeg kunne derimot for eksempel ha valgt en klasse-løs implementasjon – uten å miste noe lesbarhet av betydning. Ruby egner seg etter mening veldig godt til “små skripts” som dette, hvor kravet til “innpakning” (encapsulation) ikke er så stort.

Alt i alt: Ikke en veldig stor forskjell, men Ruby trekker det lengste strådet! I neste del vil du få se programmet i et språk som har enda finere og mere kompakt syntaks, så følg med..

Kildekoden fra denne blogposten er tilgjengelig på Github. Der står du fritt til å forgrene løsningen og gjøre egne modifikasjoner om du ønsker det (for å illustrere et poeng eller lignende). Som alt annet på bloggen er koden lisensiert under Creative Commons.

Hva er en Monad?

Friday, August 13th, 2010
8 kommentarer

yin_yang

Monad er et ord med mange betydninger; det brukes blant annet innenfor ulike filosofiske grener for å beskrive “universets essens”, eller som et slags navn på Gud. Det kjente tegnet fra kinesisk filosofi og kosmologi, som kineserne kaller T’ai-Chi, kalles også the Great Monad i Vesten, og representerer den underliggende harmonien mellom motstridende krefter i universet (mann/kvinne, yin/yang, hard/myk, sol/måne). Men ordet har også en betydning i matematikken, og den er overført til informatikk, hvor det er mest kjent som en feature i programmeringsspråket Haskell.

Windows PowerShell er også kjent under kodenavnet Monad, men meg bekjent er dette ikke relatert til betydningen i de funksjonelle språkene.

Mange betrakter monads som det mest skumle og uforståelige med funksjonelle språk, og det tar tid å skjønne hva det er for noe. Jeg har akkurat begynt å forstå det selv, og dette er forklaringen jeg skulle ønske jeg hadde sett før jeg begynte å lese de mer teoretiske beskrivelsene. Forhåpentligvis kan dette være til hjelp for andre som sliter med det samme, men jeg gir ingen garantier.

Du skal ikke se bort fra at du har brukt monader allerede

Da jeg begynte å lese meg opp på F# lærte jeg at man har monads der også, men at man kaller dem computation expressions. Og så sa læreboken at LINQ i .net også er computation expressions. Tidligere har jeg også hørt at LINQ er en form for list comprehension, som jeg lærte meg da jeg begynte å bruke Erlang, og som jeg senere har funnet igjen i flere andre språk. (List comprehension er en slags spesialsyntaks for å generere lister.) Det viser seg at list comprehensions kan kalles bruk av en list monad, og monad comprehension er en mer generell variant av list comprehensions.

Ok, du ble kanskje ikke noe klokere av dette, men saken er at alle disse tingene henger sammen på et vis. Du vil forhåpentlig vis se lyset mot slutten av blogposten!

For å skape noen nye hjernekoblinger kan vi først ta tak i litt Ruby-kode. Følgende er en banal, liten kodesnutt som deklarerer et par variabler som brukes til å beregne og skrive ut en verdi:

n = 1337
m = n * 3.1415
puts m # Skriver ut: 4200.1855

Dette er imperativ kode, slik man for eksempel finner i C og alle etterfølgerne av det språket. I Ruby har vi derimot muligheten til å gjøre koden mer funksjonell ved å gjøre følgende:

def times_pi x
  yield x * 3.1415
end

times_pi(1337) {|m| puts m} # Skriver ut: 4200.1855

Legg merke til at det ikke er noen likhetstegn i denne koden – det deklareres ingen variabler. I stedet kaller man en metode (times_pi) hvor man sender med en anonym funksjon som et av argumentene (en kodeblokk). Denne kodeblokken kalles så i funksjonen ved å bruke nøkkelordet yield. Når metodekallet er ferdig finnes det ingen variabler – ingen state!

For å bli en dyktig Ruby-utvikler må man endre tankemønsteret man bruker fra kode som i det første, imperative eksempelet til kode som det man finner i det andre eksempelet hvor man bruker kodeblokker. Men dette er også et stort steg på veien mot å lære seg funskjonell programmering. Times_pi er nemlig en høyere-ordens funksjon (den tar en funksjon som et parameter), og koden har ingen variabler / state. Vi har snudd måten vi tenker på (en slags Inversion of Control).

Hva har dette med monad å gjøre?

Jeg skal snart vise deg en monad, men ta først en titt på følgende, skrekkelige eksempel på Clojure-kode:

; BAD BAD BAD – DON’T DO THIS EVER!!!
(def pi 3.1415)  ; pi = 3.1415
(def n 1337)     ; n = 1337
(def m (* n pi)) ; m = n * pi
(println m) ; Skriver ut: 4200.1855000000005

Her har jeg skrevet imperativ kode i Clojure, noe det ikke akkurat er laget for (men det fungerer). I virkeligheten ville jeg ha gjort det slik:

(let [pi 3.1415
      n  1337
      m  (* n pi)]
  (println m)) ; Skriver ut: 4200.1855000000005

Let lar meg definere et sett med verdier som så er tilgjengelig innenfor let-kallet. Når let-kallet er ferdig opphører verdiene å eksistere. Let er faktisk en monad – i Haskell er den kjent som Identity Monad. Det let gjør er å transformere argumentene sine til en kjede med funskjonskall, og resultatet blir noe sånn som dette:

((fn [pi]
     ((fn [n]
          ((fn [m]
               (println m)) 
           (* n pi))) 
      1337)) 
 3.1415) ; Skriver ut: 4200.1855000000005

Den koden er jo nærmest uleselig, og i alle fall ekstremt vanskelig å skrive (selv for dette lille eksempelet). Og det er hele poenget: Let-monaden lar meg på en enkel måte skrive imperativ-lignende kode – noe som ser ut som statements – som så blir omformet og kjedet sammen i en pipeline av funksjonskall (ren funskjonell kode).

Så hva er en monad?

Monader er altså i prinsippet, slik jeg ser det, syntaktisk sukker som omformer kode til funksjonskall etter bestemte regler. Tenk for eksempel på LINQ, som er en spørre-syntaks som under panseret blir omformet til funksjonskallene Select(), Where(), etc. Det er en formell teori knyttet til monad som snakker om bind og return, og forteller hvordan denne omformingen skal fungere. Dette er derimot ikke så viktig før man skal lage sine egne monads.., og jeg er ikke der enda. Det har derimot ikke hindret meg fra å bruke monads lenge uten å vite at det var det jeg gjorde i det hele tatt.

Monads er dermed ikke så skummelt lenger. List comprehensions og LINQ hjelper meg til å unngå løkker, og til å skrive bedre og mer deklerativ kode. Og let-monaden er helt uvurderlig i Clojure.

Andre monader

En annen, velkjent monad kalles Maybe-monad. Den fungerer i prinsippet slik at hvis én av handlingene i sekvensen/pipelinen returnerer null/ingenting så vil hele monaden returnerer det samme. På den måten kan monader forenkle kode hvor man normalt må sjekke for null. Det samme prinsippet kan brukes for å forenkle unntakshåndtering.

Ellers er vel ingen omtale av monad komplett uten å nevne I/O (input/output). Haskell bruker monads til dette, og det garanterer at I/O-handlinger (som jo har sideeffekter) kun utføres én gang, og i riktig rekkefølge. Det er altså ikke riktig at Haskell er et språk uten sideeffekter, slik jeg har blitt fortalt. Men språket krever at handlinger som har sideeffekter utføres i spesielle strukturer (altså monads) som håndterer sideeffektene på riktig måte. Dermed står man friere til å betrakte koden som “rent funksjonell”, og man kan utføre andre deler av koden i “valgfrie” rekkefølger, benytte lazy evaluering effektivt etc.

Vel, det var mine betraktninger rundt monad, og en forklaring for dem som bare har litt erfaring med funksjonelle språk. Det er kun kort tid siden jeg følte at jeg begynte å forstå konseptet, så om du betrakter noe av dette som feil får du bare si fra. Min kunnskap er selvsagt langt fra komplett, så jeg vil gjerne høre andre synspunkter.

Litt ADO.NET i IronRuby

Thursday, July 8th, 2010
Ingen kommentarer

For automatisering av driftsrutiner og andre ad-hoc oppgaver er det gull å ha et bra, dynamisk skriptspråk tilgjengelig. Og med IronRuby får du også tilgang til å bruke hele .Net-rammeverket, så da har du i både pose og sekk for å si det sånn. Akkurat nå sitter jeg og lager noen ADO.Net-spørringer mot SQL Server i IronRuby, og tenkte det kunne være greit å vise hvor enkelt det er.

Så uten noe mer fjas og vas, her er et eksempel hvor jeg henter ut noe data og printer det til konsollet:

1 load_assembly System.Data
2 include System::Data::SqlClient
3
4 # Utility method to open a DB connection, read and print
5 # some data based on a SQL command and a hash of field
6 # display names and related ordinals in the recordset.
7 def execute_read connection_string, sql, fields
8   connection = SqlConnection.new connection_string
9   command = SqlCommand.new sql, connection
10   connection.open
11
12   reader = command.execute_reader
13   while reader.read
14     puts fields.inject({}) do |acc, field|
15       #{acc} #{field.first}: #{reader[field.last]} 
16     end
17   end
18   connection.close
19 end
20
21 execute_read(
22   User ID=foo;Password=bar;Data Source=THEBOSS\\SQL2005;Initial Catalog=theDB;,
23   SELECT * from Rule,
24   Id => 0, Name => 1, Active => 3, Transp => 4)

Så lett er det å inkludere et namespace fra .Net-rammeverket og ta det i bruk.

Det eneste som kan være litt vanskelig å tyde her er måten jeg skriver ut rader til konsollet på – jeg har blitt så utrolig glad i inject (aggregate/reduce/fold/whatever) i det siste, og bruker det hele tiden, men det resulterer ikke alltid i den mest lesbare koden i verden for dem som ikke er vandt til slikt ;)

Les også: Slette/tømme MSMQ-køer med IronRuby.

filtrer, projiser, aggreger

Wednesday, June 9th, 2010
7 kommentarer

Det siste året har jeg knyttet intim bekjentskap til bruk av såkalte higher order functions; først da jeg virkelig tok i bruk LINQ og lambda i C# 3.0, deretter da jeg lærte meg Ruby, og til sist nå når jeg lærer meg å bruke språk som Erlang og F# i den funksjonelle paradigmen. Jeg har lært om Lambda calculus, og hva en closure er for noe – ting jeg nå mener alle utviklere burde ha et forhold til.

For å gi deg, kjære leser, et bedre innblikk i noe av dette, vil jeg snakke litt om noen av disse funksjonene av “høyere orden”. Higher order function betyr i bunn og grunn bare en funksjon som tar som innput en annen funskjon som spesifiserer en del av hva den skal utføre.

Filtrer

Filter er en av de mest vanlige, høyere orden funskjonene som brukes på lister eller andre datastrukturer. Gitt en liste og et predikat så brukes filter-funksjonen til å returnere de elementene i listen som tilfredstiller predikatet. Jeg sier “filter” fordi det er det funksjonen heter i de fleste programmeringsspråkene. I C# heter derimot filter-funksjonen for IEnumerable<T> Where, og den kan for eksempel brukes slik:

var aList = new []{1, 2, 3, 5, 8, 13, 21, 33, 54};
var evenNumbers = aList.Where(n => n % 2 == 0);
// array contains: 2, 8, 54

Filter-funksjonen for Enumerations i Ruby heter select (evt. find_all). Her er samme eksempel i Ruby:

1 $a_list = [1, 2, 3, 5, 8, 13, 21, 33, 54]
2 $even_numbers = $a_list.select {|n| n % 2 == 0}
3 # array contains: 2, 8, 54

Projiser

Den neste av de mest brukte, høyere orden funksjonene er Map. Denne funksjonen tar en liste eller lignende, samt en funksjon for å omforme (“mappe”) elementene i listen til en ny type elementer. Dette kaller vi også en projisering (projection). C#-varianten av map heter Select (og er altså ikke det samme som select i Ruby). Ønsker jeg for eksempel å konvertere listen med tall om til en liste med strenger kan jeg gjøre det slik:

var evenNumbersAsString = evenNumbers.Select(n => n.ToString());

I de fleste andre språk heter funksjonen map, men i Ruby har man også et alias som heter collect.

Aggreger

Filter og map er fine funksjoner som er anvendelige og lette å forstå og å huske på. En som ikke er fullt så mye brukt, blant utviklere innenfor den imperative paradigmen i alle fall, er Fold. For å forvirre oss fremstår også denne funksjonen med ulike navn i de ulike språkene – som for eksempel Reduce, Accumulate, Compress og Inject. Og i .NET heter den Aggregate:

Console.Write(evenNumbersAsString.Aggregate(
    "", (accumulator, next) => accumulator + next + " "));
// prints "2 8 54 "

Ser du hvordan den virker? I motsetning til de andre funksjonene som returnerer lister, så returnerer Aggregate én verdi. En verdi som er bygget opp ved å kalle det spesifiserte lambda-uttrykket for hvert element i listen. Accumulator er både output og input til lamdaen. For det første elementet vil accumulator ha verdien som ble spesifisert som den første parameteren til Aggregate, altså en tom streng. Når funksjonen kalles igjen for det andre elementet vil accumulator ha verdien som ble returnert fra den første. Og så fortsetter det til listen er tom, og accumulator returneres.

Her er nesten samme eksempel i Ruby, hvor fold heter inject (eller reduce om du ønsker å bruke det):

5 $foo = $even_numbers.inject() {|acc,next| #{acc}#{next.to_s}, }
6 puts $foo[0..-3] # outputs “2, 8, 54″

Har du aldri brukt en slik funksjon ser dette sikkert ganske gresk ut. Den er på kanten til å være uleselig, og hvis du jobber i et team som ikke er så godt kjent med fold/aggregate så bør du kanskje være forsiktig med å bruke den. Men jeg lover deg at om du venner deg til denne funksjonen så vil du i mange tilfeller kunne produsere mere kompakt og elegant kode.

Oppsummering i F#

Her er et oppsummerende eksempel i F#. Jeg oppretter en liste, og bruker pipelining til å sende listen gjennom filter, map (projiser) og fold (aggreger), for til slutt å skrive ut den resulterende strengen.

open System

let aList = [1; 2; 3; 5; 8; 13; 21; 33; 54]

aList |> List.filter (fun n -> n % 2 = 0)
      |> List.map    (fun n -> n.ToString())
      |> List.fold   (fun acc n -> acc + n + " ") String.Empty
      |> Console.WriteLine

Pixel-liv

Tuesday, May 25th, 2010
4 kommentarer

I denne blogposten om hvordan det er å være en gammer programmerer refererte Uncle Bob Martin i kommentarfeltet til noe han kalte Langton’s Ant. Wikipedia kunne fortelle meg at dette er en todimensjonal Turingmaskin oppfunnet av Chris Langton i 1986. Den består av et par enkle regler, men utviser kompleks, emergent adferd. Reglene er: Står mauren på en hvit celle, snu 90 grader til høyre – står mauren på en svart celle, snu 90 grader til venstre. Endre deretter fargen på cellen, og gå én celle frem.

Langton’s Ant virket som en interessant, liten programmeringsoppgave i samme bane som Conway’s Game of Life. Jeg fikk selvsagt lyst til å prøve meg på en implementasjon, så jeg satte igang i mitt favoritt-fritids-språk Ruby. For den grafiske representasjonen fant jeg frem Gosu, et genialt og enkelt rammeverkt jeg har sett litt på tidligere for å lage grafiske spill i Ruby. Gosu gir meg et vindu med en update-draw-syklus (for dem som er vandt til spill-utvikling), og enkle metoder for å plassere og manipulere grafikk.

Og her er resultatet – etter noen få hundre steg har mauren laget en liten krussedull:

Ser du den lille, rød mauren? Etter noen tusen steg virker det som om alt bare er kaos:

Men etter rundt 10.000 steg begynner det å skje noe. Mauren begynner å lage en “motorvei”. Det er ikke bevist at dette alltid vil skje, men alle forsøk som er gjort – uansett start-forhold – har før eller senere produsert et slikt mønster.

Det var ganske kult å for en gangs skyld ikke kunne skrive enhetstester for adferden jeg var ute etter. I stedet måtte jeg vente over ti tusen sykler (ca 2,8 minutter – 0,01685 sekund pr steg – en framerate på nesten 60 pr sekund) for å se at mauren min fungerte som den skulle. Jeg kunne selvsagt skrevet tester for andre ting, men valgte å kjøre uten sikkerhetsnett denne gangen.

Hvis noen er interessert i å se koden så ligger den på min github-konto. Selve logikken for å vise et todimensjonalt sort/hvitt grid i et Gosu-vindu gjorde jeg generell. Jeg implementerte også en versjon av Conway’s Life med den – den ble noe tregere enn jeg hadde håpet, skylder det på Gosu-rammeverket inntil jeg finner en bedre optimalisering i min egen kode :P

Hva har du gjort i dag for å bli en bedre utvikler enn du var i går? Her er to forslag til hva du kan gjøre. 1) Les koden min, last den ned, og forsøk enkelte modifikasjoner for å forsikre deg om at du forstår den. Kom gjerne med spørsmål til koden, eller forbedringsforslag. Eller 2) Implementer din egen Langton’s Ant. Hva med en .NET-implementasjon vha. XNA?

Dynamisk opprette typer basert på XML

Tuesday, March 30th, 2010
Ingen kommentarer

Én av fordelene med å programmere i et dynamisk språk er at man kan opprette typer og objekter basert på data som er ukjent i designtime. Som for eksempel å opprette sterkt typede objekter basert på XML. For hvorfor skal jeg jobbe med XML når jeg kan jobbe med objekter?!

Men da jeg begynte å skikke på XML-biblotekene i Ruby ble jeg faktisk litt skuffet.., det fungerte nemlig ikke sånn out-of-the-box. Jeg fant en rekke open source prosjekter som tenkte i samme bane som meg, men kvaliteten var svært varierende. Jeg fikk derfor lyst til å forsøke selv, mest får å trene på meta-programmering.

Jeg har ikke kommet så langt som jeg ønsket enda – denne formen for programmering er krevende, ettersom man har to lag med kode man må holde rede på (den man skriver, og den som oppstår når programmet kjører). Jeg har derimot noe som fungerer godt nok for et lite eksempel, så da er det på tide med en blogpost.

Sett at jeg har XML’en som følger nedenfor. Jeg ønsker å kunne parse denne med en generisk modul som gir meg en typet kolleksjon tilbake.

1 <people>
2   <person category=“Programmer”>
3     <firstname>Bob</firstname>
4     <lastname>Martin</lastname>
5   </person>
6   <person category=“Programmer”>
7     <firstname>Kent</firstname>
8     <lastname>Beck</lastname>
9   </person>
10 </people>

For akkurat dette eksempelet vil jeg ha ut en objektstruktur som den nedefor (den kunne vært bedre, men det er dette koden min produserer for øyeblikket). XmlObject opprettes ved å sende inn en xml. Når parsingen er ferdig har objektet fått en people-property. People er en kolleksjon av Person – med en count-propery og en iterator. Person-klassen som er opprettet har fått properties tilsvarende det vi finner i xml’en: category, firstname og lastname.

XmlObject_spec (2)

Her er et eksempel på bruk av XmlObject, tatt fra enhetstestene jeg brukte for å utvikle løsningen.

Utdrag fra xml_object_spec.rb:
28   def setup
29     @xml_obj = XmlObject.new PEOPLE_XML
30   end
—- snip —-
66   def test__usage_sample
67     list_of_names = []
68     @xml_obj.people.each do |person|
69       list_of_names << #{person.lastname}, #{person.firstname} (#{person.category})
70     end
71     list_of_names.first.should == Martin, Bob (Programmer)
72     list_of_names.last.should == Beck, Kent (Programmer)
73   end

PEOPLE_XML er dataene gjengitt i starten av denne artikkelen. Som du ser har @xml_obj etter setup fått en people-property jeg kan bruke til å iterere og hente ut data om personene. I linje 71 og 72 verifiserer jeg at objektene inneholdt det jeg forventet.

En fullstendig kodelisting av løsningen vil forvirre mer enn jeg ønsker.., jeg vil heller trekke frem et par ting som illustrerer hva som er mulig i forhold til in-memory kodegenerering i runtime. Nedenfor ser du for eksempel en metode jeg bruker for å opprette en ny klasse basert på et XML element.

15     def define_class_and_create_instanse name
16       name = name.capitalize
17       unless XmlObject.const_defined? name.to_sym
18         eval %(
19           class #{name} < Array
20             alias :count :length
21           end
22         )
23       end
24       klass = eval XmlObject::#{name}
25       [klass, klass.new]
26     end

Metoden tar inn navnet på elementet, og sørger for at det begynner med en stor bokstav (linje 16). Hvis klassen ikke er definert fra før (linje 17) oppretter jeg en ny klasse som arver fra Array ved å evaluere strengen i linje 19 til 21. Alias (linje 20) brukes for å døpe length-propertien til Array om til “count”, som er det jeg ønsker objektet skal ha.

I linje 24 bruker jeg eval igjen for å få tak i en referanse til den nye klassen. Til slutt returnerer jeg klassen samt en ny instans (i et array á to elementer – linje 25).

Neste eksempel er metoden jeg bruker for å opprette attributter (properties) og sette verdien for et gitt objekt.

50     def add_attr name, klass, instanse, value
51       attr_name = name.to_sym
52       klass.send(:attr_accessor, attr_name) unless klass.respond_to? attr_name
53       instanse.send #{attr_name}=.to_sym, value
54     end

I linje 52 sender jeg meldingen “attr_accessor” til klassen – se på det som å kalle en statisk metode som heter attr_accessor. Denne metoden brukes til å opprette en get/set-property (reader/writer accessor i Ruby) – attr_name bestemmer navnet på propertien.

Legg også merke til at kallet til “send” er etterfulgt av en unless-statement. Hvis klassen allerede har denne propertien, ved at den responderer på property-navnet, trenger jeg ikke opprette propertien på nytt.

I linje 53 setter jeg verdien på den nye propertien ved å bruke send-metoden til en instans av klassen. Det jeg egentlig gjør er å kaller setter-metoden for propertien – navnet på den er like navnet på propertien pluss et erlik-tegn (=). Andre parameter til send er verdien som skal settes.

Dette er bare et lite utdrag av de mange metodene og teknikkene man har tilgjengelig for introspection og dynamisk metaprogrammering i Ruby. Jeg håper dette gir et lite innblikk i de enorme mulighetene man har i dynamiske språk som Ruby i forhold til mere statiske språk.

Les også: Du MÅ beherske et dynamsik språk | Hvilket dynamisk programmeirngsspråk du skal lære deg

DCI arkitekturen

Thursday, March 18th, 2010
14 kommentarer

Eller: Hvordan jeg fikk sansen for multippel arv.

På QCon London fikk jeg ved en tilfeldighet med meg en halv forelesning med Jim O. Coplien, som snakket om noe han kalte The DCI Architecture. DCI står for Data-Context-Interaction, og er ifølge Coplien en bedre måte å designe systemer på enn dagens “normale” bruk av objektorientering.

“Object-oriented programming was supposed to unify the perspectives of the programmer and the end user in computer code: a boon both to usability and program comprehension. While objects capture structure well, they fail to capture system action. DCI is a vision to capture the end user cognitive model of roles and interactions between them.”

Fakta:
Opphavsmannen til DCI er professor ved Univeritetet i Oslo Trygve Reenskaug. Dette er samme nordmann som formulerte model-view-controller mønsteret i 1979.

For å få en komplett forståelse av hva denne visjonen er for noe bør du klikke på referansene i slutten av blogposten. Det du får her er en liten demo av min forståelse av DCI, gjennom at jeg ved hjelp av Ruby implementerer overføring av penger mellom to kontoer. Valget av programmeringsspråk er ikke tilfeldig – for å få til DCI er jeg avhengig av støtte for multippel arv, noe Ruby har i form av mix-ins. Og for at det skal bli virkelig elegant må jeg kunne påføre arven dynamisk i runtime.

La oss anta at vi allerede har en Account-klasse i domenet vårt som representerer en bankkonto. Den er veldig enkel (eller blodfattig som mange vil si), og har bare to read-only properties: navn og balanse. I tillegg har den en en liten metode for å representere kontoen som en streng (to_s tilvarer ToString() i C#).

account.rb:
1 class Account
2   attr_reader :name, :balance
3   def initialize name, balance
4     @name, @balance = name, balance
5   end
6   def to_s
7     #{@name}\t$#{@balance}
8   end
9 end

Når vi nå skal implementere overføring av penger mellom kontoer rører vi ikke den eksisterende Account-klassen. Vi vil i stedet implementere denne nye adferden i helt egne entiteter. Det å overføre penger er i teorien et abstrakt begrep, noe som kan foregå mellom andre ting enn bare Accounts. Vi vil derfor snakke om roller i stedet – overføring av penger har to roller: sender og mottager. Først implementerer jeg senderen, som jeg kaller for MoneySource:

money_tranfer.rb (del 1):
1 require transaction/simple
2
3 module MoneySource
4   class InsufficientFundsError < StandardError; end
5   attr_writer :recipient
6
7   def transfer amount
8     validate_transfer_of amount
9     Transaction::Simple.start(self) do |trans|
10       begin       
11         remove amount
12         @recipient.receive amount
13       rescue
14         log Aborting transfer of $#{amount} from #{name}
15         trans.abort_transaction
16       end
17     end
18   end
19   def validate_transfer_of amount
20     raise InsufficientFundsError if @balance < amount
21   end
22   def remove amount
23     @balance -= amount
24     log Removing $#{amount} from #{@name}
25   end
26 end

MoneySource har en metode kalt transfer som tar et beløp som eneste parameter. Først valideres det at det er tilstrekkelig med penger for å foreta overføringen. Deretter startes det en transaksjon (“transaction-simple” en et lite Ruby-biblotek som gir grei, in-memory transaksjonsstøtte) hvor MoneySource først fjerner det gitte beløpet fra sin egen balanse, for deretter å sende det samme beløpet til en mottager. Transaksjonen avbrytes om noe av en eller annen grunn skulle gå galt.

Merk at MoneySource ikke vet noe om Account, og Account vet ikke noe om MoneySource. Antagelsen MoneySource gjør er at den har variablene @name og @balance.., det er ikke tilfeldig at Account har det samme.

MoneySource har også en property for å sette en mottager (@recipient), og antar at denne har en receive-metode. Det er nå på tide å implementere den andre rollen, nemlig MoneyDestination:

money_tranfer.rb (del 2):
28 module MoneyDestination
29   def receive amount
30     @balance += amount
31     log Adding $#{amount} to #{@name}
32   end
33 end

I design time følger vi Single Responsibility Principle – hver modul/klasse har ett klart definert ansvar (om du lurte så er Accounts ansvar er å ha en balanse) – og vi unngår som nevnt tett kobling mellom entitetene. DCI er også en løsning som følger Open-Closed Principle, og er i høy grad en smidig arkitektur. Vi forsøker å unngå polymorfisme; alt er definert ett tydelig sted – vi har ingen virtuelle metoder som normalt gjør det vanskeligere å finne frem i koden.

“A program that follows the DCI paradigm exposes its inner workings to a reader of its code.” 

I runtime derimot vil vi i DCI-arkitekturen gi objektene roller i ulike kontekster, som lar dem samhandle på nye måter. Account er et data-objekt (D’en i DCI). MoneySource og MoneyDestination er roller som definerer interaskjonen (I’en i DCI) mellom objekter i en gitt kontekst (C’en i DCI). Rollene arves altså inn når de behøves.., et gitt objekt kan bekle mange, ulike roller. Det er her multippel arv kommer inn i bildet.

Den siste modulen jeg trenger definerer selve konteksten: MoneyTransfer. Den har en execute-metode som tar tre parametre: et objekt som skal være kilde, et objekt som skal være destinasjon, og beløpet som skal overføres. Execute utvider kilden med MoneySource (linje 37: MoneySource-modulen mikses inn i source-objektet i runtime). På samme måte utvides destinasjonen med MoneyDestination. Source har nå fått en property recipient og en metode transfer (som tidligere definert i MoneySource). Disse benyttes i linje 39 og 40 til å utføre overføringen.

money_tranfer.rb (del 3):
35 module MoneyTransfer
36   def self.execute source, target, amount
37     source.extend MoneySource
38     target.extend MoneyDestination
39     source.recipient = target
40     source.transfer amount
41   end
42 end

I denne demoen er source og target Account-objekter, men de behøver ikke være det.

Her er et lite skript som bruker MoneyTransfer. De fleste detaljene er uvesentlige og er derfor utelatt.

tranfer.rb:
47 require account
48 require money_transfer
49
50 setup_accounts # details omitted
51 list_accounts # details omitted
52 source = get_account Select account to transfer money from
53 target = get_account Select account to transfer money to
54 amount = get_amount  Specify amount to transfer
55 MoneyTransfer.execute(source, target, amount)
56 list_accounts

Jeg setter opp noen kontoer, lister dem i konsollet, og ber brukeren om å spesifisere source, target og amount. Jeg kaller så MoneyTransfer.execute, og lister kontoene igjen. Her er et eksempel på en slik overføring:

Sample run #1:
C:\Users\tormar\ruby_projects\DCI>transfer.rb
0: Salery       $1000
1: Usage        $1000
2: Savings      $1000
Select account to transfer money from: 1
Select account to transfer money to: 2
Specify amount to transfer: 800
Tue Mar 16 16:24:07 +0100 2010: Removing $800 from Usage
Tue Mar 16 16:24:07 +0100 2010: Adding $800 to Savings
0: Salery       $1000
1: Usage        $200
2: Savings      $1800

Siden jeg implementerte transaksjonshåndtering vil jeg også demonstrere hva som skjer om target av en eller annen grunn skulle kaste et exception (selv om det ikke har så mye med temaet å gjøre). Den røde logge-linjen kommer fra MoneyDestination:

Sample run #2:

C:\Users\tormar\ruby_projects\DCI>transfer.rb
0: Salery       $1000
1: Usage        $1000
2: Savings      $1000
Select account to transfer money from: 0
Select account to transfer money to: 2
Specify amount to transfer: 1000
Tue Mar 16 16:28:11 +0100 2010: Removing $1000 from Salery
Tue Mar 16 16:28:11 +0100 2010: Error: not able to receive money right now
Tue Mar 16 16:28:11 +0100 2010: Aborting transfer of $1000 from Salery
0: Salery       $1000
1: Usage        $1000
2: Savings      $1000

DCI er altså en reaksjon på det “oppfinnerne” ser på som en gal bruk av objektorientering. De hevder at måten vi begynte å bruke polymorphism, coupling, cohesion, etc da vi oppdaget OO på 80- og 90-tallet strider mot objektorienteringens mål, som var å gi et bedre samsvar mellom software og folks mentale modell av virkeligheten. De vil gjøre systemets adferd mer eksplisitt ved å gjøre roller til fullverdige entiteter – løsrevet fra objektene.

Dette får meg til å tenke på Command Query Responsibility Segregation (CQRS) som er så i skuddet for tiden – er ikke det også på en måte en reaksjon på det samme? Der gjør man i alle fall adferden eksplisitt gjennom command-objekter, og skreddersyr øvrig domenemodell i forhold til dem.

En siste tankevekker: Coplien og Reenskaug er to av dem som (såvidt jeg forstod) hardnakket hevder at språk som Java og C# ikke er orntlige, objektorienterte språk. I C# dreier egentlig alt seg om design av klasser, mens objekter har en mere sentral posisjon i språk som Ruby – hvor de f.eks. kan endre karakter fullstendig under kjøring av programmet. Man bryr seg sjelden om hvilken type et objekt har i Ruby, bare hvilke egenskaper det tilbyr. Jeg vet ikke om det er så fruktbart å stå på den barikaden, men det er interessant å diskutere forskjellen.

“Roles are about objects and how they interact to achieve some purpose. For thirty years I have tried to get them into the into the main stream, but haven’t succeeded. I believe the reason is that our programming languages are class oriented rather than object oriented. So why model in terms of objects when you cannot code them? And why model at all when you cannot keep model and code synchronized?” –Trygve Reenskaug

Til slutt må jeg få gjenta det jeg sa innledningsvis, ikke døm DCI ut fra det du har sett her. Dette har vært min tolkning, så gå til kildene for en dypere forståelse, som er: The DCI Architecture: A New Vision of Object-Oriented Programming | The Common Sense of Object Oriented Programming (pdf) | DCI på wikipedia | Trygve Reenskaug (UiO)

Siste kommentarer

Torbjørn
PS: Takk til Børge Hansen, som delte SCARF-modellen med meg!...
Børge Hansen
Denne likte jeg veldig godt. Du skriver godt og har gode betraktninger  Keep it up – flere trenger å tørre å lære mer om ledelse – du l...
Tormod
Er egentlig ikke overrasket. F# sin fortè er programmererens produktivitet/kvalitet og anledning til parallell kjøring. Men kjøremotoren har ...
Stian
Ville også prøvd med et større problem (x100 eller x1000 f.eks). Når man snakker så små brøkdeler av et sekund som her så kan tiden for en ell...
Torbjørn
Har ikke sjekket - tar en titt i morgen hvis tid :)...
Einar W. Høst
Mhp tco: hva sier ILSpy?...
Torbjørn
Har ikke sett noe på PSeq før, men kjenner til den typen funksjoner fra blant annet Clojure. Og problemet med slike funksjoner i sammenhenger som de...
Håvard
Veldig bra sammenligning! Har du sett på ytelsen av PSeq.* fra powerpakken? Tipper den vil gi performancehit på små mengder, men kan kanskje resul...
Torbjørn
Jeg kom på en demonstrasjon-variant til jeg burde inkludere, nemlig bruk av list comprehension (en type computation expression (også kalt monads)). ...
Einar W. Høst
Interessant, det blir en trade-off mellom eleganse og fart på en måte. Den funksjonelle løsningen med vanlig filter er ren og pen, mens den imperat...
Creative Commons-lisens
Innholdet på denne bloggen er tilgjengelig under Creative Commons Navngivelse-Ikkekommersiell-DelPåSammeVilkår 3.0 Norge lisens.

Programmeringsbloggen
Kjempekjekt.com

© 2006-2013 Torbjørn Marø

Jeg har vært en profesjonell programmerer siden 1999, og dette er min blogg. Målet med bloggen er å stimulere meg selv og alle andre til kontinuerlig eksperimentering og læring.

Jeg forsøker å være allsidig, og programmerer blant annet i C#, Ruby, Erlang og Clojure.

Jeg praktiserer TDD og andre smidige utviklingspraksiser. Jeg er opptatt av kvalitet og ren kode.

Dette og ganske mye mer kan du lese om på denne bloggen!