Il gruppo PUG MoRe si riunisce (solitamente l'ultimo sabato di) ogni mese alle 14.00 qui; vi invitiamo a seguire i nostri aggiornamenti in mailing list, su questa pagina e su Twitter.

PHP User Group di Modena e Reggio Emilia

Prossimo incontro: sabato 30 maggio

Il nostro prossimo incontro si terrà sabato 30 maggio alle 14.00.

Reduci dal recente phpDay abbiamo deciso di sperimentare una formula nuova legata al TDD e al code review.

Se siete curiosi di saperne di più non mancate!

Posted in Eventi | Tagged , , , | Leave a comment

Quando c’e` comunicazione c’e` tutto

Il buon Roberto (a patto di lasciarlo in pace) se ne e` uscito alla grande con NetRobots, che e` un server in Python che simula una arena di combattimento, fra robot che dialogano attraverso messaggi in formato JSON, inviati su protocollo HTTP.

JSON e HTTP sono stati scelti per permettere a robot scritti in diversi linguaggi (un must per la natura eterogena del gruppo), di poter dialogare con il server. La sequenza di eventi e`:

  • un robot chiede al server di eseguire un comando, tipo “fai lo scan nella direzione 35, con ampiezza 90″, inviando il comando in JSON -> HTTP -> Socket
  • il server HTTP riceve il comando, lo esegue nell’arena di gioco virtuale aggiornando lo stato, e invia una risposta al robot tipo “c’e` un robot nemico alla distanza 20″, sempre in formato JSON -> HTTP -> Socket

Il server HTTP e` stato scritto in Flask, che e` un server HTTP minimale (nel senso buono del termine), che da` ampio controllo al programmatore. Il codice risultante e` compatto e leggibile, in perfetto stile Python. Per esempio questo e` il codice che riceve una richiesta JSON di scanner da un robot e risponde:

mod_robot.route('/<token>/scan', methods=['PUT'])
@check_token
def scan(robot):
    degree = int(float(request.form['degree']))
    resolution = int(float(request.form['resolution']))
    assert isinstance(robot, Robot)
    time.sleep(app.app.game_board_th.get_sleep_time())
    ret = robot.scan(degree, resolution)

    if ret is not None:
        resp = Response(response=json.dumps({'status': 'OK', 'distance': ret}),
                        status=200,
                        mimetype="application/json")
        return resp

    resp = Response(response=json.dumps({'status': 'KO', 'distance': None}),
                    status=406,
                    mimetype="application/json")
    return resp

Il problema di Flask, e che e` pensato (giustamente) per servire richieste HTTP in ordine FIFO, ma i robot dall’altra parte essendo in battaglia e in perenne pericolo di vita o di morte, non sono molto dell’idea di inviare educate richieste aspettando il loro turno, e sono in continuo bombardamento di messaggi HTTP.

Ogni thread Flask riceve la sua richiesta HTTP e se fosse fair, risponderebbe ai Robot dividendo i turni di gioco in intervalli regolari, per dare modo ad ogni robot, anche a quelli scritti in VisualBasic, di elaborare la contromossa. Inoltre le latenze della rete non sono note a priori e senza arrivare agli estremi del gioco per corrispondenza, sarebbe fair dare modo anche ai robot installati in zone colpite dal digital-divide, di poter dire la loro. Ci sono stati casi di Robot che tempo di inviare un messaggio di benvenuto al server, e una cartolina a casa dicendo che stavano bene ed erano pronti alla sfida, gia` erano stati scannerizzati e bombardati un migliaio di volte e il datagram TCP di risposta aveva trovato solo un profondo pozzo nero.

Ma in Flask non e` facile mettere in sospensione le risposte, o ordinarle per turno. Anche rispondendo con un messaggio tipo “non e` il tuo turno”, si rischia comunque di avere un poll continuo di richieste dal robot.

Ovviamente con un po’ di (in)sano hacking e` sicuramente possibile trovare una soluzione piu` o meno elegante per Flask, ma noi siamo il Team PUG MoRe e ci meritiamo di piu` :-)

ZeroMQ e` una libreria multilinguaggio (esistono versioni per quasi qualunque linguaggio di programmazione) che permette di scrivere facilmente un server che accetta comandi su connessioni in formato ZeroMQ, e inviare risposte ai robot scritti in altri linguaggi (un requirements del progetto NetRobots), ma che usano sempre la stessa libreria.

ZeroMQ puo` essere vista come una versione elegante (a livello di API) dei socket Linux. Le prestazioni sono di primissimo livello, tante` che e` stata scelta dal CERN per processare i dati dei suoi esperimenti scientifici, che non sono notoriamente parchi di banda o fiacchi nelle latenze, a meno che non si possano considerare le particelle sub-atomiche poche, lente e prevedibili :-)

Uno dei punti di forza di ZeroMQ e` che scala dal piccolo (due thread nello stesso processo che si scambiano messaggi), al medio (due processi sullo stesso host che si scambiano messaggi), al grande (due processi su host diversi che si scambiano messaggi). Per ogni situazione ZeroMQ usa una versione ad-hoc del protocollo cercando di minimizzare (o portare a 0) l’overhead dell’invio e ricezione del messaggio, ma con il vantaggio di avere una sola API di processing all’interno del programma. Quindi uno puo` scalare una applicazione, lasciando invariata la logica interna (la API e` sempre la stessa) ma cambiando solo la configurazione di deploy, facendo diventare ad esempio un thread un processo su una macchina esterna.

ZeroMQ puo` essere usata anche per gestire situazioni di multi-threading, dato che la logica a scambio di messaggi e` piu` intuitiva da capire rispetto a semafori o simili e allo stesso tempo ZeroMQ non e` molto piu` lenta di una chiamata di funzione, se usata all’interno dello stesso processo, dove basta passare un puntatore al messaggio da elaborare.

ZeroMQ si pone ad un livello piu` basso rispetto a Flask, dato che e` a livello di libreria di comunicazione (tipo Socket) e non di application-server. Pero`:

  • questo da` piu` liberta` di controllo, su come e quando ricevere, mettere in hold e inviare le risposte
  • la API molto elegante, non fa sentire troppo la mancanza di un prodotto piu` high-level come Flask

La filosofia di ZeroMQ per scrivere una applicazione e` questa:

  • io (ZeroMQ) ti do`:
    • una serie di protocolli di comunicazione
    • una serie di metodi di comunicazione fra socket (direct, publish-subscribe, broadcasting, etc…)
    • la certezza che qualunque combinazione (lecita) di protocollo e metodo di comunicazione, te la supporto in modo che piu` efficiente e robusto non si puo`
  • tu (programmatore) puoi:
    • combinare i protocolli e i metodi di comunicazione nel modo che preferisci (una esplosione combinatoria), per creare la piattaforma di messagging ad-hoc che ti serve
    • vedere l’applicazione come una serie di socket tra loro collegati, su cui leggono e scrivono processi consumer e producer
    • scalare l’applicazione facilmente

Al contrario, un prodotto di messagging completo e omnicomprensivo come RabbitMQ, tende ad essere da una parte completo (logging, persistenza dei messaggi, etc..), ma dall’altra ristretto alle funzionalita` che ha lui per gestire i messaggi. La sua filosofia e` questa:

  • queste sono le opzioni tipiche che ti offro per scambiare i messaggi
  • quasi di sicuro, sei in una di queste situazioni tipiche e quindi hai tutto gia` pronto
  • se non sei in una di queste situazioni, sono cavoli tuoi (che e` l’effetto Flask)

Evidentemente il CERN non era in una delle loro situazioni tipiche :-)

I robot si connettono tramite ZeroMQ al server

self.get_game_server_socket().send(c.SerializeToString())
status = RobotStatus()
status.ParseFromString(self.get_game_server_socket().recv())

e inviano una sequenza di:

  • send a command
  • recv the answer

Riceveranno la risposta solo quando e` il loro turno. Quindi il “recv” li mette in attesa della risposta (tipica pattern ZeroMQ dove il receive fa anche da synchro). Tutto il resto del codice e` per serializzare e deserializzare un messaggio usando Google ProtoBuf che e` una libreria multi linguaggio per la serializzazione/deserializzazione binaria efficiente dei messaggi. Quindi ProtoBuf prende il posto di JSON e ZeroMQ di HTTP.

Il server NetRobots e` decisamente piu` interessante:

  • apre un socket ZeroMQ esterno, da cui riceve le richieste dei robot
  • si mette in sleep una qualche frazione di secondo, in attesa dell’inizio del nuovo turno di gioco
  • nel mentre le richieste dei robot si accodano. ZeroMQ e` una libreria poco schizzinosa e ci pensa lei ad accodare lato server o nei casi piu` critici mettendo in hold i client
  • quando e` il momento del nuovo turno di simulazione, inizia a gestire le richieste dei clients
  • ZeroMQ e` multi threading e non e` un problema estrarre messaggi dalla coda (consumer), mentre altri thread spingono messaggi nella stessa coda (producer)

Il codice non e` di immediata comprensione dato che implementa una politica fair e tenta di evitare che uno stesso robot invii due richieste, senza aspettare la risposta, cercando di giocare quindi due o piu` volte nello stesso turno di gioco.

def process_robots_requests(self, client_socket):
    self._board.tick(self._deltatime)

    self.processed_robots.clear()

    # First process queued robots.
    queue = self.queued_robot_messages
    self.queued_robot_messages = {}
    # DEV-NOTE: these instructions are very important:
    # * the code must work assuming there are no any more queued robots
    # * a copy of the reference is maintaned for processing them

    for token, v in queue.iteritems():
        try:
            self.process_robot_command(v['command'], v['sender_address'], client_socket)
            self.processed_robots[token] = True
        except:
            self.debug_message("Unexpected error " + str(sys.exc_info()[0]))
            client_socket.send_multipart([v['sender_address'], b'', b''])
            # in ZMQ is mandatory sending an answer

    # Update the robots according the new requests from clients.
    again = True
    while again:
        sender_address = None
        try:
            sender_address, empty, binary_command = client_socket.recv_multipart(zmq.NOBLOCK)
            command = MainCommand()
            command.ParseFromString(binary_command)
            self.process_robot_command(command, sender_address, client_socket)

        except zmq.error.Again:
            # there are no more messages to process in the queue
            again = False

        except:
            # there is an error processing the command for this client.
            self.debug_message("Unexpected error " + str(sys.exc_info()[0]))
            if sender_address is not None:
                client_socket.send_multipart([sender_address, b'', b''])
                # in ZMQ is mandatory sending an answer


def process_robot_command(self, command, sender_address, client_socket):
    if command.HasField('createRobot'):
        self.process_create_robot_request(command.createRobot, sender_address, client_socket)

    elif command.HasField('deleteRobot'):
        self.process_delete_robot_request(command.deleteRobot, sender_address, client_socket)

    elif command.HasField('robotCommand'):
        token = command.robotCommand.token

        if token in self.banned_robots:
            # nothing to do, avoid to answer to these requests
            pass
        elif token in self.queued_robot_messages:
                self.banned_robots[token] = True
                robot = self._board.get_robot_by_token(token)
                self.debug_message("Banned " + token + ". Board time is " + str(self._board.global_time()) + ", robot time is " + str(robot.last_command_executed_at_global_time))
        else:
            if token in self.processed_robots:
                self.queued_robot_messages[token] = {'sender_address': sender_address, 'command': command }
                robot = self._board.get_robot_by_token(token)
            else:
                self.processed_robots[token] = True
                self.process_robot_request(command.robotCommand, sender_address, client_socket)

In questo codice la pattern di utilizzo delle ZeroMQ e` sinceramente un po’ astruso perche` e` stato utilizzato questo codice

sender_address, empty, binary_command = client_socket.recv_multipart(zmq.NOBLOCK)

che separa l’indirizzo del sender e il contenuto del messaggio ricevuto da un client Robot. Una volta processato il comando, la risposta viene inviata al client, usando

client_socket.send_multipart([sender_address, b'', status.SerializeToString()])

che in forma speculare invia il contenuto, rimettendo dentro al messaggio, l’indirizzo ricevente precedentemente estratto.

A differenza del codice Flask, in ZeroMQ i messaggi sono self-class-citzien, che possono essere manipolati esplicitamente:

  • separare contenuto di un messaggio, da indirizzo del sender
  • spostare un messaggio da un socket all’altro, anche di tipo diverso
  • etc.. etc..

La mia impressione finale e` che se avessi usato RabbitMQ avrei fatto prima:

  • non c’era bisogno di usare ProtoBuf
  • le pattern di utilizzo erano gia` codificate, e nel 90% dei casi vanno gia` bene per quello che uno vuole fare.
  • la curva di apprendimento e` minore

Pero` usare ZeroMQ e` stato veramente divertente:

  • dopo un po’ uno realizza che le combinazioni con cui usare e comporre la libreria sono infinite come il gioco dei Lego: ci puoi fare il castello, o l’aereo o …
  • e` un po’ la filosofia di Linux e delle pipes, dove uno puo` collegare ad un componente altri componenti, e cosi` via all’infinito
  • uno magari ci mette piu` tempo la prima volta a capire la filosofia della libreria, ma dopo ha in mano uno strumento veramente molto potente e versatile
  • con RabbitMQ avrei dovuto avere probabilmente un server separato, per la gestione dei messaggi, oltre al server Python. Un po’ come quando uno deve far girare un server MySQL insieme alla sua applicazione per avere le funzionalita` di database.. Le ZeroMQ sono come Sqlite, uno usa la libreria all’interno del server Python gia` scritto, e le funzionalita` che la fanno diventare una applicazione di messaging evoluta sono gia` tutte incluse nella libreria incorporata.

Inutile dire che mi aspetto che nel mondo commerciale prenderanno sempre piu` piede prodotti come RabbitMQ, dato che hanno la filosofia “tutto incluso”, vincente come tempi di apprendimento e utilizzo, rispetto al “tutto e` possibile” di ZeroMQ. Pero` lato divertimento la filosofia “tutto e` possibile” e` impagabile… :-)

Posted in code kata, progetti condivisi | Leave a comment

Power GO contro il povero Haskell

Non specifico i dettagli dell’esercizio, dando per scontato che c’eravate o abbiate letto i due articoli precedenti.

Haskell usato come lo uso io (senza lenses) e` particolarmente verboso nella specifica del cambiamento di stato di un oggetto, cosa che e` invece alla base di un linguaggio imperativo. Qua per esempio sto togliendo health ad un giocatore dell’arena

   attacked2 
     = attacked { combatant_health 
                    = max 0 ((combatant_health attacked) - attackStrenght) 
                }

Questa linea dice:

  • il nuovo oggetto “attacked2″, e` come l’oggetto “attacked” iniziale (di cui non posso cambiare niente, dato che in Haskell non posso modificare il valore di una variabile), tranne che ha il campo “health” diverso
  • il campo health e` uguale al massimo fra 0 (quindi non puo` diventare mai negativo) e la salute iniziale (“combatant_health attacked”) meno la potenza dell’attacco

In un linguaggio imperativo, sarebbe stato tutto molto piu` immediato e leggibile, tipo:

attacked.health -= attackStrenght;
if attacked.health < 0 then attacked.health = 0;

Quindi GO segna il primo GOL e si porta sul 1-0.

In Haskell pero` il codice e` piu` componibile, dato che essendoci meno side-effects ci sono anche meno sorprese quando si iniziano ad incastrare mattoncini su mattoncini. Coi pero` non si vincono le partite e quindi siamo ancora 1-0 per GO e il suo team.

Questo codice decide cosa fare, quando il giocatore “attacker” si scontra contro “attacked”

attack attacker attacked moves1 
  = case attacker == attacked of
      True
        -> moves1
           -- non fa niente e torna le mosse di prima
      False
        -> case combatant_isDead attacker of
             True
               -> (DeadMove attacker):moves1
                  -- un giocatore morto non puo` attaccare
             False
                -> case (mod tick (combatant_attackSpeed attacker) == 0) of
                    False
                      -> moves1
                         -- non e` ancora il turno di attacker e non puo` fare mosse
                    True
                      -> case combatant_isDead attacked of
                           True -> moves1
                                   -- un giocatore morto non puo` essere attaccato
                           False -> (AttackMove (combatant_name attacker) 
                                                (combatant_name attacked)
                                    ):moves1
                                   -- aggiunge una mossa di attacco alla lista di mosse fatte

In questo caso il codice funzionale ricorda un po’ quelle dimostrazioni matematiche in cui si analizzano le diverse path possibili, e terminano con un “come volevasi dimostrare”. E` elegante, ma di certo non posso segnare nessun gol a favore, dato che sono semplici azioni di contenimento a centro campo.

In Haskell si applica una mossa all’arena in questo modo:

arena_move :: Arena -> Move -> Arena 

cioe` si definisce una funzione che accetta un’arena iniziale, la mossa da applicare, e torna la nuova arena con la mossa applicata. Siccome non si puo’ cambiare il valore di una variabile, non si puo` che tornare un nuovo ogetto.

Perlomeno l’applicazione di piu` mosse ad una arena e` compatta come codice:

arena_moves :: Arena -> [Move] -> Arena
arena_moves arena1 moves = foldl' arena_move arena1 moves

e iniziamo a capire perche` le funzioni si compongono velocemente in Haskell. La “foldl” applica iterativamente una funzione di move all’arena precedente, passando come parametro la move successiva presa nella lista di mosse da applicare. Nel caso di due giocatori vive sono solo 2 mosse, o nessuna se sono tutti e due morti o non e` il loro turno, o piu` mosse se si stanno scontrando diversi giocatori.

I parser in Haskell si scrivono velocemente, dato che una delle librerie di Haskell (Parsec) definisce un interprete di un Domain Specific Language, adatto per descrivere i parser. E` tipico in Haskell descrivere computazioni complesse, usando una notazione ad-hoc (Domain Specific Language) interpretata da una libreria. Quindi Haskell da un linguaggio verboso, diventa un linguaggio compatto e ad-hoc per risolvere il problema di turno. Quello che in un linguaggio OO diventa una pattern (una serie di ogetti organizzati in un modo ricorrente), in Haskell diventa un interprete, e una rappresentazione piu` compatta del codice, usando un Domain Specific Language. Poi nella realta` gli interpreti hanno i loro limiti: meglio un aproccio a compilatore, dove e` possibile fare una pre-analisi del tutto, per esempio e` difficile in Haskell dire se la grammatica contiene errori di definizione o ricorsioni infinite, cose che invece riesce a fare un parser compilato con Bison/YACC & C.

parseLine :: Parser Combatant
parseLine = do 
   cn <- parseString
   sep
   speed <- parseInt
   sep
   health <- parseInt
   sep
   wn <- parseString
   sep
   wStrenght <- parseWeaponStrenght

   let w = Weapon {
             weapon_name = wn
           , weapon_strength = wStrenght
           }

   let c = Combatant {
             combatant_name = cn
           , combatant_attackSpeed = speed
           , combatant_health = health
           , combatant_weapon = w
           , combatant_order = 0          
           }

   return c

Qua` e` chiaramente un gol a favore di Haskell, ma peccato che sia stato annullato dall’arbitro per fuori gioco, dato che il team GO ha saggiamente optato per usare direttamente una libreria CSV del linguaggio, invece che scriversi il proprio parser.

Questo e` il codice per fare il parsing delle armi con potenza casuale all’interno di un range

parseWeaponStrenght :: Parser (Int, Int)
parseWeaponStrenght
  = P.try (do strictSpace
              P.char '('
              strictSpace
              i1 <- parseInt
              P.char ','
              strictSpace
              i2 <- parseInt
              P.char ')'
              strictSpace
            return (i1, i2)) <|> (do i <- parseInt
                                     return (i, i))

riconosce stringhe come “1” oppure “(1, 2)”.

Il simbolo “<|>” usato nel codice sopra dice: se il primo parser non e` riuscito a fare match, allora prova con questa alternativa. Il tutto codificato come libreria del linguaggio, non come built-in. Notevole direi. Quindi 1-1.

Ma purtroppo questo e` stato l’ultimo goal del team Haskell e segnato al limite del tempo regolamentare, quando oramai il team GO aveva dilagato segnando diversi goal e completando tutti gli esercizi del incremental Kata, e per di piu` giocando fuori casa, in completa discomfort zone, con un linguaggio nuovo sotto tutti i punti di vista. Praticamente la squadra primavera del GO ha annientato il blasonato ma viziato team Haskell.

L’allenatore del team Haskell ha provato a protestare affermando che il codice di Haskell supportava RPG fra 3 o piu` giocatori, e non solo 2, ma l’arbitro ha fatto notare che mai nel corso del Incremental Kata era stato chiesto questo e ha quindi ammonito il team Haskell, per mancanza di rispetto delle regole del TDD.

Il mio giudizio su Haskell: il linguaggio del futuro sara` un linguaggio con molte idee prese dai linguaggi funzionali (come gia` accade, vedi i Nullable types), una semantica/sintassi piu` ad oggetti tipo “subject.property” e la gestione dei domain-specific-languages attraveros una fase di analisi/compilazione, invece che di interpretazione. Quindi non sara` Haskell cosi` come e` ora a mio parere. Pero` molte delle idee dei linguaggi funzionali stanno diventando main-stream nei linguaggi imperativi tradizionali.

Ma voglio terminare “fantasticando” su cosa poteva fare l’accoppiata Roberto / Alessandro / Python: una manciata di chiamate di libreria, due piccole funzioni come collante e il pitone con le batterie termonucleari incluse, avrebbe strangolato nella sua morsa il povero Haskell ancor prima del fischio di inizio :-)

Posted in code kata | 3 Comments

Go Go Go PowerRanger

Spoiler Alert: questo articolo parla dello scorso kata incrementale (RPG Inc) e coloro i quali vogliano farlo sono pregati di non leggere questo articolo fino a che non lo abbiano terminato.

Quindi… in seguito al kata dello scorso incontro ci siamo imbattuti in GO, un linguaggio fuori dal tempo.

Analizziamo la soluzione appunto in GO, partendo dalla stesura funzionante, ma non elegante (in fondo abbiamo visto il linguaggio per la prima volta appunto all’incontro del PUG):

Questo approccio parsa le righe del file come se fossero le righe di un CSV, anche se non lo sono propriamente, poi computa le due eccezioni possibili; le armi con il tipo e il danno con il range. Lo fanno controllando se il valore estratto dal csv inizia per parentesi tonda aperta [(]. In tal caso shifta di 1 gli indici a cui prenderà le entità dopo e trimma le parentesi chiuse di troppo. Questo per 2 volte. Ci eravamo ripromessi di refattorizzare il tutto in caso di una terza eccezione, perché il codice era arrivato al limite della manutenibilità

Letti i dati li abbiamo messi in una struct (non ne vedevo da anni), senza strutturare troppo questo contenitore di informazione. Anche qui ci eravamo ripromessi di strutturarlo di più appena la complessità si fosse alzata anche solo di poco, ma il kata è terminato prima :)

La simulazione del combattimento vera e propria è stata la parte più semplice. Un ciclo fino alla morte di uno o entrambi i giocatori. La stampa dello stato durante il combattimento è risultata un po’ insidiosa, perché i punti ferita stampati sono sempre nell’ordine giocatore1;giocatore2, mentre subito li avevamo stampati nell’ordine giocatore-attaccante;giocatore-attaccato.

Qui il sorgente

Sull’onda della curiosità per questo linguaggio ci siamo chiesti come scrivere “bene” questo codice così confuso. Abbiamo quindi ottenuto il seguente codice per il nostro main.go

package main

import (
 "rpgkata"
 "os"
)

func main() {
 filename := "combat.rpg"
 combat_stats := rpgkata.ReadFile(filename)
 players := rpgkata.Decode(combat_stats)
 os.Exit(rpgkata.Run(players))
}

Inoltre restituiamo un codice di uscita in base a chi ha vinto (o in caso di pareggio).

Abbiamo create 3 funzioni nel package rpgkata; ReadFile, Decode e Run

Abbiamo affinato la ReadFile, permettendole di gestire qualunque profondità di parentesi tonde annidate; abbiamo ottenuto di conseguenza una Decode più facile da scrivere e manutenere. Per la Run i cambamenti sono stati minimi, in quanto già partiva dall’oggetto Player che non abbiamo toccato.

Possibili evoluzioni anche qui sarebbero quelle alla struttura dati del giocatore e della sua arma, creando una struct per l’arma e mettendo nella struct del Player un puntatore all’arma usata. Ma abbiamo preferito provare a fare i test.

Ecco intanto i 3 file con le 3 funzioni.

http://pastebin.com/KwzJ6hCw
http://pastebin.com/ktAtkVSM
http://pastebin.com/ExUw9su7

Test.

Leggendo la documentazione di GO, abbiamo creato un primo test per la funzione ReadLine del file reader.go

http://pastebin.com/Fggk9xRh

Il test è volutamente semplice perché è un primo approccio a Go; abbiamo preso in prestito un paio di funzioni per determinare l’uguaglianza di oggetti, che fanno uso di reflection, dal magico mondo di internet. Il framework di test ha le funzionalità anche per i test paralleli, ma non ha gli assert di base che ci si aspetterebbe venendo da PHP o Python.

Alla fine il test parsa una stringa e asserisce che l’oggetto risultante sia uguale a quello creato nel test.

Soooo…… GOooooo ?

Quindi GO come linguaggio del futuro? No. È palesemente un linguaggio per chi deve scrivere codice che deve essere compilabile e di veloce esecuzione. Quindi non ci sembra un linguaggio di prototyping (come può essere Python), ma un linguaggio per software di produzione soggetti a pesante carico, con pero’ il vantaggio di potere essere molto più pulito da leggere di C o C++.

Posted in Uncategorized | Leave a comment

Resoconto incontro 09 Maggio

In questo incontro abbiamo ripreso il tema dei kata incrementali e ci siamo messi in uncomfort zone! I linguaggi che i diversi team hanno scelto sono stati: GO, Java, PHP e Haskell.

IMG_20150509_185916 IMG_20150509_185933

Il kata verteva sullo sviluppo incrementale del gioco di ruolo che trovate qui.

Il kata si prestava abbastanza allo sviluppo test driven infatti alcuni hanno adottato questa pratica.

IMG_20150509_185857

La new entry è stato GO: un linguaggio che sembra essere tornato con una DeLorean dagli anni ’80, stile C, ma con molte librerie ed effettivamente molto più veloce da apprendere; è bastata una mezz’ora per ambientarsi.

La principale difficoltà del kata è stata rappresentata dal parsing del formato di input che nel corso degli step variava continuamente. Automi a stai finiti? Regex? Abbiamo affilato le armi e scelto diversi approcci interessanti.

Posted in Eventi | Tagged , , , , , , , | Leave a comment