Skip to content

Instantly share code, notes, and snippets.

@mmacphail
Last active August 14, 2020 09:41
Show Gist options
  • Save mmacphail/7e0aa890aacb555adfb7c2777f28db63 to your computer and use it in GitHub Desktop.
Save mmacphail/7e0aa890aacb555adfb7c2777f28db63 to your computer and use it in GitHub Desktop.
Clojure article 1
Macro planning
Article 1 : présentation générale de clojure, syntaxe, définitions, fonctions simples
Article 2 : symboles, keywords, values, datastructure, fonctions et macros built-in (if let do map filter), lamda, la doc et son accessibilité, pas de boucles ;), les valeurs truthy
Article 3 : présentation de programmation fonctionnelle dans clojure: la recursion, les lambdas (reduce, loop)
Article 4 : polymorphisme, exemples avancées (trees)
Article 5 : java interop
Article 6 : threading, futures, ref, agents, atoms, STM
Article 7 : macros
========================================
Article 1
========================================
Clojure rationale
https://clojure.org/about/rationale#:~:text=Clojure%20meets%20its%20goals%20by,transactional%20memory%20and%20asynchronous%20agents.
Clojure is an effort in pragmatic dynamic language design in this context. (contexte de la JVM)
Concu pour être utilisé là où java est utilisé.
Conviction : dans le futur concurrent programming, la mutabilité doit disparaître
Définition concurrent programming : https://en.wikipedia.org/wiki/Concurrent_computing
Définition mutabilité : https://en.wikipedia.org/wiki/Immutable_object
n object-oriented and functional programming, an immutable object (unchangeable object) is an object whose state cannot be modified after it is created.
It endeavors to be a general-purpose language suitable in those areas where Java is suitable. It reflects the reality that, for the concurrent programming future, pervasive, unmoderated mutation simply has to go.
Inspiré des langages LISP
-> lisp Homoiconic code. This allows structured self-modifying code.
It is quite common, and easy, for Clojure programs to manipulate, transform and produce other Clojure programs.
(https://clojure.org/reference/reader)
Clojure language has syntax defined in terms of symbols, lists, vectors, maps etc.
Homiconic https://en.wikipedia.org/wiki/Homoiconicity
A language is homoiconic if a program written in it can be manipulated as data using the language, and thus the program's internal representation can be inferred just by reading the program itself.
Le code est traité comme de la data.
Concept important, promeut le data-orented programing.
Conséquence: la syntaxe de clojure est très compacte
Java 51 mots réservés https://en.wikipedia.org/wiki/List_of_Java_keywords
Javascript 3*11 mots réservés https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar
Clojure 21 mots réservés (grosso modo car on parle des spécial forms)
Clojure est un langage de programmation fonctionnel (https://en.wikipedia.org/wiki/Functional_programming) et on verra ce que ça signifie concrétement au cours de cet article.
Particularité: clojure est un langage dynamic (pas de static typing)
Clojure est compatible avec le java. Il existe aussi une variante de clojure compatible avec javscript, ClojureScript. Le même langage est supporté par deux plateformes différentes! (avec des variances de syntaxe, mais fondamentalement le code qui fonctionne sur une plateforme fonctionne sur l'autre [mieux expliquer])
Clojure est designé pour être simple (mais attention simple != facile https://www.infoq.com/presentations/Simple-Made-Easy/)
REPL driven development (https://lambdaisland.com/guides/clojure-repls/introduction#orge1d5097)
A REPL is an interactive prompt, a program which allows you to type in some code, and shows you the result of evaluating this code. You are probably already familiar with several REPLs, such as the browser console, which evaluates JavaScript, or your Terminal (Console, Command Prompt, Shell), which evaluates shell code.
REPLs are important because they give you instant feedback. You don't have to save, compile, then run. Just type the code and press enter. This encourages experimentation. It's the "let's poke at it with a stick" of programming, a way of instantly validating, or adjusting, your concept of reality.
Clojure is especially suitable for this because the representation of its values is in turn valid Clojure code, so the return value of a function can be easily read and interpreted. Instead of opaque impenetrable objects we have simple maps, vectors, sets, with their contents clearly showing.
===================================================
Premiers pas avec Clojure
Se connecter sur : https://repl.it/languages/clojure
A gauche, fichier main, à droite REPL
Plan :
- explication repl, fichier main
- afficher un hello world
- faire une addition => parler des fonctions => en clojure, n'importe quel opération est un appel de fonction. Même l'opération + ; et un appel de fonction a toujours la forme (fonction argument1 argument2 argument3)...
par conséquent, une simple addition sera (+ 1 2). La syntaxe est particulière mais elle a le mérite d'être claire et consistente car ça sera la même chose pour tous les appels de fonction partout dans le langage.
Autre avantage de cette syntaxe: les fonctions standardes prennent souven du multi argument.
On peut donc écrire (+ 1 2 3 4) pour 1 + 2 + 3 + 4 ou (/ 1 2 3) pour 1 / 2 / 3
- pas d'odre de précédence dans les opérations (http://www.cs.bilkent.edu.tr/~guvenir/courses/CS101/op_precedence.html ; toujours une optique de simplicité)
- les variables. elles n'existent pas en clojure (préciser que ce n'est pas vrai en temps que tel; il existe des variables globales dont le but est de gérer la configuration pendant l'exécution du programmer, à utiliser dans des cas particuliers donc https://clojure.org/reference/vars). Une variable comme son nom l'indique est mutable, c'est à dire qu'elle peut être modifiée après usage.
Ex en java:
int age = 19;
age = 21;
Hé bien en clojure ce n'est pas possible de faire ça :). Mais ça n'empêche pas de pouvoir programmer des choses utiles.
- il est possible toutefois de déclarer des constantes, avec le mot clé def. La déclaration se faire de la forme (def maconstante mavaleur). On peut ensuite référencer la valeur de la code en indiquant son nom.
(def salaire 2000)
(def loyer 800)
(def solde (- salaire loyer))
solde dans le repl retour 1200
- un petit mot sur les (). Les parenthèse signifient en clojure qu'on déclare une liste. (Lists are sequential linked lists, comme une linkedlist en java mais immutable (on ne peut pas modifier son contenu après déclaration)).
Ainsi en clojure (1 2 3) est une liste valide (en repl taper '(1 2 3), explications plus tard).
(def my-grades '(1 2 3)) est donc une déclaration d'une liste immutable de 3 éléments nommée my-grades; on peut déclare des tableaux (mais mutables) facilement en js `const myGrades = [1, 2, 3];` en JS. [PARLER DE SYNTAXIC SUGAR]
Donc, quand on fait un appel de fonction comme (+ 1 2 3), véritablement, on crée une liste de 4 éléments avec le premier élément un symbole qui identifie la fonction, les trois autres éléments sont les arguments 1, 2 et 3
De même quand on fait appel à la macro def (explication plus tard de ce qu'est une macro), on dit liste avec symbole def, symbole correspondant au nom de la constante, et valeur de la constante.
Le language est consistent car la logique tout au long de sa conception, ce qui en fait un langage très agréable à utiliser.
- Les datatypes supportés (globalement les mêmes qu'en java) + tout un tas de data structures
- Les fonctions. Il est possible de déclarer des fonctions avec defn.
(defn my-+ [arg1 arg2]
(+ arg1 arg2))
décodage de ce langage particulier:
defn => macro utilisée pour déclarer une fonction. Declare function. Simple :)
my-+ => symbole utilisé pour nommer la fonction; ici c'est comme du java myPlus. Clojure supporte différents symboles dans les noms de fonction. Donc le + est une fonction comme les autres...
[arg1 arg2] : [] est utilisé pour décrire un vecteur. Nous parlerons des datastructures après mais grosso modo si () correspond à une LinkedList immutable en java, [] correspond à une ArrayList immutable en java.
donc [arg1 arg2] est un vecteur contenant 2 symboles ; arg1 et arg2 qui sont le noms de paramètres de cette fonction.
On a ensuite une liste qui correspond au body de la fonction, ici on fait simplement appel à la fonction built-in clojure +.
Pour utiliser notre fonction: (my-+ 1 2)
Attention on ne supporte que 2 arguments pour l'instant contrairement à + qui supporte un nombre variable d'arguments.
Si on essaye avec 3 on a une erreur du compilateur : (my-+ 1 2 3 4)
ArityException Wrong number of args (4) passed to: user/my-+ clojure.lang.AFn.throwArity
Donc pour déclarer n'importe quelle fonction en clojure, la syntaxe sera la suivante:
- liste constituée du symbole defn suivi du symbole du nom de la fonction suivi d'un vecteur de x arguments suivi d'x elements (https://clojuredocs.org/clojure.core/defn)
- on comprend à ce stade que le language lui même pour être écrit est une suite de datastructures imbriquées qui correspondent à un AST (https://en.wikipedia.org/wiki/Abstract_syntax_tree)
- cela signfie qu'il est très facile de parser du code clojure car la manière dont on écrit des programmes tout en restant symple est proche de sa représentation compilée.
Par défaut en clojure toutes les fonctions retournent une valeur (pas de void). La valeur retournée par défaut est nil (correspond au null java). C'est pour cela que si on marque (+ 1 2) alors le REPL retourne 3 tandis que (println "Hello, world") retourne nil.
Le body d'une fonction peut avoir un nombre varié d'éléments:
(defn say-hello [name]
(println "hello" name)
(println "Welcome to clojure")
name)
- en clojure, on utilise principalement 5 data structures:
- le vecteur, dès qu'on cherche à avoir une liste ordonnée d'éléments: [] [a b c] (pas de virgule)
il est possible de query un élément dans le tableau part index avec la fonction nth
(def my-grades [14 10 8 16 17])
(nth my-grades 0) => 14
(nth my-grades 3) => 16
il est possible d'accèder aux éléments du vecteur avec la fonction first et last
(first my-grades) => 14
(last my-grades) => 17
il n'est pas possible d'ajouter des éléments à ce vecteur. Mais il est possible de créer un vecteur à partir de ce vecteur. Par exemple, supposons qu'on veule ajouter un élément au vecteur, on utilisera la fonction conj.
(conj my-grades 3 14 15)
=> [14 10 8 16 17 3 14 15]
Mais => my-grades n'a pas été modifié
my-grades
=> [14 10 8 16 17]
Il est possible d'ajouter deux vecteurs ensemble avec la fonction concat
(concat my-grades my-grades)
=> (14 10 8 16 17 14 10 8 16 17)
Il y a pas mal d'autres opérations qu'on verra plus tard. Voir la cheatsheet qui est très bien faite (https://clojure.org/api/cheatsheet).
- la map, qui est le coeur du langage. En clojure, on sépare les fonctions des data structures; donc il n'y a pas de notion d'objet. La map est comme une map java, clés/valeurs, ou comme un objet javascript:
{"first-name" "James" "last-name" "Bond"} est une map (noter la syntaxe particulière, par de ',')
(def james {"first-name" "James" "last-name" "Bond"})
c'est comme faire ça en java:
Map<Object, Object> myMap = new HashMap<Object, Object>();
myMap.put("first-name", "James")
myMap.put("last-name", "Bond")
Il est possible de faire plein d'opérations sur les maps:
- query un élément avec la fonction get
(get james "last-name")
=> "Bond"
Il est possible de rajouter un élément à la map avec assoc la fonction ; mais cela ne la modifiera pas:
(assoc james "call-sign" 007)
=> {"first-name" "James", "last-name" "Bond", "call-sign" 7}
james
=> {"first-name" "James", "last-name" "Bond"}
Note: on aurait pu aussi utiliser conj comme ceci:
(conj james ["callsign" "007"])
les maps sont très puissantes en clojure et il y a plein de fonction built-in dessus. la aussi la cheatsheet est très utile.
Il existe un type de valeur spécifique en clojure, les keywords. Les keywords sont très utiles car ils permettent d'utiliser des noms plus simplement. Il s'agit simplement d'un symbole précédé d'un ":". Reprenons l'exemple précédent:
(def james {:first-name "James", :last-name "Bond"})
Il y a pas mal d'interraction entre les keywords et les maps, il sont donc très utilisés ensemble.
Par exemple ce shortcut:
(:last-name james)
=> "Bond"
Hé oui, en clojure keyword map correspond à (get keyword map). Bon à savoir !
- le set. Un set est un ensemble de valeurs uniques non ordonnées.
(def colors #{"red" "blue" "yellow"})
On peut ajouter une valeur à un set, ce qui crééra un nouveau set, mais le set ne conservera que les valeurs uniques dans tous les cas.
(conj colors "black")
=> #{"blue" "yellow" "red" "black"}
(conj colors "yellow")
=> #{"blue" "yellow" "red"}
Il est possible de savoir si un element existe dans un set pas par le biais de la fonction contains (qui est un peu particulière ;) ; la fonction contains? ne doit pas être utilisée pour les autres DS enfin pas dans le sens attendu à éclaircir. The members of a set are the keys of those elements.
(contains? colors "yellow")
=> true
- la lazy seq, qui n'a pas de syntaxic sugar. Elle est très utile, mais assez différente des autres, et nous en reparlerons. Juste : elle est lazy ce qui signifie qu'elle n'est pas évaluée si pas besoin.
- la liste, que nous avons vu. La liste est surtout utilisée pour faire des appels de fonctions et de macros (la forme utilisée est toujours (ma-fonction-ou-macro mes-arguments))
Clojure étant un langage non-type, il est possible de combiner à loisir ces éléments
(def filmography
{:name "James Bond",
:actors ["Daniel Craig" "Sean Connery" "Pierce Brosnan"]
:callsigns #{"007"}
:films {"Terrence Young" [{:name "Dr.No" :year 1962}
{:name "From Russia With Love" :year 1963}
{:name "Thunderball" :year 1965}]}
"Guy Hamilton" [{:name "Goldfinger" :year 1964}
{:name "Diamonds Are Forever" :year 1971}
{:name "Live And Let Die" :year 1973}
{:name "The Man With The Golden Gun" :year 1974}]})
C'est lisible. Ca ressemble à du json ou à du xml. Mais c'est du code, et c'est une des vraies force du langage.
opérations sur les films:
- count number of films
- get each years of movies
- get directors
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment