1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
|
---
language: clojure
filename: learnclojure-pt.clj
contributors:
- ["Adam Bard", "http://adambard.com/"]
translators:
- ["Mariane Siqueira Machado", "https://twitter.com/mariane_sm"]
- ["Ygor Sad", "https://github.com/ysads"]
lang: pt-br
---
Clojure é uma linguagem da família do Lisp desenvolvida para a JVM (máquina virtual Java). Possui uma ênfase muito mais forte em [programação funcional](https://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_funcional) pura do que Common Lisp, mas inclui diversos recursos [STM](https://en.wikipedia.org/wiki/Software_transactional_memory) para lidar com estado e mutabilidade, caso isso seja necessário.
Essa combinação permite gerenciar processamento concorrente de maneira muito simples - frequentemente, de modo automático.
(Sua versão de clojure precisa ser pelo menos 1.2)
```clojure
; Comentários começam por ponto e vírgula
; Código Clojure é escrito em formas - 'forms', em inglês. Tais estruturas são
; simplesmente listas de valores encapsuladas dentro de parênteses, separados por
; espaços em branco.
; Ao interpretar um código em Clojure, o interpretador ou leitor - do inglês 'reader' - assume
; que o primeiro valor dentro de uma forma é uma função ou macro, de modo que os demais valores
; são seus argumentos. Isso se deve ao fato de que Clojure, por ser uma derivação de Lisp,
; usa notação prefixa (ou polonesa).
; Num arquivo, a primeira chamada deve ser sempre para a função ns,
; que é responsável por definir em qual namespace o código em questão
; deve ser alocado
(ns learnclojure)
; Alguns exemplos básicos:
; Aqui, str é uma função e "Olá" " " e "Mundo" são seus argumentos. O que ela faz é criar
; uma string concatenando seus argumentos.
(str "Olá" " " "Mundo") ; => "Olá Mundo"
; Note que espaços em branco separam os argumentos de uma função. Opcionalmente vírgulas
; podem ser usadas, se você quiser.
(str, "Olá", " ", "Mundo") ; => "Olá Mundo"
; As operações matemáticas básicas usam os operadores de sempre
(+ 1 1) ; => 2
(- 2 1) ; => 1
(* 1 2) ; => 2
(/ 2 1) ; => 2
; Esses operadores aceitam um número arbitrário de argumentos
(+ 2 2 2) ; = 2 + 2 + 2 => 6
(- 5 1 1) ; = 5 - 1 - 1 => 3
(* 3 3 3 3) ; = 3 * 3 * 3 * 3 => 81
; Para verificar se dois valores são iguais, o operador = pode ser usado
(= 1 1) ; => true
(= 2 1) ; => false
; Para saber se dois valores são diferentes
(not= 1 2) ; => true
(not (= 1 2)) ; => true
; Conforme vimos acima, é possível aninhar duas formas
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2
(* (- 3 2) (+ 1 2)) ; = (3 - 2) * (1 + 2) => 3
; Se a leitura ficar comprometida, as fórmulas também podem ser escritas em múltiplas linhas
(* (- 3 2)
(+ 1 2)) ; => 3
(*
(- 3 2)
(+ 1 2)) ; => 3
; Tipos
;;;;;;;;;;;;;
; Por ter interoperabilidade com Java, Clojure usa os tipos de objetos de Java para booleanos,
; strings e números. Para descobrir qual o tipo de um valor, você pode usar a função `class`:
(class 1234) ; Literais Integer são java.lang.Long por padrão
(class 1.50) ; Literais Float são java.lang.Double
(class "oi") ; Strings sempre usam aspas duplas e são java.lang.String
(class false) ; Booleanos são java.lang.Boolean
; Tenha cuidado, ao dividir valores inteiros:
(= (/ 1 2)
(/ 1.0 2.0)) ; => false
(class (/ 1 2)) ; => clojure.lang.Ratio
(class (/ 1.0 2.0)) ; => java.lang.Double
; Aqui temos uma diferença em relação a Java, pois valores nulos são representados por `nil`
(class nil) ; nil
; Coleções e sequências
;;;;;;;;;;;;;;;;;;;
; Os dois tipos básicos de coleção são listas - "list" em inglês - e vetores - "vectors"
; no original. A principal diferença entre eles se
; dá pela implementação:
; - Vetores são implementados como arrays
; - Listas são listas ligadas
(class [1 2 3]) ; => clojure.lang.PersistentVector
(class '(1 2 3)) ; => clojure.lang.PersistentList
; Outra forma de declarar listas é usando a função list
(list 1 2 3) ; => '(1 2 3)
; Clojure classifica conjuntos de dados de duas maneiras
; "Coleções" são grupos simples de dados
; Tanto listas quanto vetores são coleções:
(coll? '(1 2 3)) ; => true
(coll? [1 2 3]) ; => true
; "Sequências" (seqs) são descrições abstratas de listas de dados.
; Sequências - ou seqs - são conjuntos de dados com avaliação "lazy"
; Apenas listas são seqs:
(seq? '(1 2 3)) ; => true
(seq? [1 2 3]) ; => false
; Ter avaliação lazy significa que uma seq somente precisa prover uma informação quando
; ela for requisitada. Isso permite às seqs representar listas infinitas.
(range) ; => (0 1 2 3 4 ...)
(cycle [1 2]) ; => (1 2 1 2 1 2 ...)
(take 4 (range)) ; => (0 1 2 3)
; A função cons é usada para adicionar um item ao início de uma lista ou vetor:
(cons 4 [1 2 3]) ; => (4 1 2 3)
(cons 4 '(1 2 3)) ; => (4 1 2 3)
; Já conj adiciona um item em uma coleção sempre do jeito mais eficiente.
; Em listas, isso significa inserir no início. Já em vetores, ao final.
(conj [1 2 3] 4) ; => [1 2 3 4]
(conj '(1 2 3) 4) ; => (4 1 2 3)
; Concatenação de coleções pode ser feita usando concat. Note que ela sempre gera uma
; seq como resultado e está sujeita a problemas de perfomance em coleções grandes, por
; conta da natureza lazy das seqs.
(concat '(1 2) [3 4]) ; => (1 2 3 4)
(concat [1 2] '(3 4)) ; => (1 2 3 4)
; Outra forma de concatenar coleções é usando into. Ela não está sujeita a problemas
; com a avaliação lazy, mas o resultado final da ordem e do tipo dos argumentos passados
(into [1 2] '(3 4)) ; => [1 2 3 4]
(into '(1 2) [3 4]) ; => (4 3 1 2)
; Note que em into a ordem dos parâmetros influencia a coleção final.
(into [1 2] '(3 4)) ; => (1 2 3 4)
(into '(1 2) [3 4]) ; => (4 3 1 2)
; As funções filter e map podem ser usadas para interagir com as coleções. Repare que
; elas sempre retornam seqs, independentemente do tipo do seu argumento.
(map inc [1 2 3]) ; => (2 3 4)
(filter even? [1 2 3 4]) ; => (2 4)
; Use reduce reduzir coleções a um único valor. Também é possível passar um argumento
; para o valor inicial das operações
(reduce + [1 2 3]) ; = (+ (+ (+ 1 2) 3) 4) => 10
(reduce + 10 [1 2 3 4]) ; = (+ (+ (+ (+ 10 1) 2) 3) 4) => 20
(reduce conj [] '(3 2 1)) ; = (conj (conj (conj [] 3) 2) 1) => [3 2 1]
; Reparou na semelhança entre listas e as chamadas de código Clojure? Isso se deve ao
; fato de que todo código clojure é escrito usando listas. É por isso que elas sempre
; são declaradas com o caracter ' na frente. Dessa forma o interpretador não tenta
; avaliá-las.
'(+ 2 3) ; cria uma lista com os elementos +, 2 e 3
(+ 2 3) ; o interpretador chama a função + passando como argumentos 2 e 3
; Note que ' é apenas uma abreviação para a função quote.
(quote (1 2 3)) ; => '(1 2 3)
; É possível passar uma lista para que o interpretador a avalie. Note que isso está
; sujeito ao primeiro elemento da lista ser um literal com um nome de uma função válida.
(eval '(+ 2 3)) ; => 5
(eval '(1 2 3)) ; dá erro pois o interpretador tenta chamar a função 1, que não existe
; Funções
;;;;;;;;;;;;;;;;;;;;;
; Use fn para criar novas funções. Uma função sempre retorna sua última expressão.
(fn [] "Olá Mundo") ; => fn
; Para executar suas funções, é preciso chamá-las, envolvendo-as em parênteses.
((fn [] "Olá Mundo")) ; => "Olá Mundo"
; Como isso não é muito prático, você pode nomear funções atribuindo elas a literais.
; Isso torna muito mais fácil chamá-las:
(def ola-mundo (fn [] "Olá Mundo")) ; => fn
(ola-mundo) ; => "Olá Mundo"
; Você pode abreviar esse processo usando defn:
(defn ola-mundo [] "Olá Mundo")
; Uma função pode receber uma lista de argumentos:
(defn ola
[nome]
(str "Olá " nome))
(ola "Jonas") ; => "Olá Jonas"
; É possível criar funções que recebam multivariadas, isto é, que aceitam números
; diferentes de argumentos:
(defn soma
([] 0)
([a] a)
([a b] (+ a b)))
(soma) ; => 0
(soma 1) ; => 1
(soma 1 2) ; => 3
; Funções podem agrupar argumentos extras em uma seq:
(defn conta-args
[& args]
(str "Você passou " (count args) " argumentos: " args))
(conta-args 1 2 3 4) ; => "Você passou 4 argumentos: (1 2 3 4)"
; Você pode misturar argumentos regulares e argumentos em seq:
(defn ola-e-conta
[nome & args]
(str "Olá " nome ", você passou " (count args) " argumentos extras"))
(ola-e-conta "Maria" 1 2 3 4) ; => "Olá Maria, você passou 4 argumentos extras"
; Nos exemplos acima usamos def para associar nomes a funções, mas poderíamos usá-lo
; para associar nomes a quaisquer valores:
(def xis :x)
xis ; => :x
; Inclusive, tais literais podem possuir alguns caracteres não usuais em outras linguagens:
(def *num-resposta* 42)
(def conexao-ativa? true)
(def grito-de-medo! "AAAAAAA")
(def ->vector-vazio [])
; É possível, inclusive, criar apelidos a nomes que já existem:
(def somar! soma)
(somar! 41 1) ; => 42
; Uma forma rápida de criar funções é por meio de funções anônimas. Elas são ótimas
; para manipulação de coleções e seqs, já que podem ser passadas para map, filter
; e reduce. Nessas funções, % é substituído por cada um dos items na seq ou na coleção:
(filter #(not= % nil) ["Joaquim" nil "Maria" nil "Antônio"]) ; => ("Joaquim" "Maria" "Antônio")
(map #(* % (+ % 2)) [1 2]) ; => (3 8)
; Mapas
;;;;;;;;;;
; Existem dois tipos de mapas: hash maps e array maps. Ambos compartilham uma mesma
; interface e funções. Hash maps são mais rápidos para retornar dados, mas não mantém
; as chaves ordenadas.
(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap
(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap
; Clojure converte automaticamente array maps em hash maps, por meio da maioria das
; funções de manipulação de mapas, caso eles fiquem grandes o suficiente. Não é
; preciso se preocupar com isso.
; Chaves podem ser qualquer valor do qual possa ser obtido um hash, mas normalmente
; usam-se keywords como chave, por possuírem algumas vantagens.
(class :a) ; => clojure.lang.Keyword
; Keywords são como strings, porém, duas keywords de mesmo valor são sempre armazenadas
; na mesma posição de memória, o que as torna mais eficientes.
(identical? :a :a) ; => true
(identical? (String. "a") (String. "a")) ; => false
(def mapa-strings {"a" 1 "b" 2 "c" 3})
mapa-strings ; => {"a" 1, "b" 2, "c" 3}
(def mapa-keywords {:a 1 :b 2 :c 3})
mapa-keywords ; => {:a 1, :c 3, :b 2}
; Você pode usar um mapa como função para recuperar um valor dele:
(mapa-strings "a") ; => 1
(mapa-keywords :a) ; => 1
; Se a chave buscada for uma keyword, ela também pode ser usada como função para recuperar
; valores. Note que isso não funciona com strings.
(:b mapa-keywords) ; => 2
("b" mapa-strings) ; => java.lang.String cannot be cast to clojure.lang.IFn
; Se você buscar uma chave que não existe, Clojure retorna nil:
(mapa-strings "d") ; => nil
; Use assoc para adicionar novas chaves em um mapa.
(def mapa-keywords-estendido (assoc mapa-keywords :d 4))
mapa-keywords-estendido ; => {:a 1, :b 2, :c 3, :d 4}
; Mas lembre-se que tipos em Clojure são sempre imutáveis! Isso significa que o mapa
; inicial continua com as mesmas informações e um novo mapa, com mais dados, é criado
; a partir dele
mapa-keywords ; => {:a 1, :b 2, :c 3}
; assoc também pode ser usado para atualizar chaves:
(def outro-mapa-keywords (assoc mapa-keywords :a 0))
outro-mapa-keywords ; => {:a 0, :b 2, :c 3}
; Use dissoc para remover chaves
(dissoc mapa-keywords :a :b) ; => {:c 3}
; Mapas também são coleções - mas não seqs!
(coll? mapa-keywords) ; => true
(seq? mapa-keywords) ; => false
; É possível usar filter, map e qualquer outra função de coleções em mapas.
; Porém a cada iteração um vetor no formato [chave valor] vai ser passado como
; argumento. Por isso é conveniente usar funções anônimas.
(filter #(odd? (second %)) mapa-keywords) ; => ([:a 1] [:c 3])
(map #(inc (second %)) mapa-keywords) ; => (2 3 4)
; Conjuntos
;;;;;;
; Conjuntos são um tipo especial de coleções que não permitem elementos repetidos.
; Eles podem ser criados com #{} ou com a função set.
(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}
(class #{1 2 3}) ; => clojure.lang.PersistentHashSet
; Note que nem sempre um set vai armazenar seus elementos na ordem esperada.
(def meu-conjunto #{1 2 3})
meu-conjunto ; => #{1 3 2}
; Adição funciona normalmente com conj.
(conj meu-conjunto 4) ; => #{1 4 3 2}
; Remoção, no entanto, precisa ser feita com disj:
(disj meu-conjunto 1) ; => #{3 2}
; Para saber se um elemento está em um conjunto, use-o como função. Nesse aspecto
; conjuntos funcionam de maneira semelhante a mapas.
(meu-conjunto 1) ; => 1
(meu-conjunto 4) ; => nil
; Condicionais e blocos
;;;;;;;;;;;;;;;;;
; Você pode usar um bloco let para criar um escopo local, no qual estarão disponíveis
; os nomes que você definir:
(let [a 1 b 2]
(+ a b)) ; => 3
(let [cores {:yellow "Amarelo" :blue "Azul"}
nova-cor :red
nome-cor "Vermelho"]
(assoc cores nova-cor nome-cor)) ; => {:yellow "Amarelo", :blue "Azul", :red "Vermelho"}
; Formas do tipo if aceitam três argumentos: a condição de teste, o comando a ser
; executado caso a condição seja positiva; e o comando para o caso de ela ser falsa.
(if true "a" "b") ; => "a"
(if false "a" "b") ; => "b"
; Opcionalmente você pode não passar o último argumento, mas se a condição for falsa
; o if vai retornar nil.
(if false "a") ; => nil
; A forma if somente aceita um comando para ser executado em cada caso. Se você
; precisar executar mais comandos, você pode usar a função do:
(if true
(do
(print "Olá ")
(print "Mundo"))) ; => escreve "Olá Mundo" na saída
; Se você só deseja tratar o caso de sua condição ser verdadeira, o comando when é
; uma alternativa melhor. Seu comportamento é idêntico a um if sem condição negativa.
; Uma de suas vantagens é permitir a execução de vários comandos sem exigir do:
(when true "a") ; => "a"
(when true
(print "Olá ")
(print "Mundo")) ; => também escreve "Olá Mundo" na saída
; Isso ocorre porque when possui um bloco do implícito. O mesmo se aplica a funções e
; comandos let:
(defn escreve-e-diz-xis
[nome]
(print "Diga xis, " nome)
(str "Olá " nome))
(escreve-e-diz-xis "João") ;=> "Olá João", além de escrever "Diga xis, João" na saída.
(let [nome "Nara"]
(print "Diga xis, " nome)
(str "Olá " nome)) ;=> "Olá João", além de escrever "Diga xis, João" na saída.
; Módulos
;;;;;;;;;;;;;;;
; Você pode usar a função use para carregar todas as funções de um módulo.
(use 'clojure.set)
; Agora nós podemos usar operações de conjuntos definidas nesse módulo:
(intersection #{1 2 3} #{2 3 4}) ; => #{2 3}
(difference #{1 2 3} #{2 3 4}) ; => #{1}
; Isso porém não é uma boa prática pois dificulta saber de qual módulo cada função
; veio, além de expor o código a conflitos de nomes, caso dois módulos diferentes
; definam funções com o mesmo nome. A melhor forma de referenciar módulos é por meio
; de require:
(require 'clojure.string)
; Com isso podemos chamar as funções de clojure.string usando o operador /
; Aqui, o módulo é clojure.string e a função é blank?
(clojure.string/blank? "") ; => true
; Porém isso não é muito prático, por isso é possível dar para um nome mais curto para
; o módulo ao carregá-lo:
(require '[clojure.string :as str])
(str/replace "alguém quer teste?" #"[aeiou]" str/upper-case) ; => "AlgUém qUEr tEstE?"
; Nesse exemplo usamos também a construção #"", que delimita uma expressão regular.
; É possível carregar outros módulos direto na definição do namespace. Note que nesse
; contexto não é preciso usar ' antes do vetor que define a importação do módulo.
(ns test
(:require
[clojure.string :as str]
[clojure.set :as set]))
; Operadores thread
;;;;;;;;;;;;;;;;;
; Uma das funções mais interessantes de clojure são os operadores -> e ->> - respectivamente
; thread-first e thread-last macros. Elas permitem o encadeamento de chamadas de funções,
; sendo perfeitas para melhorar a legibilidade em transformações de dados.
; -> usa o resultado de uma chamada como o primeiro argumento da chamada à função seguinte:
(-> " uMa StRIng com! aLG_uNs ##problemas. "
(str/replace #"[!#_]" "")
(str/replace #"\s+" " ")
str/trim ; se a função só aceitar um argumento, não é preciso usar parênteses
(str/lower-case)) ; => "uma string com alguns problemas."
; Na thread uma string com vários problemas foi passada como primeiro argumento à função
; str/replace, que criou uma nova string, a partir da original, porém somente com caracteres
; alfabéticos. Essa nova string foi passada como primeiro argumento para a chamada str/replace
; seguinte, que criou uma nova string sem espaços duplos. Essa nova string foi então passada
; como primeiro argumento para str/trim, que removeu espaços de seu início e fim, passando essa
; última string para str/lower-case, que a converteu para caracteres em caixa baixa.
; ->> é equivalente a ->, porém o retorno de cada função é passado como último argumento da
; função seguinte. Isso é particularmente útil para lidar com seqs, já que as funções que
; as manipulam sempre as tomam como último argumento.
(->> '(1 2 3 4)
(filter even?) ; => '(2 4)
(map inc) ; => '(3 5)
(reduce *)) ; => 15
; Java
;;;;;;;;;;;;;;;;;
; A biblioteca padrão de Java é enorme e possui inúmeros algoritmos e estruturas de
; dados já implementados. Por isso é bastante conveniente saber como usá-la dentro
; de Clojure.
; Use import para carregar um módulo Java.
(import java.util.Date)
; Você pode importar classes Java dentro de ns também:
(ns test
(:import java.util.Date
java.util.Calendar
java.util.ArrayList))
; Use o nome da clase com um "." no final para criar uma nova instância
(def instante (Date.))
(class instante) => ; java.util.Date
; Para chamar um método, use o operador . com o nome do método. Outra forma é
; usar simplesmente .<nome do método>
(. instante getTime) ; => retorna um inteiro representando o instante
(.getTime instante) ; => exatamente o mesmo que acima
; Para chamar métodos estáticos dentro de classes Java, use /
(System/currentTimeMillis) ; => retorna um timestamp
; Note que não é preciso importar o módulo System, pois ele está sempre presente
; Caso queira submeter uma instância de uma classe mutável a uma sequência de operações,
; você pode usar a função doto. Ela é funciona de maneira semelhante à função -> - ou
; thread-first -, exceto pelo fato de que ele opera com valores mutáveis.
(doto (java.util.ArrayList.)
(.add 11)
(.add 3)
(.add 7)
(java.util.Collections/sort)) ; => #<ArrayList [3, 7, 11]>
; STM
;;;;;;;;;;;;;;;;;
; Até aqui usamos def para associar nomes a valores. Isso, no entanto, possui algumas
; limitações, já que, uma vez definido essa associação, não podemos alterar o valor
; para o qual um nome aponta. Isso significa que nomes definidos com def não se
; comportam como as variáveis de outras linguagens.
; Para lidar com estado persistente e mutação de valores, Clojure usa o mecanismo Software
; Transactional Memory. O atom é o mais simples de todos. Passe pra ele um valor inicial e
; e ele criará um objeto que é seguro de atualizar:
(def atom-mapa (atom {}))
; Para acessar o valor de um atom, você pode usar a função deref ou o operador @:
@atom-mapa ; => {}
(deref atom-mapa) ; => {}
; Para mudar o valor de um atom, você deve usar a função swap!
; O que ela faz é chamar a função passada usando o atom como seu primeiro argumento. Com
; isso, ela altera o valor do atom de maneira segura.
(swap! atom-mapa assoc :a 1) ; Atribui a atom-mapa o resultado de (assoc {} :a 1)
(swap! atom-mapa assoc :b 2) ; Atribui a atom-mapa o resultado de (assoc {:a 1} :b 2)
; Observe que essas chamadas alteraram de fato o valor de atom-mapa. Seu novo valor é:
@atom-mapa ; => {:a 1 :b 2}
; Isso é diferente de fazer:
(def atom-mapa-2 (atom {}))
(def atom-mapa-3 (assoc @atom-mapa-2 :a 1))
; Nesse exemplo, atom-mapa-2 permanece com o seu valor original e é gerado um novo mapa,
; atom-mapa-3, que contém o valor de atom-mapa-2 atualizado. Note que atom-mapa-3 é um
; simples mapa, e não uma instância de um atom
@atom-mapa-2 ; => {}
atom-mapa-3 ; => {:a 1}
(class atom-mapa-2) ; => clojure.lang.Atom
(class atom-mapa-3) ; => clojure.lang.PersistentArrayMap
; A ideia é que o valor do atom só será atualizado se, após ser executada a função passada
; para swap!, o atom ainda estiver com o mesmo valor de antes. Isto é, se durante a execução
; da função alguém alterar o valor do atom, swap! reexecutará a função recebida usando o valor
; atual do átoma como argumento.
; Isso é ótimo em situações nas quais é preciso garantir a consistência de algum valor - tais
; como sistemas bancários e sites de compra. Para mais exemplos e informações sobre outras
; construções STM:
; Exemplos e aplicações: https://www.braveclojure.com/zombie-metaphysics/
; Refs: http://clojure.org/refs
; Agents: http://clojure.org/agents
```
### Leitura adicional
Esse tutorial está longe de ser completo, mas deve ser suficiente para que você possa dar seus primeiros passos em Clojure.
Caso queira aprender mais:
* clojure.org tem vários artigos:
[http://clojure.org/](http://clojure.org/)
* Brave Clojure possui um e-book que explora em profundidade diversos recursos de clojure, incluindo ótimos exemplos:
[https://www.braveclojure.com/](https://www.braveclojure.com/)
* clojuredocs.org tem documentação com exemplos para quase todas as funções principais (pertecentes ao core):
[http://clojuredocs.org/quickref/Clojure%20Core](http://clojuredocs.org/quickref/Clojure%20Core)
* 4clojure possui alguns problemas e desafios interessantes para quem quiser treinar clojure ou programação funcional:
[https://4clojure.oxal.org/](https://4clojure.oxal.org/)
* clojure-doc.org tem um bom número de artigos para iniciantes:
[http://clojure-doc.org/](http://clojure-doc.org/)
Clojure for the Brave and True é um livro de introdução ao Clojure e possui uma versão gratuita online:
[https://www.braveclojure.com/clojure-for-the-brave-and-true/](https://www.braveclojure.com/clojure-for-the-brave-and-true/)
|