Ça y'est, c'est officiel, les inscriptions à la conférence Agile Open France 2009 sont désormais ouvertes. Bien évidemment, je me suis précipité sur le formulaire d'inscription dés réception de l'annonce. J'ai eu la chance de participer à l'édition 2008 et je dois dire que j'en suis revenu enthousiasmé par le format Forum Ouvert (ou Open Space) qui, à mon sens, est la meilleure manière de produire un tel événement.
J'aurais voulu utiliser ce format pour Agile Tour Lille 2008 mais celui-ci suppose que les participants aient déjà une idée de ce qu'ils viennent trouver et apporter, ce qui n'était pas nécessairement le cas du public de l'Agile Tour.
La deuxième journée des Valtech Days était aussi organisée selon ce modèle et je dois dire que c'est la partie que j'ai le plus appréciée.
J'attends avec impatience le mois de janvier pour être de nouveau surpris.
I am struggling to improve my Muse parsing software, a project that I have been working on for couple of years now and which I use to publish this blog and the whole OQube's site. It is based on the Emacs Muse wiki format which means that I can operate within the context of Emacs with all the useful tools and keyboard shortcuts I find there, and be able to serve muse content over the web in various backend formats, HTML and RSS feeds being the most useful.
As all software, especially software that is used and maintained by a single person, it is made of layers of code injected haphazardly from urgent needs or nigthly ideas.
There are some basic flaws in the design of the key elements of this software that are now impediments for further evolutions:
ContentHandler interface from SAX which means events are not handled uniformly, This means that the time spent implementing new features is raising. I am trying to implement a simple slide formatter, something that I could use to produces slide-shows for talks, including code samples for online execution and examples in Javascript, something along the line of what John Resig published on his site
To implement this, I need one of the following but both are hards to implement using the current state of my muse parser:
div elements with ids from each hX element and its siblings, up to the following hX element, I have already had 3 false starts trying to implement this, which is a sure sign that something gets wrong: either me or the software is becoming very reluctant to change.
Aujourd'hui au dojo, nous avons fait en randori du refactoring d'un bout de code Java (voir cette page). Nous avons eu le plaisir d'accueillir de nouveaux venus dont certains que je connaissais par ailleurs (Michael et Vincent, que j'ai rencontré dans le groupe PYLULE), d'autres que je ne connaissais pas. Il semblerait que l'Agile Tour ait porté ses fruits et intéressé des gens à la pratique de l'agilité.
Le code de départ est le suivant:
/** * Split a signature string into its constituent parts and fills the list * argument. * * @param sig * java standard signature * @param ret * a List where parsed arguments will be stored. May be null. * @return a List<String> of arguments' types plus return type. The strings * correspond to the standard internal encoding of the JVM. */ public static List parseSignature(String sig, List ret) { if (ret == null) ret = new ArrayList(); int i = 0; int j = 0; int ln = sig.length(); StringBuffer arr = new StringBuffer(); parse: while (i < ln) { switch (sig.charAt(i)) { case '(': i++; break; case 'L': j = sig.indexOf(';', i); arr.append(sig.substring(i, j + 1)); ret.add(arr.toString()); arr.delete(0, arr.length()); i = j + 1; break; case ')': i++; break; case '[': i++; arr.append("["); break; default: arr.append(sig.charAt(i)); ret.add(arr.toString()); arr.delete(0, arr.length()); i++; } } return ret; }
Nous n'avons pas achevé le remaniement du code, l'objectif initialement fixé étant de restructer le code pour le rendre plus lisible et maintenable tout en gardant le comportement identique. La motivation est d'envisager une évolution future de code, par exemple une modification de signature pour rendre explicite des conditions d'exception, ou une suppression de paramètres du fait du non-respect du Principe de Responsabilité Unique
La stratégie adoptée a consisté:
NullPointerException mais qui pourra évoluer par la suite; switch interne, ce qui nous permettra d'extraire ces différents cas particuliers dans des fonctions/objets connexes et donc de simplifier le code. Dans le cas de cette fonction, il est clair que l'on est en présence d'un parseur donc que l'utilisation d'expressions régulières ou d'autres mécanismes d'analyse syntaxique serait pertinents et rendrait le code beaucoup plus explicite.
Aucune vérification de correction n'est faite sur les constituants de la signature, par exemple sur le nom d'une signature représentant une classe ou sur les différents codes possibles pour les types primitifs. Cette fonctionnalité pourrait être facilement ajoutée une fois l'analyse syntaxique correctement réalisée.
This is an interesting idiom found in the paper on reactive programming in Haskell, that constructs new types using layers of type constructors, each layer providing additional properties through instances of type classes:
It is used to define an improved time type with some useful properties to work on timed-streams:
previoustype FTime = Max (AddBounds (Improving Time)) newtype Max a = Max a deriving (Eq, Ord , Bounded ) instance (Ord a, Bounded a) => Monoid (Max a) where mempty = Max minBound Max a `mplus` Max b = Max (a `max` b) data AddBounds a = MinBound | NoBound a | MaxBound deriving Eq instance Bounded (AddBounds a) where minBound = MinBound maxBound = MaxBound -- An improving value. Invariant: -- compare I iv compare (exact iv ) data Improving a = Imp{exact :: a, compare I :: a -> Ordering } compare :: Ord a => Improving a -> a -> Ordering exactly :: Ord a => a -> Improving a exactly a = Imp a (compare a)
list of topics:
voting: -> Arguments to convincing
What's worked ?
cf Guitart, "l'envie de s'y mettre"
Paul Graham in Hackers and Painters: "Language matters, and LISP is the best one because of macros/meta-programming and its sheer rarity."
Session présentée par Julien Delhomme, consultant chez Valtech, sur les différents modèles de concurrence et l'avenir de ce type de programmation pour le commun des mortels. Plusieurs modèles ont été présentés:
Kata présenté par Emmanuel Gaillot et Etienne Charignon, sur le théme du Mastermind. Excellente préparation, avec un bon déroulé du problème et un gros effort d'explication de la part du copilote et du pilote. La solution obtenue est effectivement assez élégante, encore que l'on aurait pût - dût ? - remanier le code plus tôt de manière à introduire les abstractions objets plus facilement (eg. le comptage des jetons bien et mal placés).
Questions intéressantes sur:
I have (nearly) the skeleton of a working web application in Haskell, using FastCGI interface, storing session in a map keyed by some integer value, and allowing users to login, register, logout... The UI is pure HTML with javascript based on jQuery and its rich API: data is requested from the server that is supposed to return javascript or JSON objects, which will be used by the client side to update the UI. This last part is still missing, but I am feeling I have reached, once again, some dead end.
The structure of the application is embedded within the services it exposed, meaning that, for example, that register updates the session to allow login action, which in turns update the session to allow logout and unregister, etc. As those functions work on with a database, this implies that to both work with I/O and with the state of the application, they must work within a StateT monad transformer parameterized with IO monad. We can foresee that composing additional behavior will entail more and more monad stacking for eg. logging, error tracking.
What we really is, as usual, decouple the various components and layers we are working with so that each function serves one and only one purpose and that the top-level application orchestrates them. At this stage, without thinking about additional monadic behaviours, we would like to distinguish at least two layers:
Being a very flexible language, Haskell should allow us to express composition of fucntions and behaviours externally, without the individual elements ever noticing they are part of some composite application. What is done with XML configuration files in Spring-like frameworks should be done in Haskell within the Haskell framework.
This idea I have already implemented in Java while developing the M-Commerce framework, which used an underlying finite-state machine to orchestrate access to service and the Pico container framework for dependency injection. DI within Haskell can be done:
Sujet
rake journalisé et versionné While researching a way to represent pattern-matching easily within the My Language is Bigger than Yours boardgame, I came upon a book chapter on google-books that precisely defines a program transformation for eliminating pattern-matching and recursive data-types definitions from Haskell progras, thus making them composed only of functions and primitive constants.
The key to this transformation is to observe that any datatype can be equivalently transformed, using what is called Church-encoding into a set of constructor and desctructor functions. This I have already shown in a preceding article (see this article ). Given the following data-type for binary trees:
previousdata Tree a = Leaf a | Node a (Tree a) (Tree a)
we can define the constructor functions: previous
type Tree a = ((a -> c) -> (a -> t -> t -> c) -> c) Leaf a = (\l n -> l a) Node a t t' = (\l n -> n a t t')
If we want to know the value stored at a tree, we can either write, using pattern-matching: previous
value Node x = x value Leaf x _ _ = xor, using constructor functions: previous
value t = t (\a -> a) (\a _ _ -> a)
The key insight to this transformation is to notice that:
So we can hopefully generalize this transformation to any datatype: given a type defined as a sum of products as: previous
data Type = Ctor1 p11 p12 ... p1n | Ctor2 p21 ... p2m | ... | Ctork pk1 ... pkl
we can define the type Type and a set of constructor functions ctor1, ..., ctorl defined as: previous
type Type = (t11 -> ... -> t1n -> c) -> (t21 -> ... -> t2m -> c) -> ... -> (tk1 -> ... -> tkl -> c) -> c ctor1 v11 ... v1n = (\ f1 f2 .... fk -> f1 v11 ... v1n) ctor2 v21 ... v2m = (\ f1 f2 .... fk -> f2 v21 ... v2m) ... ctork vk1 ... vkl = (\ f1 f2 .... fk -> f2 vk1 ... vkl)and then each function operating on
Type that wants to pattern-match against its instances should be implemented by passing the specialized function to the given instance of Type that it is passed.
This can be generalized to n-ary pattern matching in the obvious way, ie. by currying.
It took me quite a few hours to implement a feature that, at first sight, appears quite simple: Extract and display a single entry from a log file as an HTML page. This feature is meant to be used as the target of links from RSS feeds. Given the unexpected difficulty of the task, and the somewhat contorted solution I found, I felt it would be worthwhile to provide details of the problem, the various paths I took and the (partial) solution I found.
I am developing a set of Java components based on the Emacs Muse publishing format, a simple wiki-like text format that I discovered a few years ago while using EmacsPlanner tool. Among other things, this blog and the whole OQube's site are written in Emacs Muse and published on the web using a dedicated web application.
This system is composed of, among others, the following components:
The Feed backend produces a RSS 2.0 formatted news feed from a muse file: Each level one title is considered a news item if the title contains a date formatted as YYYYMMDD. In the RSS 2.0 format, each entry is identified with a supposedly unique URI, which is formed by transforming the date to YYYY/MM/DD format and suffixing it with the title from which all non-ascii characters have been escaped (I should use the standard urlEscape() function).
For example, this news item is transformed to:
<?xml version="1.0" encoding="UTF-8"?> <rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0"> <channel> <title>OQube's Blog</title> <link /> <description>Random things I am working on...</description> <dc:creator>Arnaud Bailly</dc:creator> <item> <title>Filtering sections from Muse RSS Feed</title> <description>20081018: Filtering sections from Muse RSS Feed <h2>The context </h2> <p>I am developing a set of <em>Java</em> <pubDate>Fri, 17 Oct 2008 22:00:00 GMT</pubDate> <guid isPermaLink="false">http://localhost:4444/journal/journal/2008/10/18/Filtering-sections-from-Muse-RSS-Feed</guid> <dc:date>2008-10-17T22:00:00Z</dc:date> </item> ...
The problem lies in the <guid> tag: Using the given URI in a browser should provide the given news in XHTML format. The news item should be extracted from the muse file which contains all the news. The question is: how to extract the selected news (or any other subset of the file) from the muse file and format it ?
Abstracting away from the details, this problem can be narrowed to the following: Given a stream of objects, how to select a subset of this stream using simple rules ? For example, if we consider a stream of a-z letters, we want to select the substream that is contained between the letters ab and the letters yz. Applying this rule to the word jlkjlkabcdeffdsfyzflsfdkj, we obtain the word abcdeffdsfyz
It is obvious that, in the case of letters, this problem boils down to regular languages matching: extract some rational pattern from a stream (ie. a word). But in the case of events, we are facing a somewhat different problem: the alphabet is infinite which implies the language is not regular. Furthermore, writing a finite state machine ie. a regular language acceptor, while not an extremely complex task, is not immediate and quite error-prone.
The idea I explored is based on event filtering and the Decorator pattern:
filter() method which returns whether or not the event should be filtered, The problem I faced was managing the state of the underlying FSA so that parsing events can be passed in a timely fashion:
Neither of this solutions is satisfying:
The idea is to define the filter as a SinkEvent transformer, a function from SinkEvent to SinkEvent that returns the event to be passed to the real sink. A special event is defined, NullEvent that is a non-event and, when asked to activate a sink, does nothing. By defining filters as transformers, they can be composed.
previouspublic interface SinkFilter { SinkEvent filter(SinkEvent e); }
Filters are then made into combinators that operate on stream of sink events and, together with filter operators, can form a whole algebra of stream transformers and selectors.
For this to works, I also needed to create a CompoundEvent object that implements the SinkEvent interface and that passes all accumulated events to its underlying sink. This is needed because, when the filter needs more than one event lookahead, it buffers the events passed so far and it cannot produce the correct event before having matched the whole pattern.
previouspublic interface SinkEvent { SinkEvent NULL_EVENT = new SinkEvent() {...}; void passTo(MuseSink sink); }
The interesting thing about a combinator-based implementation is that, being in essence purely functional, it has a number of interesting properties:
The current implementation suffers however from some defects and is not fully functional
double_negation_property :: (SinkEvent -> SinkEvent) -> Bool double_negation_property f = not . not . f == fbut this does not work as a simple test using a
StartFilter combinator can easily shows: Actually, one would like filters to be much like a boolean algebra, closed under the usual operators: negation, intersection, union, difference, much like lists are in most functional languages, and like rational languages are.
Ça y'est, l'Agile Tour 2008 est passé par Lille ! En voici un bref résumé, plus quelques idées pour une prochaine manifestation.
Nous avons eu pas mal de désistements, mais il faut dire que nous n'avons vraiment pas assuré côté relance: un mail tardif et une relance téléphonique la veille pour le lendemain. Seuls les gens que François et moi connaissions personnellement sont finalement venus.
Les sessions proposées étaient donc les suivantes:
Les sessions étaient découpées en créneaux de 30', plus 5' de temps élastique permettant de laisser aux gens de bouger.
Comme nous n'avions pas plus de sessions que de créneaux (18 au total), les participants ont "voté" en arrivant avec des gommettes pour nous permettre de répartir au plus juste les salles. La répartition des votes semblait indiqué en grand besoin d'information, plutôt que du pratiques, probablement parce que beaucoup de participantsn n'étaient pas des experts de l'agilité et venaient là pour découvrir, apprendre et surtout discuter.
J'ai fait le planning après avoir introduit Agile Tour et les grandes lignes de l'organisation, tandis que les orateurs se présentaient, eux et leurs sessions.
Nous avions donc trois salles:
Les locaux de TELECOM Lille 1 sont très bien, mais la disposition des salles, très éclatées les unes des autres n'étaient pas idéale. Heureusement, le fléchage était bien fait et les salles quand même situées dans des endroits évidents, pas au fin fond d'une aile. Personne ne semble s'être perdu, ou du moins pas suffisamment longtemps.
Il y avait 85 inscrits, et il me reste 22 badges, donc j'en déduis qu'il y avait au moins 63 participants. EN réalité, il devait y en avoir légèrement plus puisque des gens non inscrits se sont présentés spontanèment.
Les profils étaient très variés. Je n'ai pas de statistiques précises, dont le sens sur un échantillon de 60 personnes serait de toute façon douteux, mais j'ai perçu les grandes lignes suivantes:
Tout le monde a salué l'organisation de l'événement, ce qui ne laisse pas de me surprendre car j'ai vraiment eu l'impression qu'on a fait ça à l'arrache et que c'était un peu... chaotique. Je pense, cela dit, que beaucoup ont apprécié le côté informel. Le groupe était de petite taille, la manifestation de courte durée et le lieu compact, donc l'improvisation pouvait suppléer à la préparation.
Je n'ai eu que de bons échos, aussi bien de la part des participants que des orateurs. Il faudrait avoir dépouiller les questionnaires de satisfaction pour en savoir plus. J'ai le sentiment que tout le monde a trouvé ce qu'il venait chercher ou a pu dire ce qu'il avait à dire.
Je regrette un peu le manque d'ateliers et la faible participation au dojo présenté par Eric Mignot et Laurent Cobos. Il me semble pourtant de plus en plus que le développement de l'agilisme passe par là, que c'est un outil extraordinaire pour progresser et faire progresser. Comme si beaucoup de gens avaient un peu peur de se mettre en danger en codant collectivement, comme si le code était toujours quelque chose que l'on faisait solitairement.
J'ai vraiment envie de trouver d'autres formes que la classique conférence où l'on vient écouter la bonne parole, confortablement assis dans un amphithèâtre. Il me paraît essentiel, partie intégrante des valeurs et des principes de l'agilité, que les participants contribuent activement au contenu de la conférence.
Les ateliers sont un bon moyen de parvenir à ce résultat, et en discutant avec Marie-Aimée, nous avons imaginé d'autres types d'organisation:
An old post by Michael Feather that talks about things I would like to present at SPA2009: different approach to code quality than TDD. Here it talks about the Clean Room method that, back in the 80's, enforced quality through formal expression of code behavior with logical predicates (presumably Hoare's logic predicates).
Things are getting more and more complicated as I am closing in on the client-side of the web app. Thanks to some help from the haskell-cafe mailing list denizens, I managed to got a running FastCGI web application working, with the shared state passed around, although this feature is untested.
The basic idea is to initialize the World once in a shared variable reference, preferably a thread-safe one, then to pass that value to the worker functions that could well be some IO threads.
previousmain = do world <- newTVarIO initialState -- initialize shared variable containing state runFastCGI $ work world -- service requests work :: TVar World -> CGI CGIResult work world = do sid <- fmap (maybe 0 id) $ readInput "sessionId" -- extract session identifier sname <- fmap (maybe "index" id) $ getInput "service" -- extract service name world' <- liftIO $ atomically $ readTVar world -- extract state of the world setHeader "content-type" "text/plain" output $ service sname world'
I am presently struggling with the client side, trying to use jQuery and its Form plugin. I found the hard way that although they are somewhat equivalent, and sometimes use equivalently in HTML, id and name are not the same. jQuery selectors use id while form submission uses the name attribute. So one usually define both or use [name='toto'] selector to extract the needed field.
I am still stuck with the intricacy of submitting forms, between the subtleties of submit(), ajaxForm() and ajaxSubmit(), but I can now pass around values from the client to the server, and most notably the requested service and probably the session id if it is defined.
I plan to make the application in client/server architecture, with the Haskell code providing services and JSON data, and the client code using jQuery to update the UI.
I managed to refactor the code to use explicitly State and StateT monad, thanks to this thread on Haskell-café and this blog post. In the process I got to understand a few things about those dreaded monads and the way to combine them, although this obviously only scratch the surface of things.
The code of authenticate now looks like: previous
getUser :: String -> String -> IOWorld (Maybe User) getUser login password = lift $ readResource (User login password) authenticate :: String -> String -> IOWorld (Maybe Session) authenticate login pwd = do u <- getUser login pwd liftState (createSession [("unregister", unregister),("logout", logout)] (checkPassword u))
All top-level actions are done in the IOWorld context which is a State monad encapsulating IO monad. We need IO because we are dealing with the persistence layer.
The tests look a lot nicer, although some duplication obviously persists: previous
aLoggedInUserCanUnregisterWithKey = "Logged in user can access unregister action with key" ~: (withDB "test.db" (evalStateT (do register "nono" "password" Just session <- authenticate "nono" "password" let Just action = lookup "unregister" (actions session) action session authenticate "nono" "password") initialState >>= assertEqual "unexpected user" Nothing))
The sequencing of actions that the monad allows - and imposes - is not cluttered anymore by explicit arguments passing. The extraction of the action could encapsulated too in a nice monadic action.
The important lesson about monads is: you can't mix apples and oranges. Monad sequencing is just a special kind of composition:
.`` operator, yielding function g . f: a \rightarrow c, >> and <code>>>=</code> (ie. bind``) operators, yielding function f join g: a \rightarrow m cThe important thing to note is that both encapsulated type and monad type have to be compatible (ie. the same) for this to work. One cannot compose actions occuring in different monads without some kind of transformation which is just why monad transformers are useful: they allow one to unify different kind of actions/effects within a single layered monads.
One important function in this context is the lift operator that allows, as it name implies, lifting functions operating within one monad to another layer of monad. This means that given a IO a action, you can lift it to a StateT s IO a action by lift, but given a State s a action, how do you lift it to StateT s IO a ? You need to apply the IO monad to the result of the State monad before returning it.
One solution given by the cited posts is to define a liftState function: previous
liftState :: Monad m => State s a -> StateT s m a liftState s = do state1 <- get let (result, state) = evalState (do result <- s state <- get return (result, state)) state1 put state return result
But there is another, more elegant, solution: use the interface MonadState to constrain the context of the createSession function and all other functions that operate at the state level and could need to be lifted at some other level. We can thus now write: previous
createSession :: MonadState World m => [(String,Action)] -> Maybe User -> m (Maybe Session) authenticate login pwd = getUser login pwd >>= (createSession [("unregister", unregister),("logout", logout)] . checkPassword)
I can understand this barely but will try to explain it:
createSession must be some monad m that is also an instance of MonadStateMonadState s m, one finds MonadState s (State s) and MonadState s (StateT s m), authenticate has return type IOWorld a, or more precisely that the right hand side of the equation for authenticate has this type, we know that monadic actions will take place within this monad (a type alias for StateT World IO a), getUser l p has such a type, so we can bind its result within the monad to another monadic action, checkPassword is a simple function that takes a Maybe User, checks the supplied password is correct and returns the user or Nothing, createSession, our MonadState dependent action. Thus the whole type of (createSession ... checkPassword) is <code>MonadState World m => Maybe User -> m (Maybe Session)</code> which is monadic but with the monad as a parameter m, which is then instantiated with IOWorld an alias for StateTWhile working on Haskell implementation for Colivri, I got stuck within the complexity of monad transformers, most specifically with the wiring of the State monad with the IO monad. The code uses state to store session informations when users log in and out of the system. For example, the authenticate function looks like:
authenticate :: String -> String -> World -> IO (World, Maybe Session) authenticate login pwd s = readResource (User login "") >>= return . (createSession s [("unregister", unregister), ("logout", logout)] . checkPassword) where checkPassword Nothing = Nothing checkPassword u@(Just (User uid pwd')) | pwd' == pwd = u | otherwise = Nothing
The StateT is defined as
newtype StateT s m a = StateT (\s -> m (a,s))
so I am tempted to rewrite authenticate as:
previoustype IOWorld a = StateT World IO a authenticate :: String -> String -> IOWorld (Maybe Session)
The problem is that I am not able to write the code that would make this to typecheck or compile !
Otherwise, I managed to write a simple command-line interface through which I can login/logout and register/unregister users within the database. Next step is to wire that logic through FastCGI. But how do we maintain state on a FastCGI server ?
In a recent entry of Testing in the Toilets, a technique is described for testing asynchronous handlers tied to setTimeout et al. family of functions, available at the global scope. This is something I tried to do in Benefit for the scheduling code of test executors, but much better as here the passing of time is controllable.
I spent part of the morning switching from flat file storage to DB storage for the tiny little embryonic web app in Haskell I am working on. I managed to use Sqlite3 bindings for HDBC which is quite low-level but at least works smoothly once you recall how to code in SQL.
Installation was easy once I removed the .cabal and .ghc directories from my $HOME
sudo aptitude install libghc6-hdbc-sqlite3-dev
I then made User type an instance of IResource and implemented the CRUD operations there: previous
import Database.HDBC import Database.HDBC.Sqlite3 ... handleError str err = error $ str ++ ":" ++ ( seErrorMsg err) ++ ", code:" ++ show (seNativeError err) instance IResource User where keyResource User {login = uid} = uid writeResource User {login = uid, password = pwd} = handleSql (handleError "in write resource") (do con <- connectSqlite3 "test.db" run con "insert into auth_user (username, password) values (?,?);" [SqlString uid, SqlString pwd ] commit con disconnect con)
The rest of the code is unchanged as I have abstracted operations on users using IResource interface.
Unit tests are unchanged too, except for the setup and tearDown methods which now respectively create the necessary table and drop the table.
previouscreatetableuser = "CREATE TABLE \"auth_user\" ("++ "\"username\" varchar(30) NOT NULL PRIMARY KEY,"++ "\"password\" varchar(128) NOT NULL"++ ");" setupDB fn = (do con <- catchError "connectinig" (connectSqlite3 fn) catchError "creating tables" (withTransaction con (\con -> run con createtableuser [])) catchError "disconnectinig" (disconnect con)) tearDownDB fn = (do con <- catchError "connectinig" (connectSqlite3 fn) catchError "dropping" (withTransaction con (\con -> run con "drop table auth_user;" [])) catchError "committing" (commit con) catchError "disconnecting" (disconnect con))
The reason I am catching errors for each statement rather than encapsulating everythnig at a higher-level is that I got strange error messages at first which I spent an hour tracing. The message was something like unknown error in sqlite3... and conducted to the base being locked. It was actually caused by incorrect parameters for statements: you don't need to put quotes around the ? when the expected parameter is a string.
So I am now ready to continue working on the business domain and implement the book manipulation logic.
I refactored the code to add session handling: a user is tied to a session that is supposed to restrict the actions the user can do, and the sessions are all part of some state object that is updated and read by the various interface functions (ie. functions that corresponds to actions in the application).
The code is not very nice, with lot of redundancy as I pass along the state object everywhere in the code instead of using it implicitly through the State monad
This code should be replace by something less ugly: previous
-- session differentUserHaveDifferentSessionKeys = "Log two users and check for session key" ~: (withDB "test.db" (do st <- register "nono" "password" initialState st' <- register "tutu" "pass" (fst st) (st'', Just s1) <- authenticate "nono" "password" (fst st' ) (st''', Just s2) <- authenticate "tutu" "pass" st'' assertBool "session key not different" ((key s1) /= (key s2))))
The idea is that the Session object should hold a list of allowable functions in the present state of the application. Later on, the Session shall have a time limit attached which will be checked periodically by some cleanup thread code.
In short, each session is a continuation for the current user, that when applied some action yield another continuation until termination of the interaction. The Back button problem is solved by defining the set state space independently of the pages so that whenever a user crosses some important threshold (eg. does some action that has a side-effect in the world and is not idempotent), this action is simply removed from the continuation. Getting back on the page and reactivating the link yields to an error.
OTOH, it may be possible that back is not a problem, if the state includes several views embodied as pages.
See this article, cited from Universe of Discourse for an in-depth explanation of how data types and functions are related in FP. A data type is just a function type listing all constructors for the type.
previoustype ChurchPair a b = (forall c . a -> b -> (a -> b -> c) -> c) churchPair x y = (\operation -> operation x y) churchFst p = p (\x y -> x) churchSnd p = p (\x y -> y)
A pair is constructed (by churchPair) from two elements (a -> b) and is a function that can apply a function of two arguments (a -> b -> c) to its two constituents. Here churchFst and churchSnd are destructors that can observe the values stored in the pair by decapsulating it.
A robot whose coordinates are defined as (x,y) and which has a direction can thus be modelled as the type: previous
type Robot x y d = (forall r . x -> y -> d -> (x -> y -> d -> r) -> r) makeRobot x y d = (\obs -> obs x y d) posX r = r (\ x y d -> x) posY r = r (\ x y d -> y) direction r = r (\ x y d -> d)
For project COlivri, I am trying to create my first web application in Haskell, using HApps as a web application server and framework.
Getting HApps built and installed is not straightforward, at least when your GHC installation seems a bit broken as is my case (Ubuntu Hardy Heron, GHC 6.8.2). It took me approximatively 1 hour to got the first basic example compiled and running. Assuming a ghc compiler available (in my case 6.8.2), the right way seems to be:
cabal update
darcs get --partial --tag=0.9.2 http://happs.org/repos/HAppS-HTTP cd HApps-HTTP cabal install
This scenario implies that all packages got installed into $HOME/.cabal directory. There should be a way of installing everything system-wide, probably using some prefix command.
I could not manage to start working with the HApps-Begin sample blog, which actually seems outdated and is not referenced anymore in the HApps main page. Looking at AllIn example is not really convincing that HApps is, as advertized on the web page:
A web framework for developers to prototype quickly, deploy painlessly, scale massively, operate reliably, and change easily.
I am turning towards a more barebone approach using CGI scripts. The idea anyway would be for the application to be structured as:
I decided to give a try to the approaches described here and here, which is certainly cruder than using a web application framework but at least everything in there I understand.
FastCGI is a replacement for Common Gateway Interface protocol where, instead of starting one new process for every invocation of desired script, the scripts are kept running within the context of a server so that there is no loading and linking time wasted.
Installing FastCGI to run on apache 2.2 is easy:
sudo aptitude install libapache2-mod-fastcgi sudo a2enmod fastcgi sed -i -e '/<\/VirtualHost>/ i\ \ Alias /hcgi/ /var/soft/hcgi/\ \ <Directory /var/soft/hcgi/>\ SetHandler fastcgi-script\ Options +ExecCGI\ </Directory>' /etc/apache2/sites-available/default /etc/init.d/apache2 restart
This makes directory /var/soft/hcgi/ a container for executable FastCGI programs (they should have extension .fcgi).
Then I copied a simple echo program that outputs process and PID id for each request: previous
import Control.Concurrent import System.Posix.Process (getProcessID) import Network.FastCGI test :: CGI CGIResult test = do setHeader "Content-type" "text/plain" setStatus 200 "OK" pid <- liftIO getProcessID threadId <- liftIO myThreadId let tid = concat $ drop 1 $ words $ show threadId output $ unlines [ "Process ID: " ++ show pid, "Thread ID: " ++ tid] main = runFastCGIConcurrent' forkIO 10 test
Compilation is:
ghc -threaded -package fastcgi --make -o info.fcgi info.hs
I start by writing an authentication module. This module lets a user authenticate itself on a form providing a login/password combination, then lookup the user in some database. If the user is regristered, then it receives main page of the application with the ability to logout, otherwise it is show the login page again with an error message.
I want to work using continuations-based forms, without relying on cookies for user identification. All the information is stored in the server using a map from tokens to functions. Tokens have an expiry time which makes functions unavailable after a while.
I found this blog that shows how to implement persistence layer for data objects à la Hibernate in Haskell. It describes a Transaction Cache that stores allows CRUD operations for abstract resources mapped to underlying persistence layer.
I try to implement a persistence layer using sqlite and colivri DB.
2008.10.01 18:24:28
I managed to implement login and registration logic, with unit tests, using simple file system storing as I could not manage to find a working HDBC sqlite3 driver to work. When trying to install the modules from cabal, they would not compile, complaining about missing identifiers which obviously is a problem. I may try to install them from sources directly.
The code so far looks like: previous
module Colivri where import TCache import DefaultResource data User = User { login :: String , password :: String } deriving (Eq, Show, Read) instance DefaultPersistResource User where defKeyResource User {login = uid} = uid defPath User {} = "users/" authenticate :: String -> String -> IO (Maybe User) authenticate login pwd = readResource (User login "") >>= return . checkPassword where checkPassword Nothing = Nothing checkPassword u@(Just (User uid pwd')) | pwd' == pwd = u | otherwise = Nothing register :: String -> String -> IO (Maybe User) register login pwd = readResource (User login pwd) >>= registerIfNotPresent where registerIfNotPresent Nothing = do let u = (User login pwd) writeResource u return (Just u) registerIfNotPresent _ = return Nothing
The TCache module provides a simple CRUD layer where objects are stored with Show and retrieved with Read. This is actually a limitation of the DefaultPersistResource which stores data in flat files named by primary key. It should not be too complicated to implement something else as IResource interface does not rely on particular structure of data.
More interesting are the unit tests, partially shomn here: previous
import Control.Exception(bracket_) import System.Directory(createDirectoryIfMissing,removeDirectoryRecursive) setupDir :: FilePath -> IO () setupDir fn = createDirectoryIfMissing True fn tearDownDir :: FilePath -> IO () tearDownDir fn = removeDirectoryRecursive fn withDirectory fn = bracket_ (setupDir fn) (tearDownDir fn) unregisteredUserCannotBeAuthenticated = "Empty user tries to authenticate and fail" ~: (withDirectory "users" (authenticate "" "" >>= assertEqual "user should not exist" Nothing)) registeredShouldBeAuthenticated = "Existing user tries to authenticate and succeeds" ~: (withDirectory "users" (register "nono" "password" >> authenticate "nono" "password" >>= assertEqual "user exist" (Just $ User "nono" "password"))) registeredUserWithBadPasswordIsRejected = "Existing user tries to authenticate with wrong password and fails" ~: (withDirectory "users" (register "nono" "password" >> authenticate "nono" "pass" >>= assertEqual "user exist" Nothing))
I found this article very useful. it provided me the necessary features to create setup and teardown methods. The key point is to notice that Assertion actually produces an IO value, and thus can be injected into IO monad code easily.
Here is a short list of resources I used today:
Some mathematical musing about the illusions of normal distribution and how it affects our perception of average, even for that fraction of a population which is being selected from the above average
http://blog.plover.com/2008/09/30/#right-skewed
It has some interesting consquences as far as programming and software development is concerned, and I particularly liked this sentence:
That's an important thing to know about the sport, and about team sports in general: you don't need great players to completely clobber the opposition; it suffices to have players that are merely above average. But if you're the coach, you'd better learn to make do with a bunch of players who are below average, because that's what you have, and that's what the other team will beat you with.
Do not confuse the average with the median !
Working on displaying links from the site feed. The idea is that a link like /journal/2008/08/08/some-title should display only the selected entry if it exists, respecting the overall page style of the site. The problem is: how to extract part of the file for markup ?
The solution I came up with is to wrap the destination sink (usually HTHL) so that it receives only events within the start and end of the selected section. One obvious problem with this scheme is that sectioning is not hierarchical. Titles are just like hX header tags in HTML, they only denote some formatting change but not distinct levels. This could be changed easily by embedding sectionning within the muse parser, so that section state would last to next sectioning or end of document, whichever comes first.
It is then easy to select only the events that pertains to some section as we can filter on the start event and then decide whether or not to pass all events to wrapped sink.
Another solution is to accumulate the markup events and pass them to downstream formatter only if selector's pattern is triggered.
Started to write tests for OQube's site that could be run under CI to ensure proper operation of the site. First test written is a spider that checks all links collected from the web pages traversed by the site. This test uses code from Jeff Heaton web sites for a simple spider. For the moment, it is sophisticated enough for my needs.
I ran into a problem reported here while checking URL from Ubuntu web site using HTTPS. Looks like Sun's default cacerts file is missing some useful certificates.
I also want to setup some load and performance testing. The problem is how to setup a meaningful test for such a thing ? I looked at various tools for web application testing:
sudo aptitude install httperfand produces results like:
$> httperf --hog --server www.oqube.net --num-conn 1000 --ra 70\ --timeout 5 httperf --hog --timeout=5 --client=0/1 --server=www.oqube.net --port=80 --uri=/ --rate=70 --send-buffer=4096 --recv-buffer=16384 --num-conns=1000 --num-calls=1 Maximum connect burst length: 5 Total: connections 1000 requests 619 replies 619 test-duration 19.279 s Connection rate: 51.9 conn/s (19.3 ms/conn, <=230 concurrent connections) Connection time [ms]: min 119.9 avg 1229.7 max 4529.6 median 1209.5 stddev 868.4 Connection time [ms]: connect 636.6 Connection length [replies/conn]: 1.000 Request rate: 32.1 req/s (31.1 ms/req) Request size [B]: 64.0 Reply rate [replies/s]: min 32.6 avg 40.7 max 45.6 stddev 7.1 (3 samples) Reply time [ms]: response 590.6 transfer 2.5 Reply size [B]: header 172.0 content 1884.0 footer 0.0 (total 2056.0) Reply status: 1xx=0 2xx=619 3xx=0 4xx=0 5xx=0 CPU time [s]: user 0.21 system 14.00 (user 1.1% system 72.6% total 73.7%) Net I/O: 66.5 KB/s (0.5*10^6 bps) Errors: total 381 client-timo 381 socket-timo 0 connrefused 0 connreset 0 Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
Might have a look at http://webflange.sourceforge.net/ too.
On a recent post, Ricky CLarkson implements constraints on data fields using a builder pattern: fields are declared using a combination of filter (Parameter) objects that apply certain constraints. Then an object is constructed with Builder pattern that checks appropriate constraints and throws unchecked exception upon error.
Un dojo inspiré de Tony Morris: comment analyser une chaîne de caractères composée de paires de parenthèses et crochets équilibrés ?
Started working on the tester agent, first laying out basic infrastructure for executing JUnit tests: wrapping JUnitCore executor so that test cases get detected by the framewok, while handling class loading issues.
Test executions is OK, both for failures and successes, but I suspect reporting is rather lame. This shall be improved later on. My goal is to reach rapidly a state where:
I read and compiled the code from http://lucdup.blogspot.com/2008/09/closures-statefulness.html using the latest java compiler integrating closures from http://javac.info.
I finally wrote a Haskell program for managing my ledger files and producing raw data for my accountant. The problem is simple: I use ledger program for bookkeeping, which is basically a flat text file containing all basic accounting transactions in double-entry format (a transaction is valid iff all atomic writings to its accounts sum up to 0, see http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system).
Ledger program can produces various outputs, most notably the balance format, that outputs the net debit and credit value of each account taking into account all transactions; and the csv format which is all transactions as CSV, for easy inclusion in spreadsheets for example. There is no debit or credit notion in ledger, only positive and negative numbers.
What I need is somewhat similar to CSV output, but with additional requirements:
So the basic steps for this is the following:
In short, we have the following structure for the main function:
previousmakeGeneralLedger :: [String] -> [String] makeGeneralLedger = (concatMap displayCsv) . (map (accumulate amountZero)) . groupByAccount . sort . (map parseRawCsvLine) groupByAccount :: [TxItem] -> [[TxItem]] groupByAccount = groupBy (\ t t' -> accountNo t == accountNo t') accumulate :: Integer -> [TxItem] -> [TxItem] accumulate acc [] = [] accumulate acc ((TxItem d c p l an al am _) : ts) = (TxItem d c p l an al am (addAmount am acc)) : accumulate (addAmount am acc) ts displayCsv :: [TxItem] -> [String] displayCsv ts = (map displayTx ts) ++ [""] displayTx :: TxItem -> String displayTx (TxItem d c p l an al am ac) = concatMap (++ ";") [(show d) ,an ,al ,"(" ++ p ++ ")" ,l ,(debcred am ";") ,(debcred ac ";")]
TxItem is a record data type that contains all the necessary information about a single transaction.
The first version I wrote uses parsec library for parsing the input. This is where I spent a lot of time, understanding how parsing with combinators worked and making lot of mistakes, out of ignorance and out of bad practice.
I tried to use TDD to do the parsing, which helped me a bit but did not produce a lot of tests. Tests are painful to write, like:
previouscanParseCSVTxItem = TestList [ txItemParser txItem "\"2006/06/12\",\"Facture Norsys SAS\",\"706100:Produits:Services\",\"EUR-900,000000\",\"EUR-900,000000\",\"*\",\"F20060601\",\"\"" ~?= Right (TxItem (toGregorian 2006 06 12) True "F20060601" "Facture Norsys SAS" "706100" "Produits:Services" (TxAmount Credit "EUR" 900000000) (TxAmount Credit "EUR" 900000000)) ]
Main problems I had were:
| preventing =< | >= rules to complete succesfully; |
EUR string. There seems to be well-known limitations or problems with the way Haskell handles UTF-8: although internally strings are lists of unicode characters, I/O is not so forgiving. readInt :: String -> [(Int, String)] readInt = reads ... let (ip,_):_ = readInt intpart
toInteger to transform everything. There seems to be a Data.Fixed type that would be better suited but it is poorly equipped in terms of operators Developping version 1 I cut corners and it failed: The program computed incorrect values. Two things that I did not take into account where:
TxAmount data, I separate again integral and decimal part with quotRem then concatenate the two strings. But I did not take into account the fact that the decimal part could be lower than 10^6, which means its string represenation has 5 instead of the required 6 digits. The interesting thing is that both errors are due to lack of verification of those parts of the code that just do this parsing, or in other words, I did not test enough my program before shipping, or worse I did not developed it using TDD, which would have hopefully revealed this problem before.
These problems are actually the two sides of the same coin, and what I really need is asserting that:
Integer, I can show it then parse it again and it will be the same value. The real problem, of course, lies in the any: These properties should be proved for all possible correct strings and all possible Integer values, while my TDD tests cover only a fraction of the possible values, selected for their representativity or their usefulness at the time of writing the test. The devils lurks in the details, so I must be careful while TDDing in choosing those values.
Quickcheck may help in solving those problems. I can express the two above properties using Quickcheck: previous
roundtripOnInteger = forAll generateDecimal $ \d -> (right . parseDecimal . showDecimal) d == d roundtripOnStrings = forAll generateCorrectStrings $ \s -> let s' = trimStrings s in collect (lengthOfDecimal s') $ (showDecimal . right . parseDecimal) s' == s
The second one is a little more complex as we are verifying the property for all strings where trailing zeros in the decimal part have been omitted, which means we first generate a correct version of the string, then trim it, then compare the roundtrip transformation to the initial input.
The collect (lengthOfDecimal s') statement allows us to collect some statistics about the distribution of the lenght of generated strings, which unsurprisingly shows that lenght 7 (including the leading comma) are vastly outnumbering other lengths, but those are not null:
90% 7. 8% 6. 0% 5. 0% 4. 0% 3.
The 0% figures are due to some formatting issues of percentages in Quickcheck.
Given that we generated 10000 test cases without failures, we can now be pretty confident that this part of the program is safe. We can have some information about what part of the code we exercised using hpc:
ghc --make -fhpc -i/home/nono/soft/Calendar Accounting.hs QCTests.hs hpc report QCTests hpc markup QCTests
and here are the figures:
We can see that the coverage of Accounting module which contains the main code is rather poor, which is normal as the quickcheck tests only exercise a small part of the reading and parsing of numbers.
La traduction d'un article de Scott Ambler sur les projets au forfait dans lesquels toutes les variables sont fixées: prix, délais, étendue.
While working on a dependency management tool for Courtanet, I am trying to understand how maven resolves things. Given a POM file for some project, here are the following steps I could identify:
ArtifactCollector#collect()DefaultMaven and MavenEmbedderArtifact and MavenProject is added automatically when a project is read but this does not seem to be recursive: parent's getArtifact method returns null, so does parent.getParentArtifact()MavenProject and Artifact instances. The former is constructed through reading some effective project description (ie. a file) while the latter is constructed through resolution mechanism, using ArtifactResolver and/or ArtifactCollector. The link between them is the File object: getFile() and getArtifact()getFile() and do something Tried to do some handling of ledger's CSV output using Haskell's parsec library. It took me a whole afternoon to manage to construct a signle transaction item as I had lot of problems:
The resulting code is ugly, following closely my experiments and progress in understanding Parsec's combinators. I suspect the time to implement loading into database will be similar...
Maybe I could propose a short or informal tutorial software engineering practices that relates to generative programming:
This workshop could easily be a 300' full tutorial for the Sunday afternoon, or day-long workshop.
Title: More Bells and Whistles: Validating Code Beyond Unit Tests with Functional Programming
One-line description: Exploring validation and verification of code from an agile perspective with more than unit tests, or how can we improve quality, simplicity, legibility using tools from the functional programming paradigm ?
Session format: 150 Minutes Workshop/Tutorial
Abstract: Test Driven Development is one cornerstone of eXtreme Programming and its advantages are well-known: it guides developers, builds confidence in the system and provides secure scaffolding to support change. There are however some drawbacks in using this technique: it is quite verbose, it places to much emphasis on details, on the 'how' instead of the 'what', it produces more code that will need to be maintained and is not very useful to non technical people.
In this session, we want to explore techniques that keep the spirit of TDD while being more concise, provides a better abstract view of what we want to achieve, and may sometimes be more legible. These techniques are drawn from the functional programming languages tools and techniques. Functional languages are getting more and more attention and some of their characteristics may be useful in increasing software quality.
This workshop will be a hands-on guided coding party using the pure, lazy, statically typed language Haskell and providing introduction to some tools and techniques. Examples in other languages (Java, Scala, Javascript) will also be provided.
Its goal is to study and assess each of the following techniques and tools within the framework of XP practices in order to go beyond standard code quality improvement practices:
Audience background:
Benefits of participating:
Materials you'll provide:
Process:
Detailed timetable:
Outputs:
History: I designed with Christophe Thibaut an introductory session on functional programming languages which has been presented at XP Day Paris and Agile 2008, and will be presented at XP Days Benelux 2008. This session is a creation that draws upon our experience on this theme, exploring more advanced topics.
Themes: Technology Practice
Session Leaders Arnaud Bailly
While writing script for extracting commit stats from Hg such that one can produce a graph of volume change in a code base, I needed to upgrade my builder tool which contains some classes for handling programmatically in Java Mercurial repositories. The code did not work anmyore, if it has ever worked, a couple of tests failing. While digging through JCI API I finally concluded that it would be better to use standard tools provided by javax.tools.* package in Java 6.
As a side-effect, I was finally able to use closure proposal for Java and compile (mostly) functional java samples. This will allow me to start studying Reductio and how it applies to some standard code base.
I plan to revive builder and quickly adding the two most needed tools:
A type parameter used in a datatype declaration that does not appear in the rhs (ie. in the constructors) but serve as a way to separate various instances of the same types at compile time. Can be used to:
-- Peano numbers at the type level. data Zero = Zero data Succ a = Succ a -- Example: 3 can be modeled as the type -- Succ (Succ (Succ Zero))) data Vector n a = Vector [a] deriving (Eq, Show) vector2d :: Vector (Succ (Succ Zero)) Int vector2d = Vector [1,2] vector3d :: Vector (Succ (Succ (Succ Zero))) Int vector3d = Vector [1,2,3] -- vector2d == vector3d raises a type error -- at compile-time, while vector2d == Vector [2,3] works.
Test Driven Development is one cornerstone of eXtreme Programming. The red-green-refactor cycle is the smallest form of feedback the developer can receive. The advantages of using TDD are well-known and may be summarized as: first, it gives the developers a strong guidance on what to do that together with the heartbeat nature of cycles provide confidence and ultimately good code; second, it incrementally builds a kind of scaffolding for software that gives strong assurance when one needs to change anything in the system.
There are however some drawbacks in using this technique: it is quite verbose, it places to much emphasis on details, on the 'how' instead of the 'what', it produces more code that will need to be maintained and is not very useful to non technical people (this problem arises most often when trying to introduce the TDD practice as lot of managers, and all customers, cannot easily appreciate the value it provides without delving into code and may dismiss it as some mundane developer practice that we can easily get rid of when in a hurry, while actually it stands at the heart of XP).
In this session, we want to explore techniques that keep the spirit of TDD: build incrementally a software's safety net, while being more concise, provides a better abstract view of what we want to achieve, and may sometimes be more legible. These techniques, drawn from the vast array of formal methods and programming languages research and development, are not new, but most often are confined in some particular contexts, eg. embedded and human-life critical software, research groups.
We believe that two parallel movements we see are occuring in the software development business may be signs that the time is ripe for introducing more formal techniques in the mundane world of IT: the widespread adoption of some core agile practices such as unit and functional automated testing, driven by the need for better quality software; and the rise in interest for functional programming languages such as Scala, F#, Haskell or functional aspects of OO languages: closures, value-objects, side-effect free computations.
We are thus proposing this workshop as an exploration into languages and tools for better quality software, applied to some short yet representative problems. This workshop will be a hands-on guided coding party using Scala or Haskell language and providing introduction to some tools:
The goal of the workshop is to study each of these techniques and tools within the framework of XP practices, in order to assert which, if any, could supplement, complement or replace standard TDD practices.
This workshop could introduce some of the following features:
Attention à la connectique des disques transportables, elle doit résister aux épreuves d'un transport en sac à dos.
System thinking is about modeling systems as variables (observables) with constraints between them. More formally, a dynamic system is a set of differential equations defining the behavior of a set of variables over time.
This kind of system can easily be represented as labelled digraphs where each node is a variable and each edge is labelled with some coefficient, the value of the variable at any point in time being the sum of all its incoming edges coefficient time the opposite's vertex value.
Equivalently, the system can be represented as a square matrix where each row is a variable, with value in cell defining the coefficient for the variable at this column.
We want to simulate the evolution of such a system, with a discrete time: state of the system progresses in discrete equal time increments. While the former representation is easier to understand, the latter is better suited for easy calculation of the state of the system, which is just matrix multiplication.
Networking in Haskell is just as magic as actors: one it compiles, chances are high that you end up with a running program.
previous-- a server that listen for connection on some socket then -- dispatches the content to actors according to message id main = do sock <- listenOn (PortNumber $ fromInteger 6789) forever $ answer sock where answer sock = do (hdl, host, port) <- accept sock forkIO $ server hdl
Some experiments on actors and concurrency with Haskell. My first actor program : previous
module Main where import Control.Concurrent import Control.Concurrent.Chan import System.IO -- simple messages type Msg = String -- an actor has an idea, an associated channel and some action -- performed when receiving messages data Actor = MkActor Int (Actor -> Msg -> IO ()) run :: Chan Msg -> Actor -> IO () run chan a@(MkActor id hdl) = do msg <- readChan chan hdl a msg threadDelay 1000 handler :: Actor -> Msg -> IO () handler (MkActor id hdl) msg = putStrLn $ "actor " ++ show id ++ ": handling " ++ msg main = let channels = [ newChan | id <- [ 1 .. 10]] actors = [ (MkActor id handler) | id <- [1 .. 10] ] -- read a command from stding -- xx some string command = do line <- getLine return $ splitL line splitL :: String -> (Int, String) splitL (x : ' ' : rest) = (read (x : ""), rest) splitL _ = (0,"") loop chans = do (i,s) <- command case i of 0 -> return () x -> do writeChan (chans !! (x - 1)) s loop chans in do -- create 10 channels chans <- sequence channels -- create 10 runners runs <- mapM (forkIO . uncurry run) (zip chans actors) -- loop over input line loop chans
This program is definitely ugly, however, and does not do justice to the terse style usually available in haskell. I still have some things to understand when working in a monad. Channels and actors creation is especially tricky:
newChan in a do construct to pass it to the actors There should be a simpler way to assemble those things.
Something really extraordinary is that, once I managed to compile that program, iet just worked as expected ! I had no previous exposure to doing I/O within Haskell apart from whaat I have read, neither did I have any clue about Control.Concurrent.Chan, but it was quite easy to assemble things up.
This is a tribute to Haskell's type systems (and a bit of my own experience) that prevents you from writing totally silly expressions. At the very least, you have the assurance that your program will run, may be not in the expected way, but it won't crash haphazardly.
192.168.50.0 utilisant des interfaces virtuelles sur eth0.1. vserver| Nom | IP | Utilisation | Port naté |
|---|---|---|---|
| public | 3 | serveur mail entrant et sortant | 25,993 |
| web | 4 | serveur web public | 80 |
| dev | 2 | serveur de développement | 443 |
oqube.net sur IP 192.168.50.xxxmake-vserver.sh pour création et ajout automatique des vservers dans le DNS www.oqube.comnonoTODO
openssl.cnfhg depuis www.oqube.comsecure pour accès HTTPS et authentification forte par certificats cryptés: création fichier mots de passe, génération certificat, configuration apache2 nono et autres An example method that constructs some UI object. The intent is expressed as comments in the code: previous
/** * Construction de l'etape(initialisation de la vue) * * @param styleStep * @param styleContent */ public FlexTable initialiseView(String styleStep, String styleContent) { // on la construit view = new FlexTable(); view.setStyleName(styleStep); // 1. header view.setWidget(0, 0, buildHeader()); // 2. contenu if (null == form) { form = initForm(); form.setStyleName(styleContent); } view.setWidget(1, 0, form); // 3. footer if (hasFooter()) { displayFooter(); } return view; }
We can rephrase it, hopefully in much more legible way thus:
previouspublic FlexTable initialiseView(String styleStep, String styleContent) { FlexTable view = new FlexTable(); view.setStyleName(styleStep); buildTableHeader(view,0); buildTableBody(view,1); if(hasFooter()) displayFooter(view); return view; } private void buildTableHeader(FlexTable view, int pos) { view.setWidget(pos, 0, buildHeader()); } private void buildTableBody(FlexTable view, int pos) { if (null == form) { form = initForm(); form.setStyleName(styleContent); } view.setWidget(pos, 0, form); } private void maybeDisplayFooter(FlexTable view,int pos) { view.setWidget(pos, 0, buildFooter()); view.getCellFormatter().setStyleName(pos, 0, AbstractStyles.FOOTER); }
This is not perfect as we can see that, for example, buildTableBody method initializes some form in the enclosing context while at the same time modifying the passed FlexTable view.
Ce court trajet nous a permis enfin de mettre en évidence l'importance de la question du discours et son irréductibilité: on ne peut pas tout dire, et sur ce dont on ne peut rien dire, il faut se résigner à ce taire. Cette position centrale du discours, des niveaux de discours, des métalangages est un leg du postmodernisme. Le sens n'est pas un produit univoque consubstantiel à un discours et qui serait produit de bout en bout par un locuteur, c'est une relation, un processus en constante évolution.
Le processus de développement d'un logiciel doit donc être envisagé
http://www.io.com/~wazmo/blog/archives/2008_08.html#000285
Keep agile small because passionate, collaborating individuals produce simple solutions
hg serve afin que chacun puisse utiliser sa machine et ses habitudes, tout en ayant le code versionné. J'imagine que l'on pourrait étendre ce principe en utilisant un serveur VNC pour pouvoir changer facilement l'affichage d'une machine à l'autre. A cool hack to have Maybe (or Option) wrapper in Java: http://stephan.reposita.org/archives/2008/08/06/for-hack-with-option-monad-in-java/
First real day at Agile 2008, starting with the AA-FTT workshop. Workshop was in OpenSpace format (sort of). I like this format: much more dynamic, gives plenty of rooms for interaction, surprises, works great with small rooms. I was both a bit tired and a lot impressed by the attendance, comprised of highly renowned and respected people in the agile testing community like Brian Marick, Elizabeth Hendrickson and many others, so I did not contribute a lot.
Some topics I picked up through the workshop:
input name function on a page where name is defined, which means you would need some sort of typing of pages, or at least distinguishing various pages contexts J'ai commencé à m'intéresser à l'informatique à peu près au moment où le cyberpunk, comme genre littéraire, naissait.
Profondément enfoui dans mon subconscient se trouve donc la conviction que l'informaticien est LE héros post-moderne, et conséquemment que l'informatique, ou plus précisèment l'activité de programmer des ordinateurs et aussi celle d'assembler des machines qui lui est souvent associée, sont des activités, des processus, éminemment post-modernes.
Le héros post-moderne se méfie des grands récits, il est paranoïaque.
Il est même parfois un peu schizophrène, quand il lui arrive de détourner le système à ses propres fins, car qui sait si ce n'est pas le système qui le détourne ? La matrice n'est-elle pas que l'espace utilisateur d'un gigantesque OS, et en déchirer le voile ne serait-il pas accéder à l'espace noyau ? Auquel cas, l'envers du décor, c'est toujours un autre décor, et non pas la réalité.
Le héros post-moderne est profondément libertaire. Il n'aime pas le pouvoir et n'apprécie les règles et les lois que lorsqu'elles sont révocables à merci. Il peut adopter un mode de vie strict, voire ascétique, dans un but de dépassement de soi, jamais par dévotion envers une religion.
Le héros post-moderne est Agile. Il est pragmatique dans sa pratique, et idéaliste dans ses objectifs. Il évolue dans des organisations qui sont idéalistes dans la pratique qu'elles imposent, et pragmatiques dans les buts qu'elles poursuivent.
Le héros post-moderne sait que la chair est triste, et il a lu un peu trop de livres. Il n'en aime pas moins passionnément la vie et la connaissance.
Il sait que la révolution doit être souhaitée, mais qu'elles finissent toutes dans le bain de sang et la dictature. Il institue donc des micro-révolutions là où c'est possible, trace des lignes de fuite. Sa stratégie est celle du rhizome. Son horizon la totalité du réel.
Le héros post-moderne aime XP car XP a été conçu par un héros post-moderne, pour d'autres héros post-modernes. Il n'est pas un solitaire et n'a aucune appétence pour l'érémitisme. Au contraire, il recherche la compagnie de ses semblables, car il sait que le sens est dans la relation aux autres, pas dans le solipsisme.
Le héros post-moderne ne se désole pas de la récupération de ses fétiches par la société spectaculaire, car il sait que c'est le lot de toute chose, et la seule chose que sait faire le spectacle. Il est lui-même un grand récupérateur, assembleur, bidouilleur, sampleur, mashupeur... Nomade et pragmatique, il change de terrain de chasse quand l'herbe a été trop remâchée.
Je ne suis pas ce héros, mais il ne me déplairait pas que je le fusse.
Created vserver creation script:
Created zone files for oqube.net
cat >> /etc/bind/db.oqube.net << EOF
$TTL 3h
oqube.net. IN SOA master.oqube.net. adminsys.oqube.net. (
1 ; serial
3h ; refresh
1h ; retry
1w ; expire
1h ); negative cache TTL
oqube.net. IN NS master.oqube.net.
master.oqube.net. IN A 192.168.50.1
dev.oqube.net. IN A 192.168.50.2
EOF
cat >> /etc/bind/db.192.168.50 << EOF
$TTL 3h
50.168.192.in-addr.arpa. IN SOA master.oqube.net. adminsys.oqube.net. (
1 ; serial
3h ; refresh
1h ; retry
1w ; expire
1h ); negative cache TTL
50.168.192.in-addr.arpa. IN NS master.oqube.net.
1.50.168.192.in-addr.arpa. IN PTR master.oqube.net.
2.50.168.192.in-addr.arpa. IN PTR dev.oqube.net.
EOF
cat >> /etc/bind/named.conf << EOF
// zone files for oqube.net. virtual network
zone "oqube.net" {
type master;
file "/etc/bind/db.oqube.net";
};
zone "50.168.192.in-addr.arpa" {
type master;
file "/etc/bind/db.192.168.50";
};
EOF
2008.07.12 19:42:58
Tried using hpc. Works nicely and generates clean if somewhat unappealing coverage reports. Very simple to use, is integrated in ghc6.8.2.
Tried installing HaRE tool for Haskell Refactoring on Ubunte 7.10 with ghc 6.6.1. Two libraries are needed for proper compilation of the snapshot:
#provide networking sudo aptitude install libghc6-network-dev # providing Control.Monad.State sudo aptitude install libghc6-mtl-dev
Tried to use the tool on Controles.hs but failed to do anything:
add project fails on import of Control.Arrow ifdef directive Unit tests does not work either:
import Test.HUnit../services/agile/postmodern un article en cours