Diverse prosjekter

Her kan du lese om diverse utviklingsprosjekter jeg driver med – hobbyprosjekter stort sett, inkludert open source prosjekter jeg har startet eller bidrar på.

Nyttårsforsetter for 2012

Da har vi feiret nyttår, og på denne tiden tenker jeg alltid mye på hvordan det nye året vil bli og hva jeg ønsker å gjøre anderledes. Jeg er vel ikke alene om det. Her skal jeg forsøke å samle tankene, og skrive ned noen forsetter – forhåpentligvis i form av konkrete mål. Ting som over tid skal gjøre meg til en bedre, sterkere og raskere utvikler. Fellesnevneren for mye av det er fokus.

skann0001

Mindre random, planløs surfing

Alt for ofte flipper jeg opp laptopen uten å vite hva jeg skal. Jeg surfer på måfå, er innom diverse forum, sjekker infoq og andre relevante aggregator-sites, facebook, twitter, og mail – uten egentlig å oppnå så veldig mye. Dette er sløs med tid! Hvis jeg er opplagt bør jeg i stedet programmere, eller lese noe jeg allerede har planlagt å lese. Er jeg på jobb bør jeg jobbe. Er jeg ikke opplagt bør jeg ikke åpne laptopen i det hele tatt.

Jeg skal ikke slutte helt med sosiale medier eller å hjelpe folk på forumene, men jeg bør begrense det kraftig – det har tatt litt overhånd.

For å få til dette bør jeg nok lage meg en plan for hvor ofte og når jeg faktisk får lov til å sjekke disse tingene. Jeg har ikke kommer opp med noe konkret her enda, men det skal jeg!

Høyere fokus på jobb

Jeg lar meg lett distrahere. Oppdager jeg noe nytt og spennende, eller får en tanke om noe som hadde vært greit å teste ut, så er det ofte vanskelig å konsentere seg om det jeg burde gjøre. Møter og email-kommunikasjon er også forstyrrende, og totalt sett gjør dette at jeg i perioder ikke er særlig produktiv.

Jeg må lære meg å administrere min egen tid og mitt fokus bedre. Pomodoro var en teknikk som hjalp noe. Men jeg trenger mer. Jeg ser for meg at jeg må strukturere arbeidsdagen – sette av tidsbokser for epost, forberedelse til møter, oppfølging av teamet og lignende. Jeg må passe på at jeg ikke må context-switche for ofte, for det er krevende for dem som driver med programmering.

Lese

Jeg har alltid vært flink til å lese, men i høst har jeg lest for lite – og som jeg sa i denne blogposten skyldes det blant annet at jeg har sluttet å reise kollektivt. Planen nå er derfor å sette av én time hver kveld til lesing. Det gjør ikke noe om jeg ikke får lest hver eneste dag, men regelen skal være at jeg leser om det ikke er noe annet jeg skal gjøre.

Fysisk trening

Vi har et fantastisk treningstilbud på jobben som jeg ikke har vært flink nok til å benytte meg av. Trening er noe jeg gjør i perioder – ofte holder jeg på i tre, fire måneder, for så å “glemme det” i en minst like lange periode. God fysisk form er viktig for å kunne holde på konsentrasjonen og for å få utslipp for stress. Dette må jeg bli bedre på.

Mestre tastataturet

Jeg har en gjennomført (eller fastgrodd) tastaturteknikk som fungerer greit nok. Jeg skriver nå opp mot 46 WPM når jeg fokuserer maksimalt (målt på typingtest.com). Men jeg skriver med få fingre, og ser mye på tastaturet.

Skrivehastighet og teknikk er viktig for å komme i god flow. Jeg ønsker derfor å lære meg og øve inn touch. Hvis jeg fokuserer på dette en halvtime hver dag i noen uker bør jeg kunne komme langt. Dette vil kunne hjelpe meg å holde bedre fokus og la tankene flyte bedre mens jeg programmerer (eller mens jeg blogger for den saks skyld).

Øve, øve, øve

Jeg er ganske flink til å trene på mine programmeringsferdigheter, og det må jeg selvsagt fortsette med. I år vil jeg forsøke å fokusere litt mer på metaprogrammering, gjerne i Lisp.

Jeg vil også forsøke å øke størrelsen litt på oppgavene jeg gir meg selv, sånn at jeg får økt sjanse til å eksperimentere med ulike design-teknikker. Jeg implementerer mye algoritmer, ting som løses på 20 til 60 linjer kode, men trenger mer trening på større ting – kanskje spesielt innenfor funksjonell programmering.

En siste ting jeg gjerne vil eksperimentere mer med er arkitekturer for å gjøre systemer “scriptbare”. Embedding av Ruby, JavaScript eller lignende runtime i applikasjoner eller tjenester. Jeg har gjort litt av dette, men vil at det skal bli en helt naturlig del av verktøybeltet mitt, slik at jeg bare kan smyge det inn når behovet dukker opp.

Et større hobby-prosjekt?

Et par ganger i året bruker jeg å starte på et eller annet større prosjekt som jeg håper kan bli noe fornuftig, men som jeg likevel vet er en stør sjanse for at ikke blir noe av. Programmeringsspråket MIST er et bra eksempel fra 2011 – det var et par interessante måneder mens jeg holdt på, men jeg ser ikke for meg at jeg kommer til å fullføre planene mine nå.

Det skremmer meg likevel ikke fra å prøve igjen. Jeg har en idé til et større prosjekt nå også, noe som kan bli ganske så gøy for mange om jeg får det til. Jeg sier ikke mer enda.., vil holde kortene litt tett til brystet en stund til. Kanskje trenger jeg noen av dere som beta-testere utpå våren eller sommeren engang. Vi får se!

Konklusjon

Som vanlig har jeg mange planer og ideer om hvordan året skal bli. Jeg har noen konkrete ting jeg skal jobbe med, og spesielt skal jeg bli flinkere til å holde fokus.

Når det kommer til bloggingen så tenker jeg bare å fortsette som før – blogge når det dukker opp noe som jeg får lyst til å dele. Jeg vil gjerne lage flere videoer, for det har vært gøy. Men det er også tidskrevende, og jeg vil ikke prioritere det fremfor det andre tingene.

Nå gjelder det bare å huske på dette her. Da er det bra jeg har denne bloggposten å komme tilbake til :)

Godt Nytt År alle sammen!!!

For 12 år siden…

Wayback Machine (www.archive.org) er en fantatisk website. Der finner du rett og slett et historisk arkiv av internett, slik det så ut i går, forrige uke, i fjor, eller for ti år siden. I dag har jeg mimret litt, og tatt en titt på min første “internett suksess”, nemlig min Learn Tetris in 21 days!

LearnTetris

Dette var en site jeg lagde mens jeg studerte på Universitetet i Bergen, og den er i alle fall 12 år gammel nå. Den hadde et fullstendig psykedelisk design, og var selvfølgelig bare laget for gøy.

Men den ble ganske populær, og jeg hadde noen svært gode plasseringer på søkemotorene som var i bruk på den tiden. Yahoo! gav meg 4. plass (mener jeg å huske) om man søkte på bare tetris, og kombinerte man ordene tetris og online så eide jeg lenge førsteplassen. Selv om selve siten har vært død i årevis finner jeg fortsatt et par referanser til den på nettet, hvor folk sier de syntes den var “pissemorsom”.

Selv synes jeg i alle fall det er ganske morsomt å lese hva jeg skrev den gangen – og ikke minst svært, svært pinlig! Fra tetris-sidene kan man browse seg videre til mer personlig innhold, og der er det veldig mye rart må jeg innrømme. 1999 er heldigvis lenge siden.

Her er hvordan jeg beskrev meg selv den gangen:

I'm a 23 year old guy, I wear nail polish (I'm straight by the way), have purple hair, and can usually be found jumping up and down at parties screaming "...unt die Sunne Sheeeeeeiiiint!!!"

I'm a student of Computer Science at the University of Bergen (Norway), and do Java, Visual Basic and Delphi programing, Active X, HTML, CSS, SQL, DBMS and other fancy acronymes, I work with n-tier application development .. and so on (read my full personalia) ! I live behind a keyboard in Bergen, but I'm really from Haugesund - the town of the seaguls!

I felt it was time to get a life,

...since apparently they'r terribly useful. So now I also spend much of my time in movie theaters, in pubs, beeing absolutely blasted, dancing my socks off, wish I didn't stop going to the dojo, hanging out with my friends, reading Terry Pratchett books, bowling, wanting all kind of girls and drinking quite a lot of Coca-Cola - in a desperate attempt to find time to read for my exams.

This, I gather, is a good life...

Rettskriving har som du kanskje ser aldri vært en av mine sterkeste sider.

Så hvis du er interessert i å lære tetris, og har 21 dager å kaste bort, så må du gjerne ta en titt på siten min. Ikke skyld på meg om du får vondt i hodet av “tapeten”.

Mist får en side

Denne blogposten presenterer den nye websiden jeg har laget for programmeringsspråket Mist, samt de rundt 20 linjene Ruby-kode som skulle til for å generere siden.

Ethvert programmeringsspråk trenger en egen side – i alle fall om andre enn designeren selv skal bruke det. Og nå har jeg som sagt begynt på en side for Mist. Den har ikke så mye innhold enda, men design og struktur er på plass. Her er et screenshot (klikk for å gå til siden):

mist_page

Siden skiller seg bort fra ting jeg har gjort tidligere på flere punkter. Blant annet har jeg innsett at HTML ikke er XML, og har nå gått for mere pragmatisk HTML5, hvor jeg blant annet har droppet ting som HTML, HEAD og BODY-tags (de skal faktisk være helt unødvendige). Jeg har også brukt mye absolutt posisjonering, og til og med litt fixed. Jeg har satset på rent design og ren markup, og er ikke nervør om noen skulle velge å titte på koden.

Jeg har også oppdaget Google Web Fonts, som lar meg inkludere et hav av ulike skrifttyper på sidene uten store problemer. Eneste faren er at jeg kanskje går litt for langt, sånn som man ofte gjør når man får et nytt leketøy. Synes du jeg har gjort det?

Generering av statiske websider med Ruby

Mist-siten er hostet som Github Pages, tilknyttet Github repositorien. Sider som serves på denne måten må være statiske html-sider. Jeg ville derimot basere siten min på en template, så for å ikke måtte repetere meg selv måtte jeg generere sidene før jeg lastet dem opp.

For dette formålet støtter Github Pages et verktøy som heter Jekyll, som jeg har brukt tidligere, og som lar deg generere statiske websider basert på maler. Denne gangen valgte jeg likevel å lage mitt eget opplegg basert på kun noen få linjer Ruby.

Først lagde jeg en fil som jeg kalte genlib.rb. Den inneholder to funksjoner: Den første leser malen som er lagret i filen template.html, og returnerer den som en streng. Den andre – generate – tar inn en hash med parametre, henter ut malen, fletter inn det som er sidespesifikt i malen, og lagrer resultatet som en html-fil.

10 def template
11   template = ''
12   File.open('template.html', "r") do |file|
13     while (line = file.gets)
14       template += line
15     end
16   end
17   return template
18 end
19 
20 def generate args
21   name = args[:file] + ".html"
22   File.open(name, "w") do |file|
23     file.puts template.
24       gsub(/\{CONTENT\}/, args[:content]).
25       gsub(/\{QUOTE\}/, args[:quote]).
26       gsub(/\{TITLE\}/, args[:title])
27   end
28   puts "Generated file #{name}"
29 end

For hver siden jeg skal generere lager jeg så en ny fil som skal kalle generate-funksjonen. Nedenfor er gen_download.rb, som genererer download.html. Den definerer ønsket filnavn (linje 13), sidetittel (14), et sitat som skal brukes i toppen av siden (16-23) og selve innholdet på siden (25-33). Til slutt kaller den generate, og siden blir produsert.

10 require "./genlib"
11 
12 page = {}
13 page[:file] = "download"
14 page[:title] = "Download Mist"
15 
16 page[:quote] = <<EOF
17 <p class="quote">
18     "Language designers are not intellectuals.<br/>
19     They're not as interested in thinking as you might hope.<br/>
20     They just want to get a language done and start using it."<br/>
21     - Dave Moon
22 </p>
23 EOF
24 
25 page[:content] = <<EOF
26 
27 <h1>Download Mist</h1>
28 <p>
29   No downloads here yet! Get the source from 
30   <a href="https://github.com/tormaroe/mist">Github</a> 
31   if you're interested.
32 </p>
33 EOF
34 
35 generate page

Til slutt laget jeg en fil jeg kalte gen.rb. Den henter ut og kjører alle filer som har et navn som begynner på “gen_”. Ved å kjøre gen.rb vil jeg dermed få generert opp alle de statiske sidene mine, klare for opplasting til serveren.

1 if __FILE__ == $0
2   Dir.foreach(".") {|f| f.match(/gen_/) { load f } }
3 end

Alternativt kunne jeg har brukt Ruby ERB templating (ala klassisk ASP eller PHP), eller kanskje et annet templating system, for å generere filene. Men for mitt formål valgte jeg altså denne gangen en mere basic løsning.

Har du noen bemerkninger til Mist, webdesignet, det kommende innholdet, eller til Ruby-koden min? Nøl ikke med å legge igjen en kommentar!

Programmeringsspråket Mist

logoEn nerd må ha noe å gjøre på. Så i sommer begynte jeg å implementere et nytt programmeringsspråk. Jeg har valgt å kalle det Mist!

Navnet ble valgt før jeg ble gjort oppmerksom på at det på tysk betyr gjødsel (for å si det pent).

Mist skal være et general purpose, dynamisk språk med fokus på den funksjonelle programmeringsparadigmen, og tilhører Lisp-familien. Det kjører på .NET-plattformen, og er implementert i C#.

Hvorfor?

Jeg begynte på dette for å lære, og for å ha det gøy. Alle utviklere med noen år på baken og respekt for seg selv burde vite hvordan man bygger et språk, og den eneste måten å kontrollere at man vet hvordan er å gjøre det.

Følelsen jeg hadde da jeg f.eks. implementerte rekursjon, eller da jeg fikk til closures, var helt hærlig. Følelsen litt senere av å forstå at jeg hadde løst scopes feil (etter å ha lest litt i Structure and Interpretation of Computer Programs), for så å rette det opp slik det burde være, var enda bedre. Jeg har allerede lært mye av dette, selv om jeg er LANGT FRA FERDIG.

Kan det brukes til noe?

Mist kommer ikke til å bli det neste, store språket som alle kommer til å ville bruke. Det kommer ikke til å kunne konkurrere med for eksempel Clojure. Men kanskje finnes det en liten nisje hvor Mist kan komme til nytte? Selv om jeg gjør dette for å lære og å ha det gøy, så har jeg i alle fall tenkt å holde på til jeg har et “ferdig” språk som kan tas i bruk av andre enn meg selv.

Mist kan brukes på flere måter; spåket er implementert i form av en høy-nivå tolker som består av én enkelt .NET dll. Denne kan refereres direkte i .NET-prosjekter for å gjøre dem “skriptbare”. Jeg har også laget en enkel Read-Eval-Print Loop hvor man interaktivt kan eksperimentere med Mist-kode, eller laste og kjøre Mist-filer.

Som en tredje opsjon har jeg tenkt å tilby en slags kompilator som tar en eller flere Mist-filer som input, og genererer en “standalone” exe-fil.

Noen betraktninger

Når man skal implementere dynamiske språk på .NET-plattformen kan det være naturlig å basere seg på Dynamic Language Runtime. Det valgte jeg derimot å ikke gjøre. Jeg ville lære mest mulig som jeg kan benytte uavhengig av plattform, og ikke bruke tid på å sette meg inn i et biblotek hvor deler av jobben allerede er gjort.

Det finnes forøvrig mange beskrivelser på nett og andre steder av hvordan man implementerer Lisp-lignende språk. Jeg valgte å ikke studere disse – mitt fokus var igjen å bruke og lære generelle teknikker for å utvikle parser, tolker og/eller kompilator (hovedinspirasjonen til prosjektet er boken Language Implementation Patternsog så trekker jeg på det jeg allerede kan om Clojure, Scheme og Common Lisp).

Ved første øyekast ser Mist ut som en hvilken som helst Lisp-implementasjon, men det skiller seg nok litt fra de fleste andre likevel. Blant annet har jeg valgt å ikke basere liste-strukturene på såkalte cons-par. I stedet bruker jeg listene i .NET basebibloteket.

Det er også vanlig at store deler av språk i Lisp-familien implementeres i språket selv. Mist består derimot av ganske mye C# – dette er for å utnytte det som allerede finnes i .NET, blant annet i et håp om å gjøre koden raskere enn jeg ellers ville klart.

Veien videre

Men det er tidlig enda. Jeg ser for meg at jeg skal jobbe med Mist – på bussen, fergen, og på kveldstid – i et par måneder til før jeg har noe som kan kalles en beta. Og estimater er til for å sprekke!

Av ting som jeg gleder meg til å gå igang med nå kan jeg nevne reader-macros, syntax quoting og tail call optimalisering. Når det gjelder tail calls så har jeg en ide om at jeg kan løse det med contiuation passing, men jeg må nok gjøre litt mer research før jeg er sikker på hvordan det skal gjøres.

Jeg har også planer om en dyp integrasjon med .NET (bruke .NET-typer fra Mist) som vil skille seg endel i fra hvordan f.eks. Clojure er integrert med Java. Med det gjenstår å se om det lar seg realisere.

Er du interessert i å ta en titt på koden, eller laste det ned og ta Mist på en liten kjøretur, så finner du prosjektet på Github. Bedre dokumentasjon på bruk og API kommer etterhvert.

Send SMS med Python

Hvis du er en Python-utvikler som lurer på hvordan du skal få til å sende SMS-meldinger fra programmene dine, så kan du slutte å lure nå. Denne uken har jeg startet open source-prosjektet pswinpy, som er et klientbiblotek for sending av SMS. Jeg kaller versjon 0.0.1 for en beta, men det fungerer helt fint, og lar deg sende meldinger slik som dette:

1 from pswinpy import API
2 
3 api = API('black_knight','p@$$w0rd')
4 api.sendSms(4712345678, "It's just a flesh wound.")

Pswinpy tilsvarer pswincomgem-prosjektet jeg startet for Ruby-utviklere for et par måneder siden, og krever en Gateway-konto hos PSWinCom

Du kan også spesifisere avsender, bruke utsatt levering, og sende CPA- og GAS-meldinger (ta betalt for varer og tjenester) om Gateway-kontoen din støtter dette.

Noen som vil hjelpe til?

Pswinpy er mitt aller første prosjekt i Python. Det eneste jeg vet om ideomatisk Python-kode er at det aldri kan skade å inkludere en referanse til Monty Python. Derfor hadde det vært greit om noen fra Python-miljøet kunne tatt en titt på koden og se over om ting ser fornuftig ut.

“Fork” meg gjerne og foreslå forbedringer!

Jeg fikk også til slutt til å publisere pswinpy som en pakke til PyPI, den sentrale indeksen over biblotek og moduler for Python. Dette var ikke like smertefritt som å publisere til RubyGems.org – og å installere Python-pakker er heller ikke så enkelt som å installere Ruby-gems, men ikke så veldig vanskelig heller når man først har lært det.

PSWinCom gem’en har forresten blitt lastet ned over 240 ganger allerede. Jeg vet ikke hvor mange som faktisk bruker den, men synes det er bra uansett. Nå håper jeg utviklere i Python-miljøet vil flokke seg rundt pswinpy på samme måte.

Ping Ring del 8: Python

Dette er del 8 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.

Etter å ha implementert Ping Ring i de programmeringspråkene som sitter sånn noenlunde i fingrene, fikk jeg lyst til å gjøre en versjon i et språk jeg ikke kunne fra før. Følgende er dermed mitt første Python-program. Som i de andre implementasjonene har jeg forsøkt å følge de idiomene som gjelder i språket, men med null erfaring er det opp til deg å vurdere om jeg har lykkes med det.

Etter litt googling og 20 minutter utvikling kom jeg frem til en fungerende Ping Ring. Det virket som om den vanlige måten å løse samtighet på i Python er å arve fra Thread-klassen, så jeg valgte å gjøre det. Implementasjonen skiller seg derfor noe fra de andre objektorienterte løsningene (C#/Ruby/Boo).

1 import sys
2 import time
3 import datetime
4 import socket
5 from threading import Thread
6
7 last_ping_time = datetime.datetime.now() # Global state
8
9 class TcpThread(Thread):
10   def __init__ (self, self_port, other_port):
11     Thread.__init__(self)
12     self.port = self_port
13     self.other = other_port
14   def get_socket(self):
15     return socket.socket(socket.AF_INET, socket.SOCK_STREAM)
16
17 class Listener(TcpThread):
18   def run(self):
19     while 1:
20       s = self.get_socket()
21       s.bind((127.0.0.1, self.port))
22       s.listen(1)
23       conn, addr = s.accept()
24       ping = conn.recv(1024)
25       self.process(ping)
26       conn.close()
27   def process(self, message):
28     global last_ping_time
29     last_ping_time = datetime.datetime.now()
30     print Received, message
31     Pinger(self.port, self.other).start()
32
33 class Pinger(TcpThread):
34   def run(self):
35     time.sleep(1)
36     s = self.get_socket()
37     try:
38       s.connect((127.0.0.1, self.other))
39       s.send(PING from %(port)s % {port: self.port})
40       s.close()
41     except Exception:
42       print *** Failed sending ping!
43
44 class Alerter(TcpThread):
45   def __init__ (self, self_port, other_port, max_delay):
46     TcpThread.__init__(self, self_port, other_port)
47     self.max_delay = datetime.timedelta(seconds=max_delay)
48   def run(self):
49     global last_ping_time
50     while 1:
51       time.sleep(5)
52       ping_delay = datetime.datetime.now() – last_ping_time
53       if ping_delay > self.max_delay:
54         print *** ALERT, RING BROKEN! + \
55             No ping in %(delay)s. % {delay: ping_delay}
56         Pinger(self.port, self.other).start()
57
58 this_port = int(sys.argv[1])
59 other_port = int(sys.argv[2])
60 max_delay = int(sys.argv[3])
61 initial_ping = sys.argv[4] == true
62
63 print ** Python Ring Server (, this_port, )
64
65 if initial_ping: Pinger(this_port, other_port).start()
66 Listener(this_port, other_port).start()
67 Alerter(this_port, other_port, max_delay).start()

På forhånd trodde jeg jeg ville like Python ganske bra. Jeg har jo litt erfaring med Python-lignende syntax fra Boo, og synes den er ganske elegant.

Det dukket imidlertid raskt opp et par ting jeg hadde problemer med å synes godt om. Dette var spesielt knyttet til scope; når man får sende inn en self-variabel i alle metoder virker det som om man har hacket inn et objekt-system i et språk som egentlig ikke er objektorientert. Å i tillegg måtte eksplisitt deklarere alle globale variabler man ønsker å benytte i en metode kan forsvares – men det minte meg om PHP, og jeg likte det ikke.

Bruk av moduler/namespace gjør meg også litt forundret. Hvorfor i all hverden må jeg si datetime.datetime.now()? Det er mulig det kan gjøres anderledes, men jeg fant dette i mange kodeeksempler.

Alt i alt liker jeg løsningen min dårlig! Designet med ulike klasser førte til alt for mye plumbing-kode. Det er mange som skryter mye av Python, men etter å ha prøvd litt (og jeg må innrømme at det er veldig lite å basere slike uttalelser på) skjønner jeg ikke helt hva de snakker om. Det slår meg at de som sier dette sansynligvis aldri har forsøkt seg på Ruby, som jeg føler er både enklere og mere sofistikert på en gang.

Bonus: Et community-bidrag..

Jeg har mottatt endel bidrag fra utviklere som har laget sine løsninger på Ping Ring i diverse språk jeg ikke behersker selv. En av disse er en 17 år gammel Python-entusiast som kaller seg oddstr13. Han har laget en fullt fungerende implementasjon med flere utvidelser i forhold til spesifikasjonen. Oddstr13′s Ping Ring sender bl.a. epost når en server går ned.

Her er et lite utdrag som viser noen av konfigurasjonsmulighetene:

334     %s [--listen=<port>] [--remote=<host>[:<port>]] [--timeout=<int>]
335    
336     –listen    Port to bind to.
337     –remote    Host to connect to.
338     –timeout   Time after last ping received before sending email.
339     –mail      Send mail on ping timeout. (Default) (Not yet implemented)
340     –nomail    Oposite of above.
341     –mailto    Address to mail. (Not yet implemented)
342     –mailfrom  Address mail apears to come from. (Not yet implemented)
343     –mailhost  Adress to smtp server. (Not yet implemented)
344    
345     (Not yet implemented) simply means that the cli argument dosn’t work
346     the functionality is however there, but you have to edit the script.

Programmer er tilgjengelig i sin helhet her.

Denne implementasjonen er på neste 370 linjer – min var under 70. Mye av koden er infrastruktur for konfigureringen og behandling av kommandolinje-argumentene. Oddstr13 har ikke brukt klasser, men har splittet opp funksjonaliteten sånn passe bra i metoder med beskrivende navn. Det at han har brukt hashtabeller (eller maps eller dictionaries eller hva de nå heter i Python) gjør endel av koden litt “bråkete”, og jeg tror jeg ville løst dette anderledes.

Men for all del, jeg er imponert og takknemlig for at han ville løse oppgaven min og spre litt Python-kunnskap. Takk skal du ha!

Flere community-bidrag, i til tider overraskende språk, presenteres snart…

Tidligere i serien: Introduksjon | C# | Ruby | Boo | Erlang | Clojure | Clojure m/Agenter.

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.

Ping Ring del 7: Clojure m/Agenter

Dette er del 7 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.

Clojure har noe som kalles agents. Jeg skjønte til å begynne med lite av hvordan jeg burde bruke dem, men etter å ha sett Rich Hickeys to og en halv timers lange demonstrasjon av concurrency i Clojure (anbefales) følte jeg meg klar for å gjøre et forsøk.

Mye av koden nedenfor er lik den du finner i del 6. En detalj er at jeg har endret hvordan jeg samler opp kommandolinje-argumentene. I orginalversjonen lagret jeg dem i et struct-map, og sendte dem rundt til alle funksjonene. Denne gangen har jeg bare lagret dem i fire ulike verdier definert globalt i namespacet, og bruker dem direkte i funksjonene (ikke helt som Thomas foreslo altså).

Matrix_Agents

Hovedforskjellen er derimot hvordan jeg har implementert den tråden som skal aktivere alarmen om det ikke kommer pings innenfor et visst tidsrom. Her har jeg nå brukt en agent, definert i linje 26. Agenten har en verdi – i utgangspunktet 0 for denne agenten – og verdien kan endres gjennom å sende den en fuksjon. Funksjonen jeg sender agenten (ved oppstart) er check-delay, definert i linje 32. Innparameter til funksjonen er alltid nå-verdien til agenten. Resultatet av funksjonen vil bli den nye verdien.

Alerter-agenten fungerer slik at den først venter så lenge som det er akseptabelt å ikke motta ping. Deretter sjekker den om det har ankommet en ping. Til dette bruker jeg et atom – en spesiell referansetype som egner seg for samtidighet – som er definert i linje 9, og som settes av listen-for-pings når en ping har blitt mottatt. Jeg har altså gått bort fra å bruke tidspunkt for når ping ble mottatt, og har nå en løsning som ligner mer på den jeg lagde i Erlang – bare i et mere behagelig språk!

Hvis ping var mottatt resetter agent-funksjonen atomet til false, og returnerer 0 (ingen alarmer aktivert). Hvis ping ikke var mottatt vil funksjonen trigge alarmen, og da bruker den agent-verdien sin til å beregne hvor mange sekunder det har gått siden sist ping (antall alarmer på rad ganger antall sekunder forsinkelse tillatt).

Men før funksjonen gjør dette – i linje 35 – køer den opp et nytt kall til seg selv. *agent* er nemlig en referanse til den agenten funksjonen kjører på. Agenter er alltid synkroniserte, dvs. at et nytt kall til agenten ikke vil starte før det forrige kallet er avsluttet. Dermed fungerer dette altså nærmest som en uendelig rekursjon.

Jeg vet ikke om jeg helt klarte å formidle hvordan agenten fungerte, men tar du en god titt på koden i tillegg burde det hele bli klarere:

1 (use clojure.contrib.server-socket clojure.contrib.duck-streams)
2 (import (java.net Socket))
3
4 (def this-port    (Integer. (nth *command-line-args* 0)))
5 (def other-port   (Integer. (nth *command-line-args* 1)))
6 (def max-delay    (Integer. (nth *command-line-args* 2)))
7 (def initial-ping (Boolean. (nth *command-line-args* 3)))
8
9 (def has-received-ping? (atom false))
10
11 (defn ping []
12       (future (Thread/sleep 1000)
13               (try (spit
14                      (Socket. “127.0.0.1″  other-port) 
15                      (str     “PING from “ this-port))
16                    (catch Exception e
17                           (println “*** Failed sending ping!”)))))
18
19 (defn listen-for-pings []
20       (create-server this-port
21                      (fn [in-stream _]
22                          (reset! has-received-ping? true)
23                          (println “Received” (slurp* in-stream))
24                          (ping))))
25
26 (def alerter (agent 0)) ; value is number of alerts in a row
27
28 (defn send-alert [delay]
29       (println “*** ALERT, RING BROKEN! No ping in” delay “seconds.”)
30       (ping))
31
32 (defn check-delay [alert-count]
33       (let [new-count (inc alert-count)]
34         (Thread/sleep (* max-delay 1000))
35         (send-off *agent* check-delay) ; queue another check
36         (if @has-received-ping?
37           (do (reset! has-received-ping? false) 0)
38           (do (send-alert (* max-delay new-count)) new-count))))
39
40 (defn main []
41      (println (format “**Clojure Ring Server (with Agents) (%s)” 
42                       this-port))
43      (when initial-ping (ping))
44      (send-off alerter check-delay)
45      (listen-for-pings))
46
47 (main)

Alt i alt synes jeg denne løsningen ble hakket mer elegant enn den forrige. Jeg slipper å rote med datoer, og den er faktisk også mer presis i når den utløser alarmen. For rasjonalet bak å bruke agenter må jeg henvise deg til Rick Hickey eller andre som har blogget om temaet. Dette var bare en liten demo av en av måtene de kan brukes på. Jeg lover derimot mer om samtidighet i Clojure i fremtidige blogposter.

Tidligere i serien: Introduksjon | Del 2 (C#) | Del 3 (Ruby) | Del 4 (Boo) | Del 5 (Erlang) | Del 6 (Clojure).

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.

Ping Ring del 6: Clojure

Dette er del 6 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.

Det måtte jo nesten komme – en implementasjon av Ping Ring i mitt nye favorittspråk, Clojure. Og dette ble også den implementasjonen jeg liker best sålangt. Les videre for å finne ut hvorfor.

Én av Clojures fire grunnpilarer er at det er designet for samtidighet. Det har derfor en rekke virkemidler for å gjøre den typen programmering enklere, som persistant and immutale data structures, transaksjonelt programvareminne (STM), samt flere datatyper med innebygde samtidighetsprimitiver (var, ref, agent og atom). I dette programmet har jeg brukt future-makroen for å kjøre de ulike delene i separate tråder – en enkel sak basert på agents, som jeg blogget om i fremtidige løfter.

TCP-kommunikasjonen var også greit og passe elegant. For å sende oppretter jeg en vanlig java-socket og bruker metoden spit (!) til å sende en streng gjennom den (linje 12 til 14). For lytte-biten fant jeg funksjonaliteten jeg trengte i Clojure Contrib-biblioteket: create-server tar inn en port og en funksjon som vil bli brukt til å håndtere forespørsler.

Her er koden i sin helhet:

1 (use clojure.contrib.server-socket clojure.contrib.duck-streams)
2 (import (java.net Socket) (java.util Date))
3
4 (defstruct server-args :this-port :other-port :max-delay :initial-ping)
5
6 (def last-ping-time
7      (atom (Date. (long 0)))) ; January 1, 1970, 00:00:00 GMT
8
9 (defn send-ping [args]
10       (future
11         (Thread/sleep 1000)
12         (try (spit ; is this the coolest function name or what?!
13                (Socket. “127.0.0.1″  (args :other-port)) 
14                (str     “PING from “ (args :this-port)))
15              (catch Exception e
16                     (println “*** Failed sending ping!”)))))
17
18 (defn listen-for-pings [args]
19       (create-server (args :this-port)
20                      (fn [in-stream _]
21                          (reset! last-ping-time (Date.)) ; that’s now!
22                          (println “Received” (slurp* in-stream))
23                          (send-ping args))))
24
25 (defn date-diff “get diff of two dates in seconds” [a b]
26       (-> (- (.getTime a) (.getTime b))
27           (/ 1000) int))
28
29 (defn ping-delay “get time since last ping in seconds” []
30         (date-diff (Date.) @last-ping-time))
31
32 (defn ping-delayed? [max-delay]
33       (> (ping-delay) max-delay))
34
35 (defn watch-for-missing-pings [args]
36       (Thread/sleep 5000)
37       (when (ping-delayed? (args :max-delay)) 
38         (println “*** ALERT, RING BROKEN!”
39                  “No ping in” (ping-delay) “seconds.”)
40         (send-ping args))
41       (recur args)) ; AGAIN!
42
43 (defn main [args]
44       (println (format “**Clojure Ring Server (%s)” (args :this-port)))
45       (when (args :initial-ping) 
46         (send-ping args))
47       (future (listen-for-pings args))
48       (future (watch-for-missing-pings args)))
49
50 (main (struct server-args ; parse command line args into struct
51               (Integer. (nth *command-line-args* 0)) ; this-port
52               (Integer. (nth *command-line-args* 1)) ; other-port
53               (Integer. (nth *command-line-args* 2)) ; max-delay
54               (Boolean. (nth *command-line-args* 3)))) ; initial-ping

Når det gjelder mengde kode stiller denne implementasjonen på linje med Ruby og Boo, den er enklere enn Erlang-versjonen, og mindre bråkete enn C#. Å beskrive logikken i Clojure-syntax var ganske greit, og programmet bør være enkelt å sette seg inn i om man klarer å undertrykke sin parantesfobi.

Skal jeg være litt kritisk så er det jeg likte minst ved koden det jeg måtte gjøre for å sjekke om en ping er forsinket – dvs. beregningene knyttet til dato/tid (linje 25 til 33). Jeg måtte bryte det opp på denne måten for at det skulle bli forståelig, og skulle ønsket den var enklere. Men man kan vel ikke få alt man ønsker seg?!

Koden for å hente inn argumentene fra kommandolinjen (linje 51 til 54) er også litt mere bråkete enn det du finner i de andre språkene, men det har ingen videre innvirkning på totalinntrykket eller kompleksiteten.

Jeg gjorde denne implementasjonen før jeg hadde satt meg orntlig inn i bruk av Clojure agents (som ikke er det samme som Erlang actors, men som likevel har noen fellestrekk i hvilke problemer de løser). På båten hjem i går implementerte jeg en ny versjon av Ping Ring i Clojure som bruker agents, og den får du se i neste del…

Tidligere i serien: Introduksjon | Del 2 (C#) | Del 3 (Ruby) | Del 4 (Boo) | Del 5 (Erlang).

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.

Ping Ring del 5: Erlang

Dette er del 5 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.

erlang_posterJeg har ikke kunnet komme med noen banebrytende oppdagelser sålangt i denne serien. Språkene jeg har valgt har vært for like. Jeg har egentlig implementert nøyaktig det samme programmet i alle språkene, og ikke funnet noen forskjeller av stor nok betydning til å utrope ett av språkene som mere egnet for oppgaven enn de andre. Det er på tide å forsøke noe radikalt anderledes…

Derfor har jeg nå valgt Erlang. Mens C#, Ruby og Boo er objektorienterte språk, er Erlang funksjonsbasert. Dette betyr blant annet at jeg bør finne en alternativ måte å løse shared state på – i de tidligere implementasjonene har listener- og alerter-trådene begge hatt tilgang til en variabel med tidspunktet for sist innkommende ping. Slikt fnyser man av i den funksjonelle verden.

Viktigere er det at Erlang er designet med fokus på samtidighet, og dessuten mye brukt i løsninger som baserer seg på TCP og andre lavnivå-protokoller. Jeg hadde derfor store forventninger til Erlang-implementasjonen av Ping-Ring.

Actor model

Et sentralt begrep i Erlang er Actor Model. Det språket i praksis støtter er at man oppretter isolerte prosesser (actors), og at prosessene deretter enkelt kan sende meldinger til hverandre. Hver prosess har en inbox i form av en kø, og leser og behandler meldinger asynkront i forhold til resten av programmet.

Figuren nedenfor er et gjensyn fra Ping Ring del 2, og illustrerer designet jeg brukte da jeg har implementert løsningene i C# / Ruby / Boo. Her ser du at jeg hadde to tråder som delte en variabel, og som opprettet egne tråder hver gang et ping skulle sendes.

pingring_alg_1

Neste figur illustrerer hvordan Erlang-løsningen skiller seg fra de foregående. Her har jeg tre selvstendige prosesser som hver holder på sin egen state, og som kommuniserer via meldinger. Både Listener og Alerter sender for eksempel melding til Pinger når de ønsker å få sendt en ping.

pingring_alg_2

Mange ser på actor model som en bedre/mere riktig form for objektorientering, og spår at vi kommer til å få se flere språk med innebygd støtte for denne modellen i årene som kommer.

Problemene

Erlang gir meg altså muligheten til å lage et ganske elegant design for å løse denne oppgaven. Problemet ligger derimot i detaljene. Før du tar en titt på koden vil jeg gå gjennom noen av de tingene jeg ikke liker med løsningen jeg har kommet opp med.

Merk at noen av mine erfaringer helt sikkert kan skyldes at jeg fortsatt er ganske fersk i Erlang. Har du mer kunnskap enn meg er det bare å sette meg på plass.

Tidligere har jeg jobbet endel med lister i Erlang, og til det er det veldig egnet. Da jeg nå skulle gjøre helt enkle ting som å konvertere en streng til en integer, og konkatinere to strenger, oppdaget jeg at språket ikke var like elegant på alle områder. Jeg savnet også if, og måtte ty til en bråkete switch-case når jeg skulle avgjøre om det skulle sendes en initiell ping ved oppstart (se linje 22 til 25).

TCP-grensesnittet var også mye mere komplisert (low level) enn det jeg hadde å forholde meg til i de andre språkene (linje 36 til 51). Jeg skulle for eksempel gjerne hatt en “read_to_end” funksjon; do_recv() er min variant av den. Generelt føltes det som om jeg måtte skrive mere kode enn hva som burde være nødvendig, nesten samme hva jeg holdt på med.

Det mest utfordrende var derimot å klare å kjøre programmet fra kommandolinjen. Til slutt måtte jeg legge til et par ekstra overloads av start-funksjonen får å klare å håndtere kommandolinje-parametrene riktig (linje 4 til 16).

Ok, nok klaging – her er kildekoden i sin helhet:

1 -module(ring_server).
2 -export([start/1, start/4]).
3
4 start(Args) -> 
5   % when started from command line args are collected in a list
6   [A1, A2, A3, A4] = Args,
7   start(A1, A2, A3, A4).
8
9 start(ThisPort, OtherPort, MaxDelay, InitialPing)
10 when is_list(ThisPort) -> 
11   % when started from command line some conversion is needed
12   start(
13     as_int(ThisPort),
14     as_int(OtherPort),
15     as_int(MaxDelay),
16     InitialPing);
17
18 start(ThisPort, OtherPort, MaxDelay, InitialPing) ->
19   io:format(“** Erlang Ring Server (~p)~n, [ThisPort]),
20   Message = lists:concat(["Ping from ", ThisPort]),
21   Pinger = spawn(fun() -> ping_sender(OtherPort, Message) end),
22   case InitialPing of 
23     “true” -> Pinger ! ping;
24     _ -> no_action_needed 
25   end,
26   Watcher = spawn(fun() -> ping_watcher(Pinger, MaxDelay, 0) end),
27   spawn(fun() -> ping_listener(ThisPort, Pinger, Watcher) end),
28   timer:sleep(infinity). % let the actors do the job forever
29
30 as_int(String) -> % simplified string to integer conversion
31   {Int, _Rest} = string:to_integer(String),
32   Int.
33
34 %% — Actor 1 : Ping listener —
35
36 ping_listener(ThisPort, Pinger, Watcher) ->
37   {ok, LSock} = gen_tcp:listen(ThisPort, [binary, {packet, 0}, {active, false}]),
38   ping_listener_loop(LSock, Pinger, Watcher).
39  
40 ping_listener_loop(LSock, Pinger, Watcher) ->
41   {ok, Sock} = gen_tcp:accept(LSock),
42   {ok, Bin} = do_recv(Sock, []),
43   ok = gen_tcp:close(Sock),
44   process_incoming_ping(Bin, Pinger, Watcher),
45   ping_listener_loop(LSock, Pinger, Watcher).
46  
47 do_recv(Sock, Bs) ->
48   case gen_tcp:recv(Sock, 0) of
49     {ok, B} -> do_recv(Sock, [Bs, B]);
50     {error, closed} -> {ok, list_to_binary(Bs)}
51   end.
52
53 process_incoming_ping(Message, Pinger, Watcher) ->
54   io:format(“Received ~p~n, [Message]),
55   Watcher ! ping, % tell the watcher about it
56   Pinger ! ping. % and the pinger, so he can forward it
57
58 %% — Actor 2 : Ping sender —
59
60 ping_sender(Port, Message) ->
61   receive ping ->
62       timer:sleep(1000),
63       case gen_tcp:connect(“localhost”, Port, [binary, {packet, 0}]) of
64         {ok, Sock} ->
65           ok = gen_tcp:send(Sock, Message),         
66           ok = gen_tcp:close(Sock);
67         {error, _} ->
68           io:format(“*** Failed sending ping~n)
69       end,
70       ping_sender(Port, Message) % loop to wait for more ping requests
71   end.
72
73 %% — Actor 3 : Missing pings watcher
74
75 ping_watcher(Pinger, MaxDelay, DelayCount) ->
76   receive ping -> 
77       ping_watcher(Pinger, MaxDelay, 0) % all is well, watch again
78   after MaxDelay * 1000 ->
79       NewDelayCount = DelayCount + 1,
80       io:format(“*** ALERT, RING BROKEN! No ping in ~p seconds.~n,
81         [MaxDelay * NewDelayCount]),
82       Pinger ! ping, % try to wake up ping ring
83       ping_watcher(Pinger, MaxDelay, NewDelayCount) % watch again
84   end.

Det jeg derimot følte ble veldig bra var det jeg kalte actor 2 og 3 (ping sender, linje 60, og ping watcher, linje 75). Watcheren ble spesielt elegant synes jeg. Her utnytter jeg Erlangs evne til å fyre av et event når jeg har ventet på en melding i x antall millisekunder. Jeg venter nemlig på pings på linje 76, og når ping kommer repeterer jeg bare (halerekursjon). Etter så mange sekunder som det er tillatt å gå uten pings (linje 78: after MaxDeley * 1000) aktiverer jeg derimot alarmen.

På den måten trenger jeg heller ikke å gjøre beregninger basert på klokketid, antall sekunder siden sist ping er jo MaxDelay ganger DelayCount.

Konklusjon

Actor-modellen er elegant, og passer veldig bra for denne oppgaven. Språket er derimot ikke så veldig bra å implementere i (eller eventuelt min kunnskap om det er for dårlig). Det er alt i alt lite tilfredstillende – kildekoden ser ikke fin ut, det er for mye av den, og den føles gammeldags. Jeg merker at jeg ønsker meg en fullgod implementasjon av actor model i et annet språk.

I neste runde av denne serien vil jeg bruke et språk som er enda eldre enn Erlang, men som har en syntax jeg synes er super-elegant…

Tidligere i serien: Introduksjon | Del 2 (C#) | Del 3 (Ruby) | Del 4 (Boo).

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.

Ping Ring del 4: Boo

Dette er del 4 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.

boo Denne gangen har jeg valgt et språk som kanskje er ukjent for en del utviklere, nemlig Boo. Boo er et objektorientert, statisk typet språk for Common Language Infrastructure (dvs. .NET). Syntaksen som er inspirert av Python er elegant og kompakt. Selv om koden er like statisk typet som C# så utleder Boo det meste av typene for deg, slik at det nesten ser ut som om Boo er dynamisk typet.

Igjen har jeg fulgt samme struktur som tidligere (del 2 og 3), og ettersom det samme rammeverket ligger i bunn her som i C#-varianten så er det ingen forskjeller i hvordan jeg for eksempel gjør sending og mottak med TCP. Den største forskjellen ligger i bruk av tråder: I Boo kan jeg kjøre hvilken som helst metode asynkront (i en ny tråd) ved å kalle BeginInvoke() på metoden (se linje 14 til 16, 35 og 48). Dette er ganske elegant – jeg trenger ikke tenke på asynkronitet når jeg implementerer basislogikken, og kan så velge om jeg vil kjøre parallelt eller ikke når jeg etterpå skal bruke den.

1 import System
2 import System.IO
3 import System.Net
4 import System.Net.Sockets
5 import System.Threading
6
7 class RingServer:
8   [Property(ThisPort)] _thisPort as int
9   [Property(OtherPort)] _otherPort as int
10   [Property(MaxDelay)] _maxDelay = TimeSpan(0, 0, 5) # default
11   _lastPingTime = DateTime.Now
12
13   def Start(sendStartupPing):
14     SendDelayedPing.BeginInvoke() if sendStartupPing
15     WatchForMissingPings.BeginInvoke()
16     ListenForPings.BeginInvoke()
17     Thread.Sleep(20) while true # Wait forever!
18
19   def SendDelayedPing():
20     try:
21       Thread.Sleep(1000)
22       using tcpClient = TcpClient(127.0.0.1, OtherPort):
23         using streamWriter = StreamWriter(tcpClient.GetStream()):
24           streamWriter.Write(PING from ${ThisPort})
25     except ex as Exception:
26       print(*** Failed sending ping: ${ex.Message})
27
28   def WatchForMissingPings():
29     while true:
30       Thread.Sleep(5000)
31       pingDelay = DateTime.Now – _lastPingTime
32       if pingDelay > MaxDelay:
33         print(*** ALERT, RING BROKEN! +
34             No ping in ${pingDelay.TotalSeconds} seconds.)
35         SendDelayedPing.BeginInvoke()
36
37   def ListenForPings():
38     tcpListener = TcpListener(IPAddress.Parse(127.0.0.1), ThisPort)
39     tcpListener.Start()
40     while true:
41       using connection = tcpListener.AcceptTcpClient():
42         using streamReader = StreamReader(connection.GetStream()):
43           ProcessIncomingPing(streamReader.ReadToEnd())
44
45   def ProcessIncomingPing(message):
46     _lastPingTime = DateTime.Now
47     print(Received ${message})
48     SendDelayedPing.BeginInvoke()
49
50 print(** Boo Ring Server (${argv[0]}))
51 srv = RingServer(
52     ThisPort: int.Parse(argv[0]),
53     OtherPort: int.Parse(argv[1]),
54     MaxDelay: TimeSpan(0, 0, int.Parse(argv[2])))
55 srv.Start(bool.Parse(argv[3]))

Hva synes du? For meg er dette ganske mye vakrere enn både C# og Ruby-versjonen, fordi all unødvendig støy er fjernet. Å definere blokker/metoder/klasser ved hjelp av innrykk er noe man må vende seg til, og det er kanskje også en smaksak om man liker det eller ikke. Jeg må si at jeg digger det, og synes det burde være mye mere utbredt.

Boo er generelt sett et veldig spennende språk. Det eneste som er synd er at det ikke er noen fullverdig støtte for språket i Visual Studio. Hadde det vært det hadde jeg byttet hovedspråk på dagen :)

Når det gjelder egnethet i forhold til oppgaven er det derimot ingen spesiell forskjell fra de andre variantene. Bruk av BeginInvoke() er interessant, men ikke nødvendigvis bedre (i dette tilfellet i alle fall). Det var dog på høy tid at jeg fikk vist litt Boo-kode på bloggen.

Jakten på det beste språket fortsetter, 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.


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 !...

Bjørn Einar Bjartnes: Jeg har også latt meg fascinere av Clojure, uten at jeg har kommet så veldig l...

Bjørn Einar Bjartnes: Sweet :) Jeg tror egentlig jeg liker det som det er, med musikk. Litt av utford...

 Hold deg oppdatert

Søk i bloggen

Ferske innlegg

  • Template Method del 4: Multippel arv
  • Template Method Intermesso
  • Template Method del 3: Bare funksjoner
  • Template Method del 2: På vei mot funksjonell programmering
  • 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 (20)
  • 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