Merge branch 'main' of git.sr.ht:~marcuskammer/emacs.d
This commit is contained in:
commit
39f9d6c993
586 changed files with 277787 additions and 88 deletions
|
@ -1,66 +1,3 @@
|
|||
;; (defun mk-show-modeline ()
|
||||
;; (interactive)
|
||||
;; (setq mode-line-format
|
||||
;; '(("-" mode-line-mule-info
|
||||
;; mode-line-modified
|
||||
;; mode-line-frame-identification
|
||||
;; mode-line-buffer-identification " "
|
||||
;; mode-line-position
|
||||
;; mode-line-modes
|
||||
;; (which-func-mode
|
||||
;; ("" which-func-format "--"))
|
||||
;; (global-mode-string
|
||||
;; ("--" global-mode-string)) "-%-")))
|
||||
;; (defvar mode-line-format-current
|
||||
;; (symbol-value 'mode-line-format)))
|
||||
|
||||
;; (defun mk-hide-modeline ()
|
||||
;; (interactive)
|
||||
;; (setq mode-line-format nil))
|
||||
|
||||
;; (defun mk-write-mode-enable ()
|
||||
;; (setq olivetti-body-width 73)
|
||||
;; (olivetti-mode)
|
||||
;; (mk-hide-modeline))
|
||||
|
||||
;; (defun mk-write-mode-disable ()
|
||||
;; (olivetti-mode)
|
||||
;; (mk-show-modeline))
|
||||
|
||||
(defun me/split-windows-horizontal (count-windows)
|
||||
"Split windows horizontal by equal width."
|
||||
(interactive "nHow many splits? ")
|
||||
(delete-other-windows)
|
||||
(let ((width (/ (window-total-width) count-windows)))
|
||||
(dotimes (i (1- count-windows))
|
||||
(split-window-right (- width)))))
|
||||
|
||||
(defun me/split-windows-vertical (count-windows)
|
||||
"Split windows vertical by equal width."
|
||||
(interactive "nHow many splits? ")
|
||||
(delete-other-windows)
|
||||
(let ((height (/ (window-total-height) count-windows)))
|
||||
(dotimes (i (1- count-windows))
|
||||
(split-window-below (- height)))))
|
||||
|
||||
(defun me/split-h3 ()
|
||||
(interactive)
|
||||
(me/split-windows-horizontal 3))
|
||||
|
||||
(defun me/split-v3 ()
|
||||
(interactive)
|
||||
(me/split-windows-vertical 3))
|
||||
|
||||
;; Set transparency of emacs
|
||||
(defun transparency (value)
|
||||
"Sets the transparency of the frame window. 0=transparent/100=opaque"
|
||||
(interactive "nTransparency Value 0 - 100 opaque: ")
|
||||
(set-frame-parameter (selected-frame) 'alpha value))
|
||||
|
||||
;; (if (display-graphic-p)
|
||||
;; (add-hook 'after-init-hook (lambda () (org-agenda-list) (me/split-h3)))
|
||||
;; (org-agenda-list))
|
||||
|
||||
(defvar mk/useful-websites
|
||||
'(("https://regexr.com/" regex debug)
|
||||
("https://regex101.com/" regex debug)
|
||||
|
@ -90,22 +27,86 @@
|
|||
("<.+>" "matching html")
|
||||
("<\/?(?:p|a|b|img)(?: \/)?>" "matchig specific tags")))
|
||||
|
||||
(defun me/list-files (folder suffix)
|
||||
;; (defun mk-show-modeline ()
|
||||
;; (interactive)
|
||||
;; (setq mode-line-format
|
||||
;; '(("-" mode-line-mule-info
|
||||
;; mode-line-modified
|
||||
;; mode-line-frame-identification
|
||||
;; mode-line-buffer-identification " "
|
||||
;; mode-line-position
|
||||
;; mode-line-modes
|
||||
;; (which-func-mode
|
||||
;; ("" which-func-format "--"))
|
||||
;; (global-mode-string
|
||||
;; ("--" global-mode-string)) "-%-")))
|
||||
;; (defvar mode-line-format-current
|
||||
;; (symbol-value 'mode-line-format)))
|
||||
|
||||
;; (defun mk-hide-modeline ()
|
||||
;; (interactive)
|
||||
;; (setq mode-line-format nil))
|
||||
|
||||
;; (defun mk-write-mode-enable ()
|
||||
;; (setq olivetti-body-width 73)
|
||||
;; (olivetti-mode)
|
||||
;; (mk-hide-modeline))
|
||||
|
||||
;; (defun mk-write-mode-disable ()
|
||||
;; (olivetti-mode)
|
||||
;; (mk-show-modeline))
|
||||
|
||||
(defun mk/split-windows-horizontal (count-windows)
|
||||
"Split windows horizontal by equal width."
|
||||
(interactive "nHow many splits? ")
|
||||
(delete-other-windows)
|
||||
(let ((width (/ (window-total-width) count-windows)))
|
||||
(dotimes (i (1- count-windows))
|
||||
(split-window-right (- width)))))
|
||||
|
||||
(defun mk/split-windows-vertical (count-windows)
|
||||
"Split windows vertical by equal width."
|
||||
(interactive "nHow many splits? ")
|
||||
(delete-other-windows)
|
||||
(let ((height (/ (window-total-height) count-windows)))
|
||||
(dotimes (i (1- count-windows))
|
||||
(split-window-below (- height)))))
|
||||
|
||||
(defun mk/split-h3 ()
|
||||
(interactive)
|
||||
(mk/split-windows-horizontal 3))
|
||||
|
||||
(defun mk/split-v3 ()
|
||||
(interactive)
|
||||
(mk/split-windows-vertical 3))
|
||||
|
||||
;; Set transparency of emacs
|
||||
(defun transparency (value)
|
||||
"Sets the transparency of the frame window. 0=transparent/100=opaque"
|
||||
(interactive "nTransparency Value 0 - 100 opaque: ")
|
||||
(set-frame-parameter (selected-frame) 'alpha value))
|
||||
|
||||
(defun mk/show-agenda-list ()
|
||||
(if (display-graphic-p)
|
||||
(add-hook 'after-init-hook (lambda () (org-agenda-list) (me/split-h3)))
|
||||
(org-agenda-list)))
|
||||
|
||||
(defun mk/list-files (folder suffix)
|
||||
(let ((regexp (concat "\\." suffix "$")))
|
||||
(directory-files folder nil regexp)))
|
||||
|
||||
(defun me/build-file-suffix ())
|
||||
(defun mk/build-file-suffix ())
|
||||
|
||||
(defun me/copy-files (src-dir dst-dir suffix)
|
||||
(defun mk/copy-files (src-dir dst-dir suffix)
|
||||
(let ((src-files '())
|
||||
(src-dir (expand-file-name src-dir))
|
||||
(dst-dir (expand-file-name dst-dir)))
|
||||
(dolist (file (me/list-files src-dir suffix) src-files)
|
||||
(dolist (file (mk/list-files src-dir suffix) src-files)
|
||||
(let ((src-file (expand-file-name (concat src-dir "/" file)))
|
||||
(dst-file (expand-file-name (concat dst-dir "/" file))))
|
||||
(add-to-list 'src-files src-file)
|
||||
(copy-file src-file dst-file t)))))
|
||||
|
||||
(defun me/delete-files (lst)
|
||||
(defun mk/delete-files (lst)
|
||||
(dolist (file lst)
|
||||
(delete-file file t)))
|
||||
|
|
|
@ -81,6 +81,8 @@
|
|||
("https://www.greenpeace.org/usa/rsslatest.xml" nature climate blog)
|
||||
("https://blog.rust-lang.org/feed.xml" rust blog news)
|
||||
("https://twobithistory.org/feed.xml" history blog coding)
|
||||
("http://planet.clojure.in/atom.xml" clojure news hacking jvm java)
|
||||
("https://taonaw.com/index.xml" emacs blog)
|
||||
("https://www.phoronix.com/rss.php" linux news blog)
|
||||
)
|
||||
elfeed-search-filter "@4-day-ago +unread"))
|
||||
|
|
|
@ -32,9 +32,14 @@
|
|||
(use-package nov
|
||||
:hook (nov-mode . visual-line-mode)
|
||||
:mode ("\\.epub\\'" . nov-mode)
|
||||
:init
|
||||
(add-hook 'nov-mode-hook #'shrface-mode)
|
||||
:config
|
||||
(setq nov-text-width 82
|
||||
nov-variable-pitch t)
|
||||
(require 'shrface)
|
||||
(setq nov-shr-rendering-functions '((img . nov-render-img) (title . nov-render-title)))
|
||||
(setq nov-shr-rendering-functions (append nov-shr-rendering-functions shr-external-rendering-functions))
|
||||
(when (eq system-type 'windows-nt)
|
||||
(when (directory-name-p "c:/msys64/")
|
||||
(setq nov-unzip-program "c:/msys64/usr/bin/unzip.exe"))))
|
||||
|
@ -87,20 +92,20 @@
|
|||
(string-match "/usr/src/linux" (buffer-file-name)))
|
||||
"linux"
|
||||
"gnu")))
|
||||
(add-hook 'c-mode-common-hook 'me/c-mode-style-hook)
|
||||
(defun me/c-mode-compile-command-hook ()
|
||||
;; if no makefile use gcc for compile command
|
||||
(unless (or (file-exists-p "makefile")
|
||||
(file-exists-p "Makefile"))
|
||||
(set (make-local-variable 'compile-command)
|
||||
(concat "gcc " "-Wall " "-g " buffer-file-name " -o "
|
||||
(file-name-sans-extension buffer-file-name)))))
|
||||
(add-hook 'c-mode-common-hook 'me/c-mode-compile-command-hook)
|
||||
(defun me/c-mode-compile-on-save ()
|
||||
;; compile on save hook
|
||||
(make-local-variable 'after-save-hook)
|
||||
(add-hook 'after-save-hook (lambda () (compile compile-command))))
|
||||
(add-hook 'c-mode-common-hook 'me/c-mode-compile-on-save))
|
||||
(add-hook 'c-mode-common-hook 'me/c-mode-style-hook))
|
||||
;; (defun me/c-mode-compile-command-hook ()
|
||||
;; ;; if no makefile use gcc for compile command
|
||||
;; (unless (or (file-exists-p "makefile")
|
||||
;; (file-exists-p "Makefile"))
|
||||
;; (set (make-local-variable 'compile-command)
|
||||
;; (concat "gcc " "-Wall " "-g " buffer-file-name " -o "
|
||||
;; (file-name-sans-extension buffer-file-name)))))
|
||||
;; (add-hook 'c-mode-common-hook 'me/c-mode-compile-command-hook)
|
||||
;; (defun me/c-mode-compile-on-save ()
|
||||
;; ;; compile on save hook
|
||||
;; (make-local-variable 'after-save-hook)
|
||||
;; (add-hook 'after-save-hook (lambda () (compile compile-command))))
|
||||
;; (add-hook 'c-mode-common-hook 'me/c-mode-compile-on-save))
|
||||
|
||||
(use-package js-mode
|
||||
:ensure nil
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
(width . 90)
|
||||
(height . 64)
|
||||
(alpha . 100)
|
||||
(horizontal-scroll-bars)
|
||||
(vertical-scroll-bars)))
|
||||
(horizontal-scroll-bars)))
|
||||
'(delete-by-moving-to-trash t)
|
||||
'(diary-file
|
||||
"d:/UserData/marcus.kammer/OneDrive - Siemens AG/documents/Diary/diary")
|
||||
|
@ -66,7 +65,7 @@
|
|||
'(org-roam-directory "d:/UserData/marcus.kammer/OneDrive - Siemens AG/org-roam/")
|
||||
'(org-web-tools-pandoc-sleep-time 1.0)
|
||||
'(package-selected-packages
|
||||
'(mpv subed ob-http ob-go powershell ox-reveal org-web-tools org-tree-slide org-roam elfeed erc-image magit go-translate simple-httpd slime racket-mode web-mode go-mode geiser-guile geiser-racket geiser eglot nov shrface vertico smartparens emojify ace-window olivetti rainbow-delimiters helpful doom-modeline doom-themes all-the-icons-dired all-the-icons use-package))
|
||||
'(cider mpv subed ob-http ob-go powershell ox-reveal org-web-tools org-tree-slide org-roam elfeed erc-image magit go-translate simple-httpd slime racket-mode web-mode go-mode geiser-guile geiser-racket geiser eglot nov shrface vertico smartparens emojify ace-window olivetti rainbow-delimiters helpful doom-modeline doom-themes all-the-icons-dired all-the-icons use-package))
|
||||
'(plantuml-default-exec-mode 'jar)
|
||||
'(plantuml-jar-path "~/AppData/Local/Programs/plantuml/plantuml.jar")
|
||||
'(python-indent-guess-indent-offset nil)
|
||||
|
@ -91,4 +90,4 @@
|
|||
;; Your init file should contain only one such instance.
|
||||
;; If there is more than one, they won't work right.
|
||||
'(font-lock-keyword-face ((t (:foreground "#81A1C1" :slant italic))))
|
||||
'(variable-pitch ((t (:height 1.3 :family "Roboto Condensed")))))
|
||||
'(variable-pitch ((t (:height 1.3 :family "Ubuntu Condensed")))))
|
||||
|
|
216
clones/clojure-doc.org/articles/about/index.html
Normal file
216
clones/clojure-doc.org/articles/about/index.html
Normal file
|
@ -0,0 +1,216 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: About</title>
|
||||
|
||||
|
||||
<meta name="description" content="CDS (Clojure Documentation Site) is a community documentation project for the Clojure programming language. It is not affiliated with
|
||||
Clojure/core, does not require going through the Clojure Contributor Agreement, and is developed on GitHub.Rationale">
|
||||
|
||||
<meta property="og:description" content="CDS (Clojure Documentation Site) is a community documentation project for the Clojure programming language. It is not affiliated with
|
||||
Clojure/core, does not require going through the Clojure Contributor Agreement, and is developed on GitHub.Rationale">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/about/" />
|
||||
<meta property="og:title" content="About" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/about/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>About</h2>
|
||||
</div>
|
||||
|
||||
<p>CDS (Clojure Documentation Site) is a community documentation project for the Clojure programming language. It is not affiliated with
|
||||
<code>Clojure/core</code>, does not require going through the Clojure Contributor Agreement, and is developed <a href="https://github.com/clojure-doc/clojure-doc.github.io">on GitHub</a>.</p><h2 id="rationale">Rationale</h2><p>The rationale is explained in more detail in the <a href="http://blog.clojurewerkz.org/blog/2012/10/10/announcing-a-new-clojure-documentation-project/">announcement blog post</a>.</p><h2 id="history">History</h2><p>CDS was started in early October, 2012, by several active members of the Clojure community due to their dissatisfaction
|
||||
with the state of documentation and documentation contribution process (that involved mailing Clojure Contributor Agreement in paper).</p><h2 id="goals">Goals</h2><p>The goal is to produce quality technical documentation for Clojure users and potential adopters with various expertise levels.</p><p>CDS strives to cover all aspects of Clojure: from tutorials and language guides to overview of the ecosystem, how
|
||||
libraries are developed and published, topics operations engineers will be interested in, JVM ecosystem tools
|
||||
and so on.</p><p>Adopting a language always takes more than just reading a book or a few tutorials about language features. Understanding
|
||||
design goals, the ecosystem and operations is just as important. CDS will try to address this.</p><h3 id="what-cds-is-not">What CDS is Not</h3><p>What's <em>not</em> here:</p><ul><li>Cheatsheets. The official <a href="http://clojure.org/cheatsheet">Clojure cheatsheet</a> is very good. There is also an unofficial <a href="https://github.com/fogus/clojurescript-cheatsheet">ClojureScript cheatsheet</a> available for download and contribution.</li><li>API reference docs. Those can currently be found (with examples) at <a href="http://clojuredocs.org/">Clojuredocs</a>.</li></ul><p>Clojuredocs needs a lot of work and redesign (as in, the way it works) which will take a while. CDS is not concerned with providing the API reference;
|
||||
only tutorials, guides, and linking to other relevant resources.</p><h2 id="structure">Structure</h2><p>CDS is structured as a number of guides. They broadly fall into 4 categories:</p><ul><li><a href="https://clojure-doc.org/articles/about/content/#clojure_tutorials">Tutorials</a></li><li><a href="https://clojure-doc.org/articles/about/content/#clojure_language_guides">Language Guides</a></li><li><a href="https://clojure-doc.org/articles/about/content/#the_clojure_ecosystem">Ecosystem & Tools</a></li><li><a href="https://clojure-doc.org/articles/about/content/#cookbooks">Cookbooks</a></li></ul><h3 id="tutorials">Tutorials</h3><p>These guides are for complete newcomers and should include a lot of hand holding. They don't assume any
|
||||
previous familiarity with Clojure, the JVM, the JVM tool ecosystem, functional programming, immutability, and so on.</p><p>Target audience: newcomers to the language.</p><h3 id="language-guides">Language guides</h3><p>These guides are more in-depth, focused on various aspects of the language and interoperability.
|
||||
Examples of such guides include:</p><ul><li>Sequences</li><li>Interoperability</li><li>Reference types</li><li>Laziness</li><li>Macros and compilation</li></ul><p>Target audience: from developers who already have some familiarity with the language to those who have been using it for
|
||||
a while.</p><h3 id="tools--ecosystem-guides">Tools & Ecosystem guides</h3><p>These guides cover key Clojure ecosystem tools such as <a href="http://leiningen.org">Leiningen</a>, <a href="http://clojars.org">Clojars</a>, <a href="https://github.com/trptcolin/reply">REPLy</a>,
|
||||
<a href="https://github.com/clojure/tools.nrepl">nREPL</a>, <a href="https://github.com/technomancy/clojure-mode">Emacs clojure-mode</a>, VimClojure, <a href="https://code.google.com/p/counterclockwise/">Counterclockwise</a>, <a href="http://plugins.jetbrains.com/plugin?pluginId=4050">La Clojure</a>, etc. It also covers important ecosystem projects that are not tools: books,
|
||||
<a href="http://www.clojuresphere.com/">ClojureSphere</a>, <a href="http://clojurewerkz.org/">ClojureWerkz</a>, <a href="https://github.com/flatland">Flatland</a> and so on.</p><p>Target audience: all developers using or interested in the Clojure programming language.</p><h3 id="cookbooks">Cookbooks</h3><p>Concise <a href="https://clojure-doc.org/articles/about/content/#cookbooks">Clojure example code</a>, categorized by subject.</p><h2 id="mailing-list">Mailing List</h2><p>CDS <a href="https://groups.google.com/group/clojure">currently uses Clojure mailing list</a> for discussions. Feel free to join it and ask any questions you may have.</p><h2 id="news--announcements">News & Announcements</h2><p>News and announcements are posted primarily on the <a href="https://clojurians.slack.com/archives/C8NUSGWG6">Clojurians Slack <code>#news-and-articles</code> channel</a>.</p><h2 id="reporting-issues">Reporting Issues</h2><p>If you find a mistake, poor grammar, an important topic not covered, or an outdated example, please <a href="https://github.com/clojure-doc/clojure-doc.github.io/issues">file an issue</a> on Github.</p><h2 id="contributing">Contributing</h2><p>CDS uses <a href="http://cryogenweb.org/">Cryogen</a>. All tutorials and guides are written in Markdown.</p><p>The toolchain and setup process are described <a href="https://github.com/clojure-doc/clojure-doc.github.io/blob/main/README.md#toolchain">in the README</a>.</p><p>To submit changes, create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request
|
||||
on GitHub.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
|
||||
|
||||
<a href="../content/index.html">Table of Contents »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="index.html">About</a></li>
|
||||
|
||||
<li><a href="../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
225
clones/clojure-doc.org/articles/content/index.html
Normal file
225
clones/clojure-doc.org/articles/content/index.html
Normal file
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,208 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Data Structures (Help wanted)</title>
|
||||
|
||||
|
||||
<meta name="description" content="Help wantedPlease follow the instructions on how to contribute and start writing over here">
|
||||
|
||||
<meta property="og:description" content="Help wantedPlease follow the instructions on how to contribute and start writing over here">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/cookbooks/data_structures/" />
|
||||
<meta property="og:title" content="Data Structures (Help wanted)" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/cookbooks/data_structures/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Data Structures (Help wanted)</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="help-wanted">Help wanted</h2><p>Please follow the <a href="https://github.com/clojure-doc/clojure-doc.github.io/tree/source#how-to-contribute">instructions</a> on how to contribute and start writing over <a href="https://github.com/clojure-doc/clojure-doc.github.io/blob/source/content/md/articles/cookbooks/data_structures.md">here</a></p><p>This cookbook covers working with core Clojure data structures.</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../../ecosystem/books/index.html">« Books about Clojure and ClojureScript</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../strings/index.html">Strings »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,208 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Date and Time (Help wanted)</title>
|
||||
|
||||
|
||||
<meta name="description" content="Help wantedPlease follow the instructions on how to contribute and start writing over here">
|
||||
|
||||
<meta property="og:description" content="Help wantedPlease follow the instructions on how to contribute and start writing over here">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/cookbooks/date_and_time/" />
|
||||
<meta property="og:title" content="Date and Time (Help wanted)" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/cookbooks/date_and_time/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Date and Time (Help wanted)</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="help-wanted">Help wanted</h2><p>Please follow the <a href="https://github.com/clojure-doc/clojure-doc.github.io/tree/source#how-to-contribute">instructions</a> on how to contribute and start writing over <a href="https://github.com/clojure-doc/clojure-doc.github.io/blob/source/content/md/articles/cookbooks/date_and_time.md">here</a></p><p>This cookbook covers working with date and time values in Clojure.</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../math/index.html">« Mathematics with Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../files_and_directories/index.html">Working with Files and Directories in Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,252 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Working with Files and Directories in Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This cookbook covers working with files and directories from Clojure,
|
||||
using functions in the clojure.java.io namespace as well as parts of
|
||||
the JDK via interoperability.This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.">
|
||||
|
||||
<meta property="og:description" content="This cookbook covers working with files and directories from Clojure,
|
||||
using functions in the clojure.java.io namespace as well as parts of
|
||||
the JDK via interoperability.This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/cookbooks/files_and_directories/" />
|
||||
<meta property="og:title" content="Working with Files and Directories in Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/cookbooks/files_and_directories/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Working with Files and Directories in Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This cookbook covers working with files and directories from Clojure,
|
||||
using functions in the <code>clojure.java.io</code> namespace as well as parts of
|
||||
the JDK via interoperability.</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="preliminaries">Preliminaries</h2><p>Note that for the examples below, "io" is an alias for
|
||||
<code>clojure.java.io</code>. That is, it's assumed your <code>ns</code> macro contains:</p><pre><code class="clojure">(:require [clojure.java.io :as io])
|
||||
</code></pre><p>or else in the repl you've loaded it:</p><pre><code class="clojure">(require '[clojure.java.io :as io])
|
||||
</code></pre><h2 id="recipes">Recipes</h2><h3 id="read-a-file-into-one-long-string">Read a file into one long string</h3><pre><code class="clojure">(def a-long-string (slurp "foo.txt"))
|
||||
</code></pre><p>Note, you can pass urls to <code>slurp</code> as well. See also <a href="http://clojuredocs.org/clojure_core/clojure.core/slurp">slurp at
|
||||
Clojuredocs</a>.</p><h3 id="read-a-file-one-line-at-a-time">Read a file one line at a time</h3><p>Suppose you'd like to call <code>my-func</code> on every line in a file,
|
||||
and return the resulting sequence:</p><pre><code class="clojure">(with-open [rdr (io/reader "foo.txt")]
|
||||
(doall (map my-func (line-seq rdr))))
|
||||
</code></pre><p>The <code>doall</code> is needed because the <code>map</code> call is lazy. The lines that
|
||||
<code>line-seq</code> gives you have no trailing newlines (and empty lines in the
|
||||
file will yield empty strings ("")).</p><h3 id="write-a-long-string-out-to-a-new-file">Write a long string out to a new file</h3><pre><code class="clojure">(spit "foo.txt"
|
||||
"A long
|
||||
multi-line string.
|
||||
Bye.")
|
||||
</code></pre><p>Overwrites the file if it already exists. To append, use</p><pre><code class="clojure">(spit "foo.txt" "file content" :append true)
|
||||
</code></pre><h3 id="write-a-file-one-line-at-a-time">Write a file one line at a time</h3><p>Suppose you'd like to write out every item in a vector, one item per
|
||||
line:</p><pre><code class="clojure">(with-open [wrtr (io/writer "foo.txt")]
|
||||
(doseq [i my-vec]
|
||||
(.write wrtr (str i "\n"))))
|
||||
</code></pre><h3 id="check-if-a-file-exists">Check if a file exists</h3><pre><code class="clojure">(.exists (io/file "filename.txt"))
|
||||
</code></pre><p>Is it a directory? :</p><pre><code class="clojure">(.isDirectory (io/file "path/to/something"))
|
||||
</code></pre><p>An io/file is a java.io.File object (a file or a directory). You can
|
||||
call a number of functions on it, including:</p><pre><code>exists Does the file exist?
|
||||
isDirectory Is the File object a directory?
|
||||
getName The basename of the file.
|
||||
getParent The dirname of the file.
|
||||
getPath Filename with directory.
|
||||
mkdir Create this directory on disk.
|
||||
</code></pre><p>To read about more available methods, see <a href="http://docs.oracle.com/javase/7/docs/api/java/io/File.html">the java.io.File
|
||||
docs</a>.</p><h3 id="get-a-list-of-the-files-and-dirs-in-a-given-directory">Get a list of the files and dirs in a given directory</h3><p>As <code>File</code> objects:</p><pre><code class="clojure">(.listFiles (io/file "path/to/some-dir"))
|
||||
</code></pre><p>Same, but just the <em>names</em> (strings), not File objects:</p><pre><code class="clojure">(.list (io/file "path/to/some-dir"))
|
||||
</code></pre><p>The results of those calls are seqable.</p><h2 id="see-also">See also</h2><ul><li><a href="https://github.com/Raynes/fs">https://github.com/Raynes/fs</a></li><li>the I/O section of the <a href="http://clojure.org/cheatsheet">cheatsheet</a></li></ul>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../date_and_time/index.html">« Date and Time (Help wanted)</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../middleware/index.html">Middleware in Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
252
clones/clojure-doc.org/articles/cookbooks/math/index.html
Normal file
252
clones/clojure-doc.org/articles/cookbooks/math/index.html
Normal file
|
@ -0,0 +1,252 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Mathematics with Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This cookbook covers working with mathematics in Clojure, using
|
||||
built-in functions, contrib libraries, and parts of the JDK via
|
||||
interoperability.This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.">
|
||||
|
||||
<meta property="og:description" content="This cookbook covers working with mathematics in Clojure, using
|
||||
built-in functions, contrib libraries, and parts of the JDK via
|
||||
interoperability.This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/cookbooks/math/" />
|
||||
<meta property="og:title" content="Mathematics with Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/cookbooks/math/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Mathematics with Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This cookbook covers working with mathematics in Clojure, using
|
||||
built-in functions, contrib libraries, and parts of the JDK via
|
||||
interoperability.</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="preliminaries">Preliminaries</h2><p>Some examples herein make use of the
|
||||
<a href="https://github.com/clojure/math.numeric-tower">math.numeric-tower</a>
|
||||
and
|
||||
<a href="https://github.com/clojure/math.combinatorics">math.combinatorics</a>
|
||||
contrib libraries. It's assumed that either you have the following in
|
||||
your source code's <code>ns</code> macro:</p><pre><code class="clojure">(:require [clojure.math.numeric-tower :as math]
|
||||
[clojure.math.combinatorics :as combo])
|
||||
</code></pre><p>or else in the repl you've loaded them like so:</p><pre><code class="clojure">(require '[clojure.math.numeric-tower :as math])
|
||||
(require '[clojure.math.combinatorics :as combo])
|
||||
</code></pre><h2 id="recipes">Recipes</h2><h3 id="simple-math">Simple Math</h3><pre><code class="clojure">(+ 3 4) ;=> 7
|
||||
(- 3 4) ;=> -1
|
||||
(* 3 4) ;=> 12
|
||||
(/ 3 4) ;=> 3/4 (an exact ratio)
|
||||
(/ 3.0 4) ;=> 0.75
|
||||
|
||||
(inc 5) ;=> 6
|
||||
(dec 5) ;=> 4
|
||||
</code></pre><p>For doing integer division and getting remainders (modulus), see the
|
||||
docs for
|
||||
<a href="http://clojuredocs.org/clojure_core/clojure.core/quot">quot</a>,
|
||||
<a href="http://clojuredocs.org/clojure_core/clojure.core/rem">rem</a>, and
|
||||
<a href="http://clojuredocs.org/clojure_core/clojure.core/mod">mod</a>.</p><p>For exponents, square roots, rounding, ceiling, floor, absolute value,
|
||||
and greatest/least common multiples, see the <a href="http://clojure.github.com/math.numeric-tower/">docs for
|
||||
math.numeric-tower</a>.</p><h3 id="trigonometry">Trigonometry</h3><p>Use what the Java platform provides, for example:</p><pre><code class="clojure">Math/PI ;=> 3.14159...
|
||||
(Math/sin x)
|
||||
(Math/cos x)
|
||||
(Math/tan x)
|
||||
</code></pre><p>There are many more functions available, which you can read about in
|
||||
the <a href="http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html">docs for
|
||||
java.lang.Math</a>.</p><h3 id="combinatorics">Combinatorics</h3><p>For combinatoric functions (such as <code>combinations</code> and
|
||||
<code>permutations</code>), see the <a href="http://clojure.github.com/math.combinatorics/">docs for
|
||||
math.combinatorics</a>.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../strings/index.html">« Strings</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../date_and_time/index.html">Date and Time (Help wanted) »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
326
clones/clojure-doc.org/articles/cookbooks/middleware/index.html
Normal file
326
clones/clojure-doc.org/articles/cookbooks/middleware/index.html
Normal file
|
@ -0,0 +1,326 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Middleware in Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.What is Middleware?">
|
||||
|
||||
<meta property="og:description" content="This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.What is Middleware?">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/cookbooks/middleware/" />
|
||||
<meta property="og:title" content="Middleware in Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/cookbooks/middleware/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Middleware in Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="what-is-middleware">What is Middleware?</h2><p>Middleware in Clojure is a common design pattern for threading a
|
||||
<em>request</em> through a series of functions designed to operate on it as
|
||||
well as threading the <em>response</em> through the same series of functions.</p><p>Middleware is used in many Clojure projects such as
|
||||
<a href="https://github.com/mmcgrana/ring">Ring</a>,
|
||||
<a href="https://github.com/dakrone/clj-http">clj-http</a> and
|
||||
<a href="https://clojure-doc.org/articles/cookbooks/middleware/TODO">something else here</a>.</p><h2 id="the-client-function">The <code>client</code> function</h2><p>The base of all middleware in Clojure is the <code>client</code> function, which
|
||||
takes a request object (usually a Clojure map) and returns a response
|
||||
object (also usually a Clojure map).</p><p>For example, let's use a <code>client</code> function that pulls some keys out of
|
||||
a map request and does an HTTP GET on a site:</p><pre><code class="clojure">(ns middleware.example
|
||||
(:require [clj-http.client :as http]))
|
||||
|
||||
(defn client [request]
|
||||
(http/get (:site request) (:options request)))
|
||||
</code></pre><p>To use the client method, call it like so (response shortened to fit
|
||||
here):</p><pre><code class="clojure">(client {:site "http://www.aoeu.com" :options {}})
|
||||
;; ⇒ {:status 200, :headers {...}, :request-time 3057, :body "..."}
|
||||
</code></pre><p>Now that a client function exists, middleware can be wrapped around it
|
||||
to change the <em>request</em>, the <em>response</em>, or both.</p><p>Let's start with a middleware function that doesn't do anything. We'll
|
||||
call it the <code>no-op</code> middleware:</p><pre><code class="clojure">;; It is standard convention to name middleware wrap-<something>
|
||||
(defn wrap-no-op
|
||||
;; the wrapping function takes a client function to be used...
|
||||
[client-fn]
|
||||
;; ...and returns a function that takes a request...
|
||||
(fn [request]
|
||||
;; ...that calls the client function with the request
|
||||
(client-fn request)))
|
||||
</code></pre><p>So how is this middleware used? First, it must be 'wrapped' around the
|
||||
existing client function:</p><pre><code class="clojure">(def new-client (wrap-no-op client))
|
||||
|
||||
;; Now new-client can be used just like the client function:
|
||||
(new-client {:site "http://www.aoeu.com" :options {}})
|
||||
;; ⇒ {:status 200, :headers {...}, :request-time 3057, :body "..."}
|
||||
</code></pre><p>It works! Now it's not very exiting because it doesn't do anything
|
||||
yet, so let's add another middleware wrapper that does something more
|
||||
exiting.</p><p>Let's add a middleware function that automatically changes all "HTTP"
|
||||
requests into "HTTPS" requests. Again, we need a function that returns
|
||||
another function, so we can end up with a new method to call:</p><pre><code class="clojure">(defn wrap-https
|
||||
[client-fn]
|
||||
(fn [request]
|
||||
(let [site (:site request)
|
||||
new-site (.replaceAll site "http:" "https:")
|
||||
new-request (assoc request :site new-site)]
|
||||
(client-fn new-request))))
|
||||
</code></pre><p>The <code>wrap-https</code> middleware can be tested again by creating a new
|
||||
client function:</p><pre><code class="clojure">(def https-client (wrap-https client))
|
||||
|
||||
;; Notice the :trace-redirects key shows that HTTPS was used instead
|
||||
;; of HTTP
|
||||
(https-client {:site "http://www.google.com" :options {}})
|
||||
;; ⇒ {:trace-redirects ["https://www.google.com"],
|
||||
;; :status 200,
|
||||
;; :headers {...},
|
||||
;; :request-time 3057,
|
||||
;; :body "..."}
|
||||
</code></pre><p>Middleware can be tested independently of the client function by
|
||||
providing the identity function (or any other function that returns a
|
||||
map). For example, we can see the <code>wrap-https</code> middleware returns the
|
||||
clojure map with the :site changed from 'http' to 'https':</p><pre><code class="clojure">((wrap-https identity) {:site "http://www.example.com"})
|
||||
;; ⇒ {:site "https://www.example.com"}
|
||||
</code></pre><h2 id="combining-middleware">Combining middleware</h2><p>In the previous example, we showed how to create and use middleware,
|
||||
but what about using multiple middleware functions? Let's define one
|
||||
more middleware so we have a total of three to work with. Here's the
|
||||
source for a middleware function that adds the current data to the
|
||||
response map:</p><pre><code class="clojure">(defn wrap-add-date
|
||||
[client]
|
||||
(fn [request]
|
||||
(let [response (client request)]
|
||||
(assoc response :date (java.util.Date.)))))
|
||||
</code></pre><p>And again, we can test it without using any other functions using
|
||||
<code>identity</code> as the client function:</p><pre><code class="clojure">((wrap-add-date identity) {})
|
||||
;; ⇒ {:date #inst "2012-11-09T12:41:05.171-00:00"}
|
||||
</code></pre><p>Middleware is useful on its own, but where it becomes truly more
|
||||
useful is in combining middleware together. Here's what a new client
|
||||
function looks like combining all the middleware:</p><pre><code class="clojure">(def my-client (wrap-add-date (wrap-https (wrap-no-op client))))
|
||||
|
||||
(my-client {:site "http://www.google.com"})
|
||||
;; ⇒ {:date #inst "2012-11-09T12:43:39.451-00:00",
|
||||
;; :cookies {...},
|
||||
;; :trace-redirects ["https://www.google.com/"],
|
||||
;; :request-time 1634,
|
||||
;; :status 200,
|
||||
;; :headers {...},
|
||||
;; :body "..."}
|
||||
</code></pre><p>(The response map has been edited to take less space where you see
|
||||
'...')</p><p>Here we can see that the <code>wrap-https</code> middleware has successfully
|
||||
turned the request for http://www.google.com into one for
|
||||
https://www.google.com, additionally the <code>wrap-add-date</code> middleware
|
||||
has added the :date key with the date the request happened. (the
|
||||
<code>wrap-no-op</code> middleware did execute, but since it didn't do anything,
|
||||
there's no output to tell)</p><p>This is a good start, but adding middleware can be expressed in a much
|
||||
cleaner and clearer way by using Clojure's threading macro, <code>-></code>. The
|
||||
<code>my-client</code> definition from above can be expressed like this:</p><pre><code class="clojure">(def my-client
|
||||
(-> client
|
||||
wrap-no-op
|
||||
wrap-https
|
||||
wrap-add-date))
|
||||
|
||||
(my-client {:site "http://www.google.com"})
|
||||
;; ⇒ {:date #inst "2012-11-09T12:47:32.130-00:00",
|
||||
;; :cookies {...},
|
||||
;; :trace-redirects ["https://www.google.com/"],
|
||||
;; :request-time 1630,
|
||||
;; :status 200,
|
||||
;; :headers {...},
|
||||
;; :body "..."}
|
||||
</code></pre><p>Something else to keep in mind is that middleware expressed in this
|
||||
way will be executed <em>from the bottom up</em>, so in this case,
|
||||
<code>wrap-add-date</code> will call <code>wrap-https</code>, which in turn calls
|
||||
<code>wrap-no-op</code>, which finally calls the <code>client</code> function.</p><p>For an example of combining a large amount of middleware, see
|
||||
<a href="https://github.com/dakrone/clj-http/blob/5534950b5ed48e3bc7285f0e956444ea832399da/src/clj_http/client.clj#L542-567">clj-http's
|
||||
client.clj</a>
|
||||
file</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../files_and_directories/index.html">« Working with Files and Directories in Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
464
clones/clojure-doc.org/articles/cookbooks/strings/index.html
Normal file
464
clones/clojure-doc.org/articles/cookbooks/strings/index.html
Normal file
|
@ -0,0 +1,464 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Strings</title>
|
||||
|
||||
|
||||
<meta name="description" content="This cookbook covers working with strings in Clojure using built-in
|
||||
functions, standard and contrib libraries, and parts of the JDK via
|
||||
interoperability.This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.">
|
||||
|
||||
<meta property="og:description" content="This cookbook covers working with strings in Clojure using built-in
|
||||
functions, standard and contrib libraries, and parts of the JDK via
|
||||
interoperability.This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/cookbooks/strings/" />
|
||||
<meta property="og:title" content="Strings" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/cookbooks/strings/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Strings</h2>
|
||||
</div>
|
||||
|
||||
<p>This cookbook covers working with strings in Clojure using built-in
|
||||
functions, standard and contrib libraries, and parts of the JDK via
|
||||
interoperability.</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="overview">Overview</h2><ul><li>Strings are <a href="http://docs.oracle.com/javase/7/docs/api/java/lang/String.html">plain Java
|
||||
strings</a>.
|
||||
You can use anything which operates on them.</li><li>Java strings are immutable, so they're convenient to use in Clojure.</li><li>You can't add metadata to Java strings.</li><li>Clojure supports some convenient notations:</li></ul><pre><code> "foo" java.lang.String
|
||||
#"\d" java.util.regex.Pattern (in this case, one which matches a single digit)
|
||||
\f java.lang.Character (in this case, the letter 'f')
|
||||
</code></pre><ul><li><strong>Caveat:</strong> Human brains and electronic computers are rather different
|
||||
devices. So Java strings (sequences of <a href="http://docs.oracle.com/javase/7/docs/api/java/lang/Character.html#unicode">UTF-16
|
||||
characters</a>)
|
||||
don't always map nicely to user-perceived characters. For example, a
|
||||
single Unicode "code point" doesn't necessarily equal a user-perceived
|
||||
character. (Like Korean Hangul Jamo, where user-perceived characters
|
||||
are composed from two or three Unicode code points.) Also, a Unicode
|
||||
code point may sometimes require 2 UTF-16 characters to encode it.</li></ul><h2 id="preliminaries">Preliminaries</h2><p>Some examples use
|
||||
<a href="http://clojure.github.io/clojure/clojure.string-api.html">clojure.string</a>,
|
||||
<a href="https://github.com/edn-format/edn">clojure.edn</a> and
|
||||
<a href="http://clojure.github.io/clojure/clojure.pprint-api.html">clojure.pprint</a>. We'll
|
||||
assume your <code>ns</code> macro contains:</p><pre><code class="clojure">(:require [clojure.string :as str]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.pprint :as pp])
|
||||
</code></pre><p>or else in the repl you've loaded it:</p><pre><code class="clojure">(require '[clojure.string :as str])
|
||||
(require '[clojure.edn :as edn])
|
||||
(require '[clojure.pprint :as pp])
|
||||
</code></pre><h2 id="recipes">Recipes</h2><h3 id="basics">Basics</h3><pre><code class="clojure">;; Size measurements
|
||||
(count "0123") ;=> 4
|
||||
(empty? "0123") ;=> false
|
||||
(empty? "") ;=> true
|
||||
(str/blank? " ") ;=> true
|
||||
|
||||
;; Concatenate
|
||||
(str "foo" "bar") ;=> "foobar"
|
||||
(str/join ["0" "1" "2"]) ;=> "012"
|
||||
(str/join "." ["0" "1" "2"]) ;=> "0.1.2"
|
||||
|
||||
;; Matching using plain Java methods.
|
||||
;;
|
||||
;; You might prefer regexes for these. For instance, failure returns
|
||||
;; -1, which you have to test for. And characters like \o are
|
||||
;; instances of java.lang.Character, which you may have to convert to
|
||||
;; int or String.
|
||||
(.indexOf "foo" "oo") ;=> 1
|
||||
(.indexOf "foo" "x") ;=> -1
|
||||
(.lastIndexOf "foo" (int \o)) ;=> 2
|
||||
|
||||
;; Substring
|
||||
(subs "0123" 1) ;=> "123"
|
||||
(subs "0123" 1 3) ;=> "12"
|
||||
(str/trim " foo ") ;=> "foo"
|
||||
(str/triml " foo ") ;=> "foo "
|
||||
(str/trimr " foo ") ;=> " foo"
|
||||
|
||||
;; Multiple substrings
|
||||
(seq "foo") ;=> (\f \o \o)
|
||||
(str/split "foo/bar/quux" #"/") ;=> ["foo" "bar" "quux"]
|
||||
(str/split "foo/bar/quux" #"/" 2) ;=> ["foo" "bar/quux"]
|
||||
(str/split-lines "foo
|
||||
bar") ;=> ["foo" "bar"]
|
||||
|
||||
;; Case
|
||||
(str/lower-case "fOo") ;=> "foo"
|
||||
(str/upper-case "fOo") ;=> "FOO"
|
||||
(str/capitalize "fOo") ;=> "Foo"
|
||||
|
||||
;; Escaping
|
||||
(str/escape "foo|bar|quux" {\| "||"}) ;=> "foo||bar||quux"
|
||||
|
||||
;; Get byte array of given encoding.
|
||||
;; (The output will likely have a different number than "3c3660".)
|
||||
(.getBytes "foo" "UTF-8") ;=> #<byte[] [B@3c3660>
|
||||
|
||||
;; Parsing keywords
|
||||
(keyword "foo") ;=> :foo
|
||||
|
||||
;; Parsing numbers
|
||||
(bigint "20000000000000000000000000000") ;=> 20000000000000000000000000000N
|
||||
(bigdec "20000000000000000000.00000000") ;=> 20000000000000000000.00000000M
|
||||
(Integer/parseInt "2") ;=> 2
|
||||
(Float/parseFloat "2") ;=> 2.0
|
||||
|
||||
;; Parsing edn, a subset of Clojure forms.
|
||||
(edn/read-string "0xffff") ;=> 65535
|
||||
|
||||
;; The sledgehammer approach to reading Clojure forms.
|
||||
;;
|
||||
;; SECURITY WARNING: Ensure *read-eval* is false when dealing with
|
||||
;; strings you don't 100% trust. Even though *read-eval* is false by
|
||||
;; default since Clojure 1.5, be paranoid and set it to false right
|
||||
;; before you use it, because anything could've re-bound it to true.
|
||||
(binding [*read-eval* false]
|
||||
(read-string "#\"[abc]\""))
|
||||
;=> #"[abc]"
|
||||
</code></pre><h3 id="parsing-complex-strings">Parsing complex strings</h3><h4 id="regexes">Regexes</h4><p>Regexes offer a boost in string-matching power. You can express ideas
|
||||
like repetition, alternatives, etc.</p><p><a href="http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html">Regex
|
||||
reference.</a></p><p><strong>Groups:</strong> Regex groups are useful, when we want to match more than
|
||||
one substring. (Or refer to matches later.) In the regex <code>#"(group-1) (group-2)"</code>, the 0th group is the whole match. The 1st group is
|
||||
started by the left-most <code>(</code>, the 2nd group is started by the
|
||||
second-left-most <code>(</code>, etc. You can even nest groups. You can refer to
|
||||
groups later using <code>$0</code>, <code>$1</code>, etc.</p><p><strong>Matching</strong></p><pre><code class="clojure">;; Simple matching
|
||||
(re-find #"\d+" "foo 123 bar") ;=> "123"
|
||||
|
||||
;; What happens when a match fails.
|
||||
(re-find #"\d+" "foobar") ;=> nil
|
||||
|
||||
;; Return only the first groups which satisfy match.
|
||||
(re-matches #"(@\w+)\s([.0-9]+)%"
|
||||
"@shanley 19.8%")
|
||||
;=>["@shanley 19.8%" "@shanley" "19.8"]
|
||||
|
||||
;; Return seq of all matching groups which occur in string.
|
||||
(re-seq #"(@\w+)\s([.0-9]+)%"
|
||||
"@davidgraeber 12.3%,@shanley 19.8%")
|
||||
;=> (["@davidgraeber 12.3%" "@davidgraeber" "12.3"]
|
||||
; ["@shanley 19.8%" "@shanley" "19.8"])
|
||||
</code></pre><p><strong>Replacing</strong></p><p>We use <code>str/replace</code>. Aside from the first arg (the initial string),
|
||||
the next two args are match and replacement:</p><pre><code> match / replacement can be:
|
||||
string / string
|
||||
char / char
|
||||
pattern / (string or function of match).
|
||||
</code></pre><pre><code class="clojure">;; In the replacement string, $0, $1, etc refer to matched groups.
|
||||
(str/replace "@davidgraeber 12.3%,@shanley 19.8%"
|
||||
#"(@\S+)\s([.0-9]+)%"
|
||||
"$2 ($1)")
|
||||
;=> "12.3 (@davidgraeber),19.8 (@shanley)"
|
||||
|
||||
;; Using a function to replace text gives us power.
|
||||
(println
|
||||
(str/replace "@davidgraeber 12.3%,@shanley 19.8%"
|
||||
#"(@\w+)\s([.0-9]+)%,?"
|
||||
(fn [[_ person percent]]
|
||||
(let [points (-> percent Float/parseFloat (* 100) Math/round)]
|
||||
(str person "'s followers grew " points " points.\n")))))
|
||||
;print=> @davidgraeber's followers grew 1230 points.
|
||||
;print=> @shanley's followers grew 1980 points.
|
||||
;print=>
|
||||
</code></pre><h4 id="context-free-grammars">Context-free grammars</h4><p>Context-free grammars offer yet another boost in expressive matching
|
||||
power, compared to regexes. You can express ideas like nesting.</p><p>We'll use <a href="https://github.com/Engelberg/instaparse">Instaparse</a> on
|
||||
<a href="http://www.json.org/">JSON's grammar</a>. (This example isn't seriously
|
||||
tested nor a featureful parser. Use
|
||||
<a href="https://github.com/clojure/data.json">data.json</a> instead.)</p><pre><code class="clojure">;; Your project.clj should contain this (you may need to restart your JVM):
|
||||
;; :dependencies [[instaparse "1.2.4"]]
|
||||
;;
|
||||
;; We'll assume your ns macro contains:
|
||||
;; (:require [instaparse.core :as insta])
|
||||
;; or else in the repl you've loaded it:
|
||||
;; (require '[instaparse.core :as insta])
|
||||
|
||||
(def barely-tested-json-parser
|
||||
(insta/parser
|
||||
"object = <'{'> <w*> (members <w*>)* <'}'>
|
||||
<members> = pair (<w*> <','> <w*> members)*
|
||||
<pair> = string <w*> <':'> <w*> value
|
||||
<value> = string | number | object | array | 'true' | 'false' | 'null'
|
||||
array = <'['> elements* <']'>
|
||||
<elements> = value <w*> (<','> <w*> elements)*
|
||||
number = int frac? exp?
|
||||
<int> = '-'? digits
|
||||
<frac> = '.' digits
|
||||
<exp> = e digits
|
||||
<e> = ('e' | 'E') (<'+'> | '-')?
|
||||
<digits> = #'[0-9]+'
|
||||
(* First sketched state machine; then it was easier to figure out
|
||||
regex syntax and all the maddening escape-backslashes. *)
|
||||
string = <'\\\"'> #'([^\"\\\\]|\\\\.)*' <'\\\"'>
|
||||
<w> = #'\\s+'"))
|
||||
|
||||
(barely-tested-json-parser "{\"foo\": {\"bar\": 99.9e-9, \"quux\": [1, 2, -3]}}")
|
||||
;=> [:object
|
||||
; [:string "foo"]
|
||||
; [:object
|
||||
; [:string "bar"]
|
||||
; [:number "99" "." "9" "e" "-" "9"]
|
||||
; [:string "quux"]
|
||||
; [:array [:number "1"] [:number "2"] [:number "-" "3"]]]]
|
||||
|
||||
;; That last output is a bit verbose. Let's process it further.
|
||||
(->> (barely-tested-json-parser "{\"foo\": {\"bar\": 99.9e-9, \"quux\": [1, 2, -3]}}")
|
||||
(insta/transform {:object hash-map
|
||||
:string str
|
||||
:array vector
|
||||
:number (comp edn/read-string str)}))
|
||||
;=> {"foo" {"quux" [1 2 -3], "bar" 9.99E-8}}
|
||||
|
||||
|
||||
;; Now we can appreciate what those <angle-brackets> were all about.
|
||||
;;
|
||||
;; When to the right of the grammar's =, it totally hides the enclosed
|
||||
;; thing in the output. For example, we don't care about whitespace,
|
||||
;; so we hide it with <w*>.
|
||||
;;
|
||||
;; When to the left of the grammar's =, it merely prevents a level of
|
||||
;; nesting in the output. For example, "members" is a rather
|
||||
;; artificial entity, so we prevent a pointless level of nesting with
|
||||
;; <members>.
|
||||
</code></pre><h3 id="building-complex-strings">Building complex strings</h3><h4 id="redirecting-streams">Redirecting streams</h4><p><code>with-out-str</code> provides a simple way to build strings. It redirects
|
||||
standard output (<code>*out*</code>) to a fresh <code>StringWriter</code>, then returns the
|
||||
resulting string. So you can use functions like <code>print</code>, <em>even in
|
||||
nested functions</em>, and get the resulting string at the end.</p><pre><code class="clojure">(let [shrimp-varieties ["shrimp-kabobs" "shrimp creole" "shrimp gumbo"]]
|
||||
(with-out-str
|
||||
(print "We have ")
|
||||
(doseq [name (str/join ", " shrimp-varieties)]
|
||||
(print name))
|
||||
(print "...")))
|
||||
;=> "We have shrimp-kabobs, shrimp creole, shrimp gumbo..."
|
||||
</code></pre><h4 id="format-strings">Format strings</h4><p>Java's templating mini-language helps you build many strings
|
||||
conveniently. <a href="http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html">Reference.</a></p><pre><code class="clojure">;; %s is most commonly used to print args. Escape %'s with %%.
|
||||
(format "%s enjoyed %s%%." "Mozambique" 19.8) ;=> "Mozambique enjoyed 19.8%."
|
||||
|
||||
;; The 1$ prefix allows you to keep referring to the first arg.
|
||||
(format "%1$tY-%1$tm-%1$td" #inst"2000-01-02T00:00:00") ;=> "2000-01-02"
|
||||
|
||||
;; Again, 1$, 2$, etc prefixes let us refer to args in arbitrary orders.
|
||||
(format "New year: %2$tY. Old year: %1$tY"
|
||||
#inst"2000-01-02T00:00:00"
|
||||
#inst"3111-12-31T00:00:00")
|
||||
;=> "New year: 3111. Old year: 2000"
|
||||
</code></pre><h4 id="cl-format">CL-Format</h4><p><code>cl-format</code> is a port of Common Lisp's notorious, powerful string
|
||||
formatting mini-language. For example, you can build strings from
|
||||
sequences. (As well as oddities like print numbers in English or two
|
||||
varieties of Roman numerals.) However, it's weaker than plain <code>format</code>
|
||||
with printing dates and referring to args in arbitrary order.</p><p>Remember that <code>cl-format</code> represents a (potentially unreadable)
|
||||
language which your audience didn't sign up to learn. If you're the
|
||||
sort of person who likes it, try to only use it in sweetspots where it
|
||||
provides clarity for little complexity.</p><p><a href="http://www.gigamonkeys.com/book/a-few-format-recipes.html">Tutorial</a>
|
||||
in Practical Common
|
||||
Lisp. <a href="http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm">Reference</a>
|
||||
in Common Lisp's Hyperspec.</p><pre><code class="clojure">;; The first param prints to *out* if true. To string if false.
|
||||
;; To a stream if it's a stream.
|
||||
(pp/cl-format true "~{~{~a had ~s percentage point~:p.~}~^~%~}"
|
||||
{"@davidgraeber" 12.3
|
||||
"@shanley" 19.8
|
||||
"@tjgabbour" 1})
|
||||
;print=> @davidgraeber had 12.3 percentage points.
|
||||
;print=> @tjgabbour had 1 percentage point.
|
||||
;print=> @shanley had 19.8 percentage points.
|
||||
|
||||
(def format-string "~{~#[~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~}")
|
||||
(pp/cl-format nil format-string [])
|
||||
;=> ""
|
||||
(pp/cl-format nil format-string ["@shanley"])
|
||||
;=> "@shanley"
|
||||
(pp/cl-format nil format-string ["@shanley", "@davidgraeber"])
|
||||
;=> "@shanley and @davidgraeber"
|
||||
(pp/cl-format nil format-string ["@shanley", "@davidgraeber", "@sarahkendzior"])
|
||||
;=> "@shanley, @davidgraeber, and @sarahkendzior"
|
||||
</code></pre><h2 id="contributors">Contributors</h2><p>Tj Gabbour <a href="mailto:tjg@simplevalue.de">tjg@simplevalue.de</a>, 2013 (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../data_structures/index.html">« Data Structures (Help wanted)</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../math/index.html">Mathematics with Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
230
clones/clojure-doc.org/articles/ecosystem/books/index.html
Normal file
230
clones/clojure-doc.org/articles/ecosystem/books/index.html
Normal file
File diff suppressed because one or more lines are too long
213
clones/clojure-doc.org/articles/ecosystem/community/index.html
Normal file
213
clones/clojure-doc.org/articles/ecosystem/community/index.html
Normal file
|
@ -0,0 +1,213 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Clojure Community</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/community/" />
|
||||
<meta property="og:title" content="Clojure Community" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/community/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Clojure Community</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>The Official Clojure mailing lists</li><li>IRC channel</li><li>Documentation sites</li><li>Clojure User Groups around the globe</li><li>Conferences about or related to Clojure</li><li>Various Community sites about Clojure (subreddit, etc)</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="clojure-mailing-lists">Clojure Mailing Lists</h2><ul><li><a href="https://groups.google.com/forum/?fromgroups#!forum/clojure">Clojure user mailing list</a></li><li><a href="https://groups.google.com/forum/?fromgroups#!forum/clojure-dev">Clojure development mailing list</a></li></ul><h2 id="clojure-irc-channels">Clojure IRC Channels</h2><h3 id="main-channel">Main Channel</h3><p><code>#clojure</code> on <code>irc.freenode.net</code>.</p><p>Channel logs are available at <a href="http://clojure-log.n01se.net/">clojure-log.n01se.net</a> and <a href="http://www.raynes.me/logs/irc.freenode.net/clojure/">www.raynes.me/logs/irc.freenode.net/clojure/</a>.</p><h3 id="documentation-channel">Documentation Channel</h3><p><code>#clojure-doc</code> on <code>irc.freenode.net</code>.</p><h2 id="documentation">Documentation</h2><ul><li><a href="http://clojuredocs.org/">Clojuredocs</a>: Clojure API reference, with examples</li><li><a href="http://jafingerhut.github.io/cheatsheet/clojuredocs/cheatsheet-tiptip-no-cdocs-summary.html">Clojure API Cheatsheet</a></li><li><a href="http://clojurekoans.com/">Clojure Koans</a></li><li><a href="http://www.getclojure.org">GetClojure</a>: Tens of thousands of searchable Clojure examples mined from all over the internet.</li><li><a href="http://grimoire.arrdem.com/">Grimoire</a></li></ul><h2 id="courses">Courses</h2><ul><li><a href="http://www.udemy.com/clojure-code">Udemy's 19 part series on Clojure</a></li><li><a href="http://mooc.cs.helsinki.fi/clojure">Functional Programming with Clojure</a></li></ul><h2 id="user-groups">User Groups</h2><ul><li>The list of <a href="../user_groups/index.html">Clojure User Groups</a> around the world</li><li><a href="../running_cljug/index.html">How to run your own Clojure User Group</a></li></ul><h2 id="videos-about-clojure">Videos About Clojure</h2><p>Videos of talks about Clojure are often made available on <a href="http://www.infoq.com/clojure">InfoQ</a>, and <a href="https://www.youtube.com/user/ClojureTV">Clojure YouTube channel</a>.</p><ul><li><a href="http://vimeo.com/channels/fulldisclojure/videos">Full Disclojure</a> is a series of screencasts about Clojure</li><li><a href="http://pluralsight.com/training/Courses/TableOfContents/clojure-concurrency-tutorial">Clojure Concurrency Tutorial</a></li><li><a href="http://shop.oreilly.com/product/0636920030409.do">Clojure Inside Out</a> from O'Reilly</li><li><a href="http://www.youtube.com/playlist?list=PL1p6TgkbKXqyOwq6iSkce_EY5YWFHciHt">Clojure Koans Walkthroughs</a></li><li><a href="http://pluralsight.com/training/courses/TableOfContents?courseName=clojure-fundamentals-part-one">Clojure Fundamentals</a></li><li><a href="http://www.purelyfunctional.tv/">LispCast</a></li><li><a href="https://tbaldridge.pivotshare.com/">Clojure Tutorials by Tim Baldridge</a></li></ul><h2 id="podcasts-about-clojure">Podcasts About Clojure</h2><ul><li><a href="http://mostlylazy.com/">Mostly λazy… a Clojure podcast</a> by Chas Emerick</li><li><a href="http://cognitect.com/podcast">Cognicast</a> by Craig Andera often discusses topics relevant to Clojure and ClojureScript.</li></ul><h2 id="code-repositories">Code Repositories</h2><p>Most folks host their projects at
|
||||
<a href="https://github.com/languages/Clojure">GitHub</a>, and most pure Clojure
|
||||
library distributions (with the exception of contrib) are available at
|
||||
<a href="https://clojars.org/">Clojars</a>.</p><h2 id="websites">Websites</h2><ul><li><a href="http://clojure.org/">Clojure.org</a>: the official website</li><li><a href="http://planet.clojure.in/">Planet Clojure</a>: aggregator of selected Clojure-related blog posts</li><li><a href="http://rosettacode.org/wiki/Category:Clojure">Clojure at Rosetta Code</a></li><li><a href="http://dev.clojure.org/dashboard.action">Clojure Confluence wiki</a></li><li><a href="http://www.clojure-toolbox.com/">The Clojure Toolbox</a>: a categorized directory of libraries and tools for Clojure</li><li><a href="https://www.4clojure.com/">4Clojure</a>: Clojure exercise problems</li><li><a href="http://exercism.io/">Exercism.io</a>: Peer-reviewed Clojure exercises</li></ul><h2 id="forums">Forums</h2><ul><li><a href="http://www.reddit.com/r/clojure">r/Clojure subreddit</a></li></ul><h2 id="conferences">Conferences</h2><p>In no particular order:</p><ul><li><a href="http://clojure-conj.org/">Clojure/conj</a>, aka "the Conj"</li><li><a href="http://euroclojure.com/">EuroClojure</a></li><li><a href="http://clojurewest.org/">Clojure/West</a></li><li><a href="https://thestrangeloop.com/">StrangeLoop</a> (not about Clojure but often has strong Clojure community presence)</li><li><a href="http://lambdajam.com/">Lambda Jam</a> (also not about Clojure but has strong Clojure community presence)</li></ul><h2 id="email-newsletters">Email Newsletters</h2><ul><li><a href="http://www.clojuregazette.com/">Clojure Gazette</a></li><li><a href="http://defnewsletter.com/">(def newsletter)</a></li></ul><h2 id="workshops">Workshops</h2><ul><li><a href="http://www.clojurebridge.org/">ClojureBridge</a></li><li><a href="http://lambdanext.eu/">Lambda Next</a></li></ul><h2 id="core-development">Core development</h2><p>See the <a href="http://dev.clojure.org/display/design/Home">Clojure Confluence wiki</a> for full details on
|
||||
how core development is handled.</p><p>Coordination of development efforts happen on the development mailing list, on the Confluence wiki,
|
||||
and make use of the <a href="http://dev.clojure.org/jira/browse/CLJ">JIRA bug and issue tracker</a>.</p><p>Although Clojure and the contrib libraries all have homes <a href="https://github.com/clojure">at GitHub</a>,
|
||||
<strong>pull-requests are not accepted</strong>. All core development happens via JIRA, patches and the Confluence wiki.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../maven/index.html">« How to use Maven to build Clojure projects</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../user_groups/index.html">Clojure User Groups »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,244 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Filters</title>
|
||||
|
||||
|
||||
<meta name="description" content="core.typed includes an implementation of occurrence typing, which helps the type
|
||||
checker refine types according to control flow.Occurrence typing helps core.typed infer a very accurate type for this expression
|
||||
by recognising the semantics of predicates like symbol? and number?.">
|
||||
|
||||
<meta property="og:description" content="core.typed includes an implementation of occurrence typing, which helps the type
|
||||
checker refine types according to control flow.Occurrence typing helps core.typed infer a very accurate type for this expression
|
||||
by recognising the semantics of predicates like symbol? and number?.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/filters/" />
|
||||
<meta property="og:title" content="core.typed - Filters" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/filters/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Filters</h2>
|
||||
</div>
|
||||
|
||||
<p>core.typed includes an implementation of occurrence typing, which helps the type
|
||||
checker refine types according to control flow.</p><p>Occurrence typing helps core.typed infer a very accurate type for this expression
|
||||
by recognising the semantics of predicates like <code>symbol?</code> and <code>number?</code>.</p><pre><code class="clojure">clojure.core.typed=> (cf (let [a (ann-form 1 Any)]
|
||||
(cond
|
||||
(symbol? a) a
|
||||
(number? a) a)))
|
||||
(U clojure.lang.Symbol java.lang.Number nil)
|
||||
</code></pre><h2 id="filters">Filters</h2><p>core.typed collects more information than just types for each expression.</p><p>A structure called a filter set is also inferred. A filter set is a collection
|
||||
of two filters:</p><ul><li>a filter that is true if the expression is a true value, called the <code>then</code> filter</li><li>a filter that is true if the expression is a false value, called the <code>else</code> filter</li></ul><h3 id="trivial-filters">Trivial Filters</h3><p>There are two trivial filters:</p><ul><li><code>tt</code>, the trivially true filter</li><li><code>ff</code>, the impossible filter</li></ul><p>We can use <code>cf</code> to check the filters of expressions.</p><pre><code class="clojure">clojure.core.typed=> (cf 1)
|
||||
[(Value 1) {:then tt, :else ff}]
|
||||
</code></pre><p>The second place of the result vector is the filter set inferred for the expression.
|
||||
<code>{:then tt, :else ff}</code> reads: the expression could be a true value, but it is impossible
|
||||
for it to be a false value. This of course aligns with the semantics of numbers in Clojure.</p><p>False values are never true:</p><pre><code class="clojure">clojure.core.typed=> (cf nil)
|
||||
[nil {:then ff, :else tt}]
|
||||
</code></pre><h3 id="positive-and-negative-type-filters">Positive and Negative Type Filters</h3><p>Filters can hold information relating bindings to types.</p><p>A positive type filter refines a local binding to be a type.</p><p>This filter says that the local binding <code>a</code> is of type <code>Number</code>.</p><pre><code class="clojure">(is Number a)
|
||||
</code></pre><p>A negative type filter refines a local binding to <em>not</em> be a type.</p><p>This filter says that the local binding <code>a</code> is <em>not</em> of type <code>Number</code>.</p><pre><code class="clojure">(! Number a)
|
||||
</code></pre><h3 id="latent-filters">Latent Filters</h3><p>Filters almost never need to be written directly in normal code. <em>Latent</em> filters
|
||||
however are very useful, and provide the most useful information to core.typed.</p><p>A <em>latent filter set</em> is a filter set attached to a function type. It is latent
|
||||
because it is not used directly: instead when a function with a latent filter set
|
||||
is called, the filter set is instantiated in a way that makes sense in the current
|
||||
context before it is used like a normal filter.</p><h3 id="predicates">Predicates</h3><p>A very common place for a latent filters are in the types for predicates.</p><p>The type for <code>symbol?</code>, is</p><pre><code class="clojure">[Any -> Boolean :filters {:then (is Symbol 0), :else (! Symbol 0)}]
|
||||
</code></pre><p>First, notice that latent type predicates can also take an integer as an identifier.
|
||||
The <code>0</code> represents the first argument of the function the latent filter set is attached to.</p><p>So the latent <code>then</code> filter <code>(is Symbol 0)</code> says the first argument to <code>symbol?</code> is of type <code>Symbol</code>
|
||||
if the whole expression is a true value. To retrieve a non-latent filter, the <code>0</code> is instantiated to
|
||||
the appropriate local binding.</p><pre><code>Note: Use `clojure.core.typed/print-filterset` to print the filter set of an expression.
|
||||
</code></pre><pre><code class="clojure">clojure.core.typed=> (cf (let [a (ann-form 1 Any)]
|
||||
(print-filterset "symbol filters"
|
||||
(symbol? a))))
|
||||
"symbol filters"
|
||||
{:then (is clojure.lang.Symbol a), :else (! clojure.lang.Symbol a)}
|
||||
empty-object
|
||||
Flow tt
|
||||
boolean
|
||||
</code></pre><p>By printing the filter set of <code>(symbol? a)</code> we can see this in work, which
|
||||
has a non-latent filter set of <code>{:then (is clojure.lang.Symbol a), :else (! clojure.lang.Symbol a)}</code>.</p><h3 id="paths-and-objects">Paths and Objects</h3><p>TODO</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../poly_fn/index.html">« core.typed - Polymorphic Functions</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../mm_protocol_datatypes/index.html">core.typed - Protocols »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,237 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Functions</title>
|
||||
|
||||
|
||||
<meta name="description" content="Common things that are IFns:">
|
||||
|
||||
<meta property="og:description" content="Common things that are IFns:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/function_types/" />
|
||||
<meta property="og:title" content="core.typed - Functions" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/function_types/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Functions</h2>
|
||||
</div>
|
||||
|
||||
<ul><li>Function types are quite different from Typed Racket
|
||||
<ul><li>anything can implement the <code>IFn</code> interface</li><li>In core.typed the <code>Function</code> type is more a special type than the type for lambdas</li></ul></li></ul><p>Common things that are <code>IFn</code>s:</p><pre><code class="clojure">- clojure.lang.Function
|
||||
- c.l.Keyword
|
||||
- [Any -> Any]
|
||||
- (c.l.PersistentHashMap k v)
|
||||
- (All [x]
|
||||
(Fn [Any -> (U nil v)]
|
||||
[Any x -> (U x v)]))
|
||||
- (c.l.PersistentHashSet v)
|
||||
- (All [x]
|
||||
(Fn [Any -> (U nil v)]))
|
||||
- c.l.Symbol
|
||||
- [Any -> Any]
|
||||
- (Value :a)
|
||||
- (All [x]
|
||||
[Any -> x :filters {:then (is {:a x} 0)}])
|
||||
- (Value sym)
|
||||
- (All [x] [Any -> (U nil v)])
|
||||
</code></pre><p>The <code>IFn</code> class might be parameterised by a <code>Function</code> type.
|
||||
The immediate problem is intersections allows us to have more
|
||||
than one function type.</p><p>eg. What function type is this?</p><pre><code class="clojure">(I (Value :a)
|
||||
(All [x]
|
||||
[Any -> x :filters {:then (is {:a x} 0)}])
|
||||
</code></pre><p>Even <code>(Value :a)</code> inherits two function types:</p><ul><li>that for c.l.Keyword</li><li>that for <code>(Value :a)</code></li></ul><p><code>(Value :a) <: (IFn x)</code> infers <code>x</code> to be:</p><pre><code class="clojure">(I [Any -> Any]
|
||||
(All [x]
|
||||
[Any -> x :filters {:then (is {:a x} 0)}])
|
||||
</code></pre><p>The second member of the intersection is more specific,
|
||||
thus can be simplified to:</p><pre><code class="clojure">(All [x]
|
||||
[Any -> x :filters {:then (is {:a x} 0)}])
|
||||
</code></pre><p>Does this work in general? As long as there is a subtyping relationship
|
||||
between the possible <code>Function</code> types, we can infer the most useful
|
||||
one.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../loops/index.html">« core.typed - Looping constructs</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../limitations/index.html">core.typed - Limitations »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,207 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - User Documentation Home</title>
|
||||
|
||||
|
||||
<meta name="description" content="core.typed is an optional type system for Clojure.Quickstart">
|
||||
|
||||
<meta property="og:description" content="core.typed is an optional type system for Clojure.Quickstart">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/home/" />
|
||||
<meta property="og:title" content="core.typed - User Documentation Home" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/home/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - User Documentation Home</h2>
|
||||
</div>
|
||||
|
||||
<p>core.typed is an optional type system for Clojure.</p><h2 id="quickstart">Quickstart</h2><p><code>(clojure.core.typed/ann v t)</code> gives var <code>v</code> the static type <code>t</code>.</p><p><code>(clojure.core.typed/ann-form f t)</code> ensures form <code>f</code> is of the static type <code>t</code>.</p><p><code>(clojure.core.typed/check-ns)</code> type checks the current namespace.</p><p><code>(clojure.core.typed/cf t)</code> type checks the form <code>t</code>.</p><p>See the <a href="../quick_guide.html">Quick Guide</a>.</p><h2 id="rationale"><a href="../rationale/index.html">Rationale</a></h2><p>Why core.typed exists, what can it do for you?</p><h2 id="getting-started-guide">Getting Started Guide</h2><p>If you are new to core.typed, gradual type systems, or even types in general, and want to learn how
|
||||
core.typed can help verify your programs, start here.</p><h3 id="introduction-and-motivation"><a href="../start/introduction_and_motivation/index.html">Introduction and Motivation</a></h3><p>We discuss some theory and design goals of core.typed.</p><h3 id="annotations"><a href="../start/annotations/index.html">Annotations</a></h3><p>Where and how to annotate your code to help core.typed check your code.</p><h3 id="types"><a href="../types/index.html">Types</a></h3><p>Syntax and descriptions of core.typed types.</p><h3 id="polymorphic-functions-bounds-and-higher-kinded-variables"><a href="../poly_fn/index.html">Polymorphic Functions, Bounds and Higher-kinded Variables</a></h3><h3 id="filters"><a href="../filters/index.html">Filters</a></h3><p>An overview of filters for occurrence typing.</p><h3 id="datatypes-and-protocols"><a href="../mm_protocol_datatypes/index.html">Datatypes and Protocols</a></h3><p>Typing definitions and usages of Clojure datatypes and protocols.</p><h3 id="looping-constructs"><a href="../loops/index.html">Looping constructs</a></h3><p>core.typed provides several wrapper macros for common looping constructs.</p><h3 id="dotted-functions">Dotted Functions</h3><h3 id="java-classes-arrays-and-interop">Java Classes, Arrays and Interop</h3><h2 id="miscellaneous-tutorials">Miscellaneous Tutorials</h2><h3 id="hole-driven-development"><a href="https://github.com/clojure/core.typed/blob/master/src/test/clojure/clojure/core/typed/test/hole.clj">Hole-Driven Development</a></h3><p>A fun diversion playing with holes.</p><ul><li>Requires some knowledge of Haskell.</li></ul><h2 id="examples">Examples</h2><h3 id="irc-bot"><a href="https://github.com/frenchy64/Parjer">IRC Bot</a></h3><h2 id="limitations---known-issues"><a href="../limitations/index.html">Limitations</a> - Known issues</h2><h2 id="documentation-contributors">Documentation Contributors</h2><p>Ambrose Bonnaire-Sergeant (@ambrosebs)</p><p>Copyright 2013, Ambrose Bonnaire-Sergeant</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../../java_jdbc/reusing_connections.html">« java.jdbc - How to reuse database connections</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../user_documentation/index.html">core.typed - User Documentation »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,222 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Limitations</title>
|
||||
|
||||
|
||||
<meta name="description" content="Namespace managementTyped dependencies NYI.">
|
||||
|
||||
<meta property="og:description" content="Namespace managementTyped dependencies NYI.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/limitations/" />
|
||||
<meta property="og:title" content="core.typed - Limitations" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/limitations/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Limitations</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="namespace-management">Namespace management</h2><p>Typed dependencies NYI.</p><h2 id="destructuring">Destructuring</h2><p>Only map destructuring <em>without</em> options is supported.</p><p>Other forms of destructuring require equality filters.</p><h2 id="shadowing-bindings">Shadowing bindings</h2><p>If an argument is shadowed and the shadowed binding is referenced
|
||||
in filters or object then the shadow is indistinguishable from the parameter
|
||||
and parameter will be incorrectly abstracted.</p><p>eg.</p><pre><code class="clojure">(fn [a]
|
||||
(if (= a 1)
|
||||
(let [a 'foo] ; here this shadows the argument, impossible to recover filters
|
||||
a) ; in fact any new filters about a will be incorrectly assumed to be the argument
|
||||
false))
|
||||
</code></pre><p>(See <code>abstract-result</code> in <code>typed/test.clj</code>)</p><h2 id="dotted-functions">Dotted Functions</h2><p>A dotted function contains a dotted variable in its function type.</p><p>eg. map's type:
|
||||
<code>(All [c a b ...] [[a b ... b -> c] (U nil (Seqable a)) (U nil (Seqable b)) ... b -> (Seqable c)]))</code></p><p>We can't currently check the definitions of functions with dotted rest arguments.</p><h2 id="rest-arguments">Rest Arguments</h2><p>Currently cannot check the definition of functions with rest arguments,
|
||||
but usage checking should work.</p><h2 id="using-filter">Using <code>filter</code></h2><p>Not everything can be inferred from a <code>filter</code>. A common example is
|
||||
<code>(filter identity coll)</code> does not work. The reason is <code>identity</code> only
|
||||
gives negative information when its result is true: that the argument is <em>not</em><code>(U nil false)</code>.</p><p>This idiom must be converted to this syntax <code>(fn [a] a)</code> and then annotated with
|
||||
positive propositions.</p><pre><code class="clojure">;eg.
|
||||
|
||||
(filter (ann-form (fn [a] a)
|
||||
[(U nil Number) -> (U nil Number) :filters {:then (is Number 0)}])
|
||||
[1 nil 2])
|
||||
; :- (Seqable Number)
|
||||
</code></pre><p>Positive information infers just fine, like <code>(filter number? coll)</code>.
|
||||
The above idiom is useful when you are filtering something like a <code>(Seqable (U nil x))</code> and there is no
|
||||
predicate to test for <code>x</code>, so you can only test if something isn't <code>nil</code>.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../function_types/index.html">« core.typed - Functions</a>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,223 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Looping constructs</title>
|
||||
|
||||
|
||||
<meta name="description" content="Due to limitations in core.typed's inference, we require using "typed" versions
|
||||
of several core forms.loop">
|
||||
|
||||
<meta property="og:description" content="Due to limitations in core.typed's inference, we require using "typed" versions
|
||||
of several core forms.loop">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/loops/" />
|
||||
<meta property="og:title" content="core.typed - Looping constructs" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/loops/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Looping constructs</h2>
|
||||
</div>
|
||||
|
||||
<p>Due to limitations in core.typed's inference, we require using "typed" versions
|
||||
of several core forms.</p><h1 id="loop">loop</h1><p>Usages of <code>loop</code> should be replaced with <code>clojure.core.typed/loop></code>.</p><p>The syntax is identical except each loop variable requires a type annotation.</p><pre><code class="clojure">(loop> [[a :- Number] 1
|
||||
[b :- (U nil Number)] nil]
|
||||
...)
|
||||
</code></pre><h1 id="named-fns">Named fn's</h1><p>Named <code>fn</code>'s require full annotation for accurate recursive calls inside the <code>fn</code> body.</p><pre><code class="clojure">clojure.core.typed=> (cf (ann-form (fn a [n] (+ (a 1) n))
|
||||
[Number -> Number]))
|
||||
(Fn [java.lang.Number -> java.lang.Number])
|
||||
</code></pre><h1 id="for">for</h1><p>Use <code>clojure.core.typed/for></code> instead of <code>for</code>.</p><p><code>for></code> requires annotations for the return type of the body
|
||||
of the for, and the left hand side of each binding form.</p><pre><code class="clojure">(for> :- Number
|
||||
[[a :- (U nil AnyInteger)] [1 nil 2 3]
|
||||
:when a]
|
||||
(inc a))
|
||||
</code></pre><h1 id="doseq">doseq</h1><p>Use <code>clojure.core.typed/doseq></code> instead of <code>doseq</code>.</p><p><code>doseq></code> requires annotations for the left hand side of each binding form.</p><pre><code class="clojure">(doseq> [[a :- (U nil AnyInteger)] [1 nil 2 3]
|
||||
:when a]
|
||||
(inc a))
|
||||
</code></pre>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../mm_protocol_datatypes/index.html">« core.typed - Protocols</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../function_types/index.html">core.typed - Functions »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,219 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Protocols</title>
|
||||
|
||||
|
||||
<meta name="description" content="Annotating Protocolsclojure.core.typed/ann-protocol annotates protocols.">
|
||||
|
||||
<meta property="og:description" content="Annotating Protocolsclojure.core.typed/ann-protocol annotates protocols.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/mm_protocol_datatypes/" />
|
||||
<meta property="og:title" content="core.typed - Protocols" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/mm_protocol_datatypes/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Protocols</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="annotating-protocols">Annotating Protocols</h2><p><code>clojure.core.typed/ann-protocol</code> annotates protocols.</p><p>Takes a name and a optionally a :methods keyword argument mapping
|
||||
method names to expected types.</p><p>Protocol definitions should use <code>clojure.core.typed/defprotocol></code> (identical syntax to <code>defprotocol</code>).</p><pre><code class="clojure">(ann-protocol IUnifyWithLVar
|
||||
unify-with-lvar [Term LVar ISubstitutions -> (U ISubstitutions Fail)])
|
||||
|
||||
(defprotocol> IUnifyWithLVar
|
||||
(unify-with-lvar [v u s]))
|
||||
</code></pre><p>Each protocol method argument (including the first) is explicit in the type annotation.
|
||||
Often, the the first argument (aka. <code>this</code>) will just be the protocol, but in some cases
|
||||
it is convenient to add more general types.</p><h2 id="annotating-datatypes">Annotating datatypes</h2><p><code>clojure.core.typed/ann-datatype</code> annotates datatypes.</p><p>Takes a name and a vector of fieldname/type type entries.</p><pre><code class="clojure">(ann-datatype Pair [lhs :- Term
|
||||
rhs :- Term])
|
||||
|
||||
(deftype Pair [lhs rhs]
|
||||
...)
|
||||
</code></pre><p>Each protocol extended in <code>deftype</code> must have an annotated expected type with <code>ann-protocol</code>.</p><p>The types for Java interface method are inferred from their corresponding Java type.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../filters/index.html">« core.typed - Filters</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../loops/index.html">core.typed - Looping constructs »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,234 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Polymorphic Functions</title>
|
||||
|
||||
|
||||
<meta name="description" content="core.typed supports polymorphic function types. They allow us to specify
|
||||
function types which are both general and accurate.All">
|
||||
|
||||
<meta property="og:description" content="core.typed supports polymorphic function types. They allow us to specify
|
||||
function types which are both general and accurate.All">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/poly_fn/" />
|
||||
<meta property="og:title" content="core.typed - Polymorphic Functions" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/poly_fn/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Polymorphic Functions</h2>
|
||||
</div>
|
||||
|
||||
<p>core.typed supports polymorphic function types. They allow us to specify
|
||||
function types which are both general and accurate.</p><h1 id="all">All</h1><p>The primitive <code>All</code> constructor creates a polymorphic binder and scopes
|
||||
type variables in a type.</p><p>The identity function has a simple polymorphic type:</p><pre><code class="clojure">(All [x]
|
||||
[x -> x])
|
||||
</code></pre><p>Read: for all types <code>x</code>, a function that takes an <code>x</code> and returns an <code>x</code>.</p><p>Polymorphic types are introduced with annotations, but where are they eliminated?
|
||||
We use local type inference to infer type variable types based on how they are used.</p><pre><code class="clojure">(identity :a)
|
||||
</code></pre><p>In the above example, we infer <code>x</code> to be <code>Keyword</code>, and instantiate the polymorphic
|
||||
type as <code>[Keyword -> Keyword]</code>.</p><h2 id="bounds">Bounds</h2><p>Type variables support upper and lower type bounds, which default to <code>Any</code> and <code>Nothing</code>
|
||||
respectively.</p><p>Equivalently, the type:</p><pre><code class="clojure">(All [x] ...)
|
||||
</code></pre><p>is shorthand for:</p><pre><code class="clojure">(All [[x :> Nothing :< Any]] ...)
|
||||
</code></pre><p>We use bounds to ensure a type variable can only be instantiated to a particular type.</p><p>The type of an identity function that only accepts <code>Number</code>s can be written:</p><pre><code class="clojure">(All [[x :< Number]]
|
||||
[x -> x])
|
||||
</code></pre><p>Bounds do not seem as useful in core.typed as languages like Java or Scala.
|
||||
Often, combinations of ordered function intersections and unions are more useful.</p><p>Bounds are also recursive: a bound can refer to the variable it's bounding.
|
||||
Type variables to the left of the type variable being bounded in the same binder are in scope in a bound.</p><h2 id="higher-kinded-variables">Higher-kinded variables</h2><p>Note: Experimental feature</p><p>A type variable can be of a higher-kind.</p><pre><code class="clojure">(def-alias AnyMonad
|
||||
(TFn [[m :kind (TFn [[x :variance :covariant]] Any)]]
|
||||
'{:m-bind (All [x y]
|
||||
[(m x) [x -> (m y)] -> (m y)])
|
||||
:m-result (All [x]
|
||||
[x -> (m x)])
|
||||
:m-zero (U (All [x] (m x)) Undefined)
|
||||
:m-plus (U (All [x]
|
||||
[(m x) * -> (m x)])
|
||||
Undefined)}))
|
||||
</code></pre><p>In this type, <code>x</code> is a type function taking a type and returning a type.
|
||||
For those familiar with Haskell, <code>x</code> is of kind <code>* -> *</code>.</p><p>The type function is also covariant, which further ensures <code>x</code> is instantiated
|
||||
to a covariant type function.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../start/annotations/index.html">« core.typed - Annotations</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../filters/index.html">core.typed - Filters »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,241 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Quick Guide</title>
|
||||
|
||||
|
||||
<meta name="description" content="Design choicesAll vars must have annotated static types">
|
||||
|
||||
<meta property="og:description" content="Design choicesAll vars must have annotated static types">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/quick_guide/" />
|
||||
<meta property="og:title" content="core.typed - Quick Guide" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/quick_guide/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Quick Guide</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="design-choices">Design choices</h2><h3 id="all-vars-must-have-annotated-static-types">All vars must have annotated static types</h3><p>Use <code>clojure.core.typed/ann</code> to assign types to vars</p><p>eg. Assign <code>my-fn</code> in the current namespace the type <code>[Any -> Any]</code> (a function of one argument).</p><pre><code class="clojure">(ann my-fn [Any -> Any])
|
||||
</code></pre><h3 id="type-checking-is-separate-to-compilation-and-must-be-explicitly-run">Type checking is separate to compilation and must be explicitly run</h3><p>Use <code>clojure.core.typed/check-ns</code> to type check the current namespace.
|
||||
This can be done at the REPL.</p><p>Note: Global annotations like <code>ann</code> are only valid when found in a namespace currently being
|
||||
checked with <code>check-ns</code>, or wrapped in a <code>cf</code>. A raw <code>ann</code> in a REPL has <em>no effect</em>.
|
||||
Global annotations should be top-level forms or inside a (possibly nested) top-level <code>do</code>.</p><h3 id="all-function-arguments-need-to-be-annotated-or-default-to-any">All function arguments need to be annotated, or default to <code>Any</code></h3><p>Use <code>clojure.core.typed/ann-form</code> to annotate a function.</p><p>eg.</p><pre><code class="clojure">(ann-form #(+ 1 %) [Number -> Number])
|
||||
</code></pre><h3 id="everything-is-type-checked-but-coretyped-can-ignore-certain-expressions">Everything is type checked, but core.typed can ignore certain expressions</h3><p>core.typed is early in development and there are Clojure idioms it cannot
|
||||
currently type check. Wrap top-level expressions in <code>clojure.core.typed/tc-ignore</code>
|
||||
to ignore them.</p><p>Suggestion: If porting a namespace to core.typed, initially use <code>tc-ignore</code> liberally to ignore problematic
|
||||
code while determining the types for expressions. Once most vars are annotated, revisit
|
||||
these sites to determine the issue.</p><h2 id="debugging">Debugging</h2><h3 id="print-env-is-your-friend"><code>print-env</code> is your friend</h3><p><code>clojure.core.typed/print-env</code> takes a debug string and prints the local type environment at the current expression.</p><h3 id="use-cf-to-experiment-at-the-repl">Use <code>cf</code> to experiment at the REPL</h3><p><code>clojure.core.typed/cf</code> takes an expression and optionally an expected type and type checks the expression,
|
||||
returning its inferred type.</p><p>eg.</p><pre><code class="clojure">clojure.core.typed=> (cf (fn [a]
|
||||
{:pre [(number? a)]}
|
||||
(inc a)))
|
||||
[(Fn [Any -> java.lang.Number]) {:then tt, :else ff}]
|
||||
</code></pre><p>If <code>cf</code> returns a vector of results, the first element is the static type.</p><h3 id="use-ann-form-to-ensure-expressions-are-particular-types">Use <code>ann-form</code> to ensure expressions are particular types</h3><p><code>clojure.core.typed/ann-form</code> can be used as a kind of static <code>assert</code>.</p><pre><code class="clojure">clojure.core.typed=> (cf (let [a (+ 1 2)
|
||||
_ (ann-form a clojure.lang.Symbol)]
|
||||
a))
|
||||
#<AssertionError java.lang.AssertionError: Assert failed: 6: Local binding a expected type clojure.lang.Symbol, but actual type clojure.core.typed/AnyInteger
|
||||
(or (not expected) (subtype? t (ret-t expected)))>
|
||||
</code></pre><h3 id="coretyped-understands-assertions-and-conditionals">core.typed understands assertions and conditionals</h3><p>Normal "untyped" Clojure code often use type predicates combined with assertions or conditionals to direct control flow.
|
||||
core.typed uses them to gain type information about the current environment.</p><pre><code class="clojure">(let [a (ann-form 1 Number)
|
||||
_ (print-env "before assert")
|
||||
_ (assert (integer? a))
|
||||
_ (print-env "after assert")])
|
||||
; "before assert"{:env {a java.lang.Number},
|
||||
; :props ()}
|
||||
; "after assert"{:env {_28338 nil, _ nil, a clojure.core.typed/AnyInteger},
|
||||
; :props ((is clojure.core.typed/AnyInteger a) (when (! (U false nil) _) ff) (when (! (U false nil) _) ff) (when (! (U false nil) _28338) ff))}
|
||||
</code></pre><p>The <code>:env</code> map is maps local bindings to their current types.
|
||||
<code>:props</code> is a list of propositions currently in scope (can usually be ignored, mostly useful for internal debugging purposes).</p><p>Notice the local binding <code>a</code> has a more accurate type after the <code>assert</code> expression.</p><p>Note: core.typed operates on a hygienic AST, so shadowed bindings will have gensymed names.</p><h2 id="typing-core-constructs">Typing core constructs</h2><h3 id="coretyped-understands-datatype-definitions">core.typed understands datatype definitions</h3><p>Use <code>clojure.core.typed/ann-datatype</code> to give a datatype an expected type.</p><h3 id="use-defprotocol-instead-of-defprotocol">Use <code>defprotocol></code> instead of <code>defprotocol</code></h3><p>core.typed currently cannot understand protocol definitions. Simply replace references to <code>defprotocol</code>
|
||||
with <code>clojure.core.typed/defprotocol></code></p><h3 id="coretyped-understands-simple-multimethods">core.typed understands simple multimethods</h3><p>core.typed can infer accurate types for multimethods that dispatch on simple things like keywords or <code>class</code>.
|
||||
Just assign an expected type to the multimethod's var with <code>ann</code> and core.typed will use it to infer accurate
|
||||
types in each <code>defmethod</code>.</p><p>If in doubt whether a multimethod is being inferred properly, use the debugging techniques to double check.
|
||||
core.typed may not throw an exception if the dispatch is too complex to type check currently.</p><h3 id="macros--macro-definitions">Macros & Macro Definitions</h3><p>Macro definitions are ignored. The type checker operates on the macroexpanded form from
|
||||
the Compiler's analysis phase.</p><h2 id="type-syntax">Type Syntax</h2><h3 id="types-use-the-current-global-scope-of-the-namespace">Types use the current global scope of the namespace</h3><p>Simply adding an <code>(:import ...)</code> to the <code>ns</code> declaration as usual in Clojure brings the class name into scope.
|
||||
Otherwise, refers to classes via their fully qualified name.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="rationale/index.html">« core.typed - Rationale</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,241 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Quick Guide</title>
|
||||
|
||||
|
||||
<meta name="description" content="Design choicesAll vars must have annotated static types">
|
||||
|
||||
<meta property="og:description" content="Design choicesAll vars must have annotated static types">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/quick_guide/" />
|
||||
<meta property="og:title" content="core.typed - Quick Guide" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/quick_guide/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Quick Guide</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="design-choices">Design choices</h2><h3 id="all-vars-must-have-annotated-static-types">All vars must have annotated static types</h3><p>Use <code>clojure.core.typed/ann</code> to assign types to vars</p><p>eg. Assign <code>my-fn</code> in the current namespace the type <code>[Any -> Any]</code> (a function of one argument).</p><pre><code class="clojure">(ann my-fn [Any -> Any])
|
||||
</code></pre><h3 id="type-checking-is-separate-to-compilation-and-must-be-explicitly-run">Type checking is separate to compilation and must be explicitly run</h3><p>Use <code>clojure.core.typed/check-ns</code> to type check the current namespace.
|
||||
This can be done at the REPL.</p><p>Note: Global annotations like <code>ann</code> are only valid when found in a namespace currently being
|
||||
checked with <code>check-ns</code>, or wrapped in a <code>cf</code>. A raw <code>ann</code> in a REPL has <em>no effect</em>.
|
||||
Global annotations should be top-level forms or inside a (possibly nested) top-level <code>do</code>.</p><h3 id="all-function-arguments-need-to-be-annotated-or-default-to-any">All function arguments need to be annotated, or default to <code>Any</code></h3><p>Use <code>clojure.core.typed/ann-form</code> to annotate a function.</p><p>eg.</p><pre><code class="clojure">(ann-form #(+ 1 %) [Number -> Number])
|
||||
</code></pre><h3 id="everything-is-type-checked-but-coretyped-can-ignore-certain-expressions">Everything is type checked, but core.typed can ignore certain expressions</h3><p>core.typed is early in development and there are Clojure idioms it cannot
|
||||
currently type check. Wrap top-level expressions in <code>clojure.core.typed/tc-ignore</code>
|
||||
to ignore them.</p><p>Suggestion: If porting a namespace to core.typed, initially use <code>tc-ignore</code> liberally to ignore problematic
|
||||
code while determining the types for expressions. Once most vars are annotated, revisit
|
||||
these sites to determine the issue.</p><h2 id="debugging">Debugging</h2><h3 id="print-env-is-your-friend"><code>print-env</code> is your friend</h3><p><code>clojure.core.typed/print-env</code> takes a debug string and prints the local type environment at the current expression.</p><h3 id="use-cf-to-experiment-at-the-repl">Use <code>cf</code> to experiment at the REPL</h3><p><code>clojure.core.typed/cf</code> takes an expression and optionally an expected type and type checks the expression,
|
||||
returning its inferred type.</p><p>eg.</p><pre><code class="clojure">clojure.core.typed=> (cf (fn [a]
|
||||
{:pre [(number? a)]}
|
||||
(inc a)))
|
||||
[(Fn [Any -> java.lang.Number]) {:then tt, :else ff}]
|
||||
</code></pre><p>If <code>cf</code> returns a vector of results, the first element is the static type.</p><h3 id="use-ann-form-to-ensure-expressions-are-particular-types">Use <code>ann-form</code> to ensure expressions are particular types</h3><p><code>clojure.core.typed/ann-form</code> can be used as a kind of static <code>assert</code>.</p><pre><code class="clojure">clojure.core.typed=> (cf (let [a (+ 1 2)
|
||||
_ (ann-form a clojure.lang.Symbol)]
|
||||
a))
|
||||
#<AssertionError java.lang.AssertionError: Assert failed: 6: Local binding a expected type clojure.lang.Symbol, but actual type clojure.core.typed/AnyInteger
|
||||
(or (not expected) (subtype? t (ret-t expected)))>
|
||||
</code></pre><h3 id="coretyped-understands-assertions-and-conditionals">core.typed understands assertions and conditionals</h3><p>Normal "untyped" Clojure code often use type predicates combined with assertions or conditionals to direct control flow.
|
||||
core.typed uses them to gain type information about the current environment.</p><pre><code class="clojure">(let [a (ann-form 1 Number)
|
||||
_ (print-env "before assert")
|
||||
_ (assert (integer? a))
|
||||
_ (print-env "after assert")])
|
||||
; "before assert"{:env {a java.lang.Number},
|
||||
; :props ()}
|
||||
; "after assert"{:env {_28338 nil, _ nil, a clojure.core.typed/AnyInteger},
|
||||
; :props ((is clojure.core.typed/AnyInteger a) (when (! (U false nil) _) ff) (when (! (U false nil) _) ff) (when (! (U false nil) _28338) ff))}
|
||||
</code></pre><p>The <code>:env</code> map is maps local bindings to their current types.
|
||||
<code>:props</code> is a list of propositions currently in scope (can usually be ignored, mostly useful for internal debugging purposes).</p><p>Notice the local binding <code>a</code> has a more accurate type after the <code>assert</code> expression.</p><p>Note: core.typed operates on a hygienic AST, so shadowed bindings will have gensymed names.</p><h2 id="typing-core-constructs">Typing core constructs</h2><h3 id="coretyped-understands-datatype-definitions">core.typed understands datatype definitions</h3><p>Use <code>clojure.core.typed/ann-datatype</code> to give a datatype an expected type.</p><h3 id="use-defprotocol-instead-of-defprotocol">Use <code>defprotocol></code> instead of <code>defprotocol</code></h3><p>core.typed currently cannot understand protocol definitions. Simply replace references to <code>defprotocol</code>
|
||||
with <code>clojure.core.typed/defprotocol></code></p><h3 id="coretyped-understands-simple-multimethods">core.typed understands simple multimethods</h3><p>core.typed can infer accurate types for multimethods that dispatch on simple things like keywords or <code>class</code>.
|
||||
Just assign an expected type to the multimethod's var with <code>ann</code> and core.typed will use it to infer accurate
|
||||
types in each <code>defmethod</code>.</p><p>If in doubt whether a multimethod is being inferred properly, use the debugging techniques to double check.
|
||||
core.typed may not throw an exception if the dispatch is too complex to type check currently.</p><h3 id="macros--macro-definitions">Macros & Macro Definitions</h3><p>Macro definitions are ignored. The type checker operates on the macroexpanded form from
|
||||
the Compiler's analysis phase.</p><h2 id="type-syntax">Type Syntax</h2><h3 id="types-use-the-current-global-scope-of-the-namespace">Types use the current global scope of the namespace</h3><p>Simply adding an <code>(:import ...)</code> to the <code>ns</code> declaration as usual in Clojure brings the class name into scope.
|
||||
Otherwise, refers to classes via their fully qualified name.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../rationale/index.html">« core.typed - Rationale</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,227 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Rationale</title>
|
||||
|
||||
|
||||
<meta name="description" content="Static typing has well known benefits. For example, statically typed languages catch many common
|
||||
programming errors at the earliest time possible: compile time.
|
||||
Types also serve as an excellent form of (machine checkable) documentation that
|
||||
almost always augment existing hand-written documentation.Languages without static type checking (dynamically typed) bring other benefits.
|
||||
Without the strict rigidity of mandatory static typing, they can provide more flexible and forgiving
|
||||
idioms that can help in rapid prototyping.
|
||||
Often the benefits of static type checking are desired as the program grows.">
|
||||
|
||||
<meta property="og:description" content="Static typing has well known benefits. For example, statically typed languages catch many common
|
||||
programming errors at the earliest time possible: compile time.
|
||||
Types also serve as an excellent form of (machine checkable) documentation that
|
||||
almost always augment existing hand-written documentation.Languages without static type checking (dynamically typed) bring other benefits.
|
||||
Without the strict rigidity of mandatory static typing, they can provide more flexible and forgiving
|
||||
idioms that can help in rapid prototyping.
|
||||
Often the benefits of static type checking are desired as the program grows.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/rationale/" />
|
||||
<meta property="og:title" content="core.typed - Rationale" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/rationale/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Rationale</h2>
|
||||
</div>
|
||||
|
||||
<p>Static typing has well known benefits. For example, statically typed languages catch many common
|
||||
programming errors at the earliest time possible: compile time.
|
||||
Types also serve as an excellent form of (machine checkable) documentation that
|
||||
almost always augment existing hand-written documentation.</p><p>Languages without static type checking (dynamically typed) bring other benefits.
|
||||
Without the strict rigidity of mandatory static typing, they can provide more flexible and forgiving
|
||||
idioms that can help in rapid prototyping.
|
||||
Often the benefits of static type checking are desired as the program grows.</p><p>This work adds static type checking (and some of its benefits) to Clojure, a dynamically typed language,
|
||||
while still preserving idioms that characterise the language.
|
||||
It allows static and dynamically typed code to be mixed so the programmer can use whichever
|
||||
is more appropriate.</p><p>(For a detailed treatment, see my Honours Dissertation, <a href="https://github.com/downloads/frenchy64/papers/ambrose-honours.pdf">A Practical Optional Type System for Clojure</a>)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../user_documentation/index.html">« core.typed - User Documentation</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../quick_guide.html">core.typed - Quick Guide »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,275 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Annotations</title>
|
||||
|
||||
|
||||
<meta name="description" content="core.typed requires a moderate amount of assistance from the user to help infer types.There are two main things that need annotating:">
|
||||
|
||||
<meta property="og:description" content="core.typed requires a moderate amount of assistance from the user to help infer types.There are two main things that need annotating:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/start/annotations/" />
|
||||
<meta property="og:title" content="core.typed - Annotations" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/start/annotations/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Annotations</h2>
|
||||
</div>
|
||||
|
||||
<p>core.typed requires a moderate amount of assistance from the user to help infer types.</p><p>There are two main things that need annotating:</p><ol><li>All vars must be annotated</li><li>All function parameters must be annotated, or default to <code>Any</code>.</li></ol><p>From the provided annotations, core.typed uses local type inference to infer
|
||||
the types for local bindings, interop calls, and other expressions, mostly
|
||||
without further assistance.</p><h2 id="vars">Vars</h2><p>When core.typed finds a var reference, <code>def</code>, <code>binding</code>, or some other var-related construct
|
||||
that relys on the derefereced value of a var, it requires an expected type.</p><pre><code class="clojure">clojure.core.typed=> (declare abc)
|
||||
#'clojure.core.typed/abc
|
||||
clojure.core.typed=> (cf abc)
|
||||
#<AssertionError java.lang.AssertionError: Assert failed: Untyped var reference: clojure.core.typed/abc
|
||||
(contains? (clojure.core/deref *var-annotations*) nsym)>
|
||||
</code></pre><h3 id="vars-in-current-namespace">Vars in current namespace</h3><p>Use <code>clojure.core.typed/ann</code> to associate a static type with a var.</p><pre><code class="clojure">clojure.core.typed=> (cf (ann abc Number))
|
||||
[clojure.core.typed/abc java.lang.Number]
|
||||
clojure.core.typed=> (cf (def abc 1))
|
||||
clojure.lang.Var
|
||||
clojure.core.typed=> (cf abc)
|
||||
java.lang.Number
|
||||
</code></pre><p><code>ann</code> qualifies the var in the current namespace if unqualified.</p><h3 id="vars-in-other-namespaces">Vars in other namespaces</h3><p>Sometimes vars from other namespaces need annotation. Just qualify the var as you
|
||||
would in the current namespace (aliases are recognised) to associate it with a static type.</p><pre><code class="clojure">clojure.core.typed=> (cf clojure.core/*compile-path*)
|
||||
#<AssertionError java.lang.AssertionError: Assert failed: Untyped var reference: clojure.core/*compile-path*
|
||||
(contains? (clojure.core/deref *var-annotations*) nsym)>
|
||||
clojure.core.typed=> (cf (ann clojure.core/*compile-path* String))
|
||||
[clojure.core/*compile-path* java.lang.String]
|
||||
clojure.core.typed=> (cf clojure.core/*compile-path*)
|
||||
java.lang.String
|
||||
</code></pre><h3 id="unchecked-vars">Unchecked Vars</h3><p>We can instruct core.typed to ignore certain var definitions by adding <code>:nocheck</code> metadata
|
||||
to <code>ann</code> forms.</p><pre><code class="clojure">(ns typed.nocheck
|
||||
(:require [clojure.core.typed :refer [ann-nocheck ann check-ns]]))
|
||||
|
||||
(ann ^:nocheck foo [Number -> Number])
|
||||
(defn foo [a]
|
||||
'a)
|
||||
|
||||
(ann bar [Number -> Number])
|
||||
(defn bar [b]
|
||||
(+ 2 (foo b)))
|
||||
</code></pre><h3 id="var-warnings">Var Warnings</h3><p>After type checking has been performed, core.typed warns about vars that have been assigned types
|
||||
but have no corresponding checked <code>def</code> form. The <code>def</code> must at least make a binding,
|
||||
so it would be a warning if the var was only <code>declare</code>d.</p><pre><code class="clojure">(ns clojure.core.typed.test.nocheck
|
||||
(:require [clojure.core.typed :refer [ann-nocheck ann check-ns]]))
|
||||
|
||||
(ann ^:nocheck foo [Number -> Number])
|
||||
(defn foo [a]
|
||||
'a)
|
||||
|
||||
(ann bar [Number -> Number])
|
||||
(defn bar [b]
|
||||
(+ 2 (foo b)))
|
||||
|
||||
;(check-ns)
|
||||
; ...
|
||||
; WARNING: Var clojure.core.typed.test.var-usage/foo used without checking definition
|
||||
;=> nil
|
||||
</code></pre><h2 id="functions">Functions</h2><p>There are several ways to annotate a function type.</p><h3 id="partial-annotation-with-fn">Partial annotation with <code>fn></code></h3><p>To annotate just the arguments of a <code>fn</code>, use the <code>fn></code> wrapper. It is exactly like <code>fn</code>,
|
||||
except each argument is wrapped in a vector which includes its static type.</p><pre><code class="clojure">clojure.core.typed=> (cf (fn> [[a :- Number]] (+ a 1)))
|
||||
[(Fn [java.lang.Number -> java.lang.Number]) {:then tt, :else ff}]
|
||||
</code></pre><p>All the usual destructuring is supported.</p><pre><code class="clojure">clojure.core.typed=> (cf (fn> [[{:keys [a b c]} :- '{:a Number :b Long :c Double}]]
|
||||
[a b c]))
|
||||
[(Fn ['{:a java.lang.Number, :b java.lang.Long, :c java.lang.Double} -> '[java.lang.Number java.lang.Long java.lang.Double]])
|
||||
{:then tt, :else ff}]
|
||||
</code></pre><h3 id="full-annotation-with-ann-form">Full annotation with <code>ann-form</code></h3><p>Often it is more useful to provide a full function type as a <code>fn</code>'s annotation. This
|
||||
especially works well with Clojure's anonymous function syntax.</p><pre><code class="clojure">clojure.core.typed=> (cf (ann-form #(inc %)
|
||||
[Number -> Number]))
|
||||
(Fn [java.lang.Number -> java.lang.Number])
|
||||
</code></pre><p>This way, you can also assign anonymous functions ordered intersection function types.</p><pre><code class="clojure">clojure.core.typed=> (cf (fn [a]
|
||||
(cond
|
||||
(number? a) 1
|
||||
(symbol? a) 'a))
|
||||
(Fn [Number -> Number]
|
||||
[Symbol -> Symbol]))
|
||||
(Fn [java.lang.Number -> java.lang.Number]
|
||||
[clojure.lang.Symbol -> clojure.lang.Symbol])
|
||||
</code></pre>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../../types/index.html">« core.typed - Types</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../../poly_fn/index.html">core.typed - Polymorphic Functions »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,237 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Getting Started: Introduction and Motivation</title>
|
||||
|
||||
|
||||
<meta name="description" content="core.typed is an optional type system for Clojure. If you are interesting in how core.typed
|
||||
can help you verify your programs as correct, read on."I use Clojure to avoid types!"">
|
||||
|
||||
<meta property="og:description" content="core.typed is an optional type system for Clojure. If you are interesting in how core.typed
|
||||
can help you verify your programs as correct, read on."I use Clojure to avoid types!"">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/start/introduction_and_motivation/" />
|
||||
<meta property="og:title" content="core.typed - Getting Started: Introduction and Motivation" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/start/introduction_and_motivation/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Getting Started: Introduction and Motivation</h2>
|
||||
</div>
|
||||
|
||||
<p>core.typed is an optional type system for Clojure. If you are interesting in how core.typed
|
||||
can help you verify your programs as correct, read on.</p><h2 id="i-use-clojure-to-avoid-types">"I use Clojure to avoid types!"</h2><p>Many programmers use Clojure as relief from popular typed languages such as Java or C#.
|
||||
Java's verbosity and redundant type annotations help make the move to Clojure feel liberating
|
||||
and enjoyable: so why go back to types?</p><p>core.typed has a different story to tell:</p><ul><li>type checking is optional
|
||||
<ul><li>only use the type system where you need it</li></ul></li><li>local type inference is used to infer local bindings
|
||||
<ul><li>locals rarely need type annotations</li></ul></li><li>it can type check (mostly) normal Clojure code
|
||||
<ul><li>the Clojure you know and love!</li></ul></li></ul><p>If Java has driven you away from types, core.typed could be pleasant surprise.
|
||||
It might even become one of your go-to tools for code verification in Clojure.</p><h2 id="what-are-types">What are types?</h2><p>This is a good question, especially in the context of a dynamically-typed (DT) language
|
||||
where we don't have "types".</p><p>We use the term "type" to mean static type and "tag" for runtime tags.
|
||||
Types only exist at compile time and are used by the static type system to model runtime
|
||||
invariants and properties.</p><p>Thinking of compile-time and runtime as distinct phases in terms of types often helps.
|
||||
The type system uses types to reason about the runtime behaviour of code, which can
|
||||
also include tag invariants.</p><p>There are no types in Clojure, only tags. We can also say that Clojure has exactly one type: <code>Any</code> (subtype of all types).
|
||||
The closest equivalent to types we have
|
||||
are ad-hoc comments or doc-strings which describe the input/output behaviour
|
||||
of functions.</p><p>For example, the <code>number?</code> predicate returns true if its (runtime) argument
|
||||
has a tag that is a subtype of <code>java.lang.Number</code>, otherwise false. In core.typed
|
||||
we use a type to model these invariants.
|
||||
The tag of <code>number?</code> might be <code>IFn</code>, while its type is <code>[Any -> boolean :filters {:then (is Number 0) :else (! Number 0)}]</code>.</p><p>In summary:</p><ul><li>types only exist at compile time</li><li>tags only exist at runtime</li></ul><h2 id="why-types">Why types?</h2><p>Why use types at all? A static type checker gives earlier and often clearer type errors.</p><p>For example, you might observe:</p><ul><li>fewer "Boolean is not an ISeq" errors without line numbers in production</li><li>more "Cannot pass Boolean to second argument of map" with line numbers at compile time in development.</li></ul><p>Types, when coupled with an appropriate doc-string, are excellent machine checkable documentation.
|
||||
They never go out of date, and are often invaluable as a quick reminder of what a function does.</p><p>Types are useful when a program grows, especially when there are multiple contributors.
|
||||
If a contribution passes the type system, we know that it is type correct (type errors are amongst
|
||||
the most common user errors in programming).</p><h2 id="great-types-are-the-answer">Great, types are the answer!</h2><p>Not quite. Types can help verify that a program is basically correct, but not if it does the right thing.
|
||||
Use as many verification techniques as you can: core.typed works great coupled with unit testing or
|
||||
generative testing.</p><p>Clojure simply is not built with static typing in mind. It is impractical to expect core.typed alone
|
||||
to prevent as many user errors as say Haskell's type system: core.typed either needs to choose some
|
||||
subset of Clojure optimised for user error prevention, or attempt to check all Clojure code
|
||||
while making some compromises (it does the latter).</p><p>This might seem discouraging, but in practice core.typed will catch all type errors in your code.
|
||||
The problem is some Clojure idioms are so flexible it is often impossible to distinguish
|
||||
between intended and unintended usage.</p><p>A small example: <code>map</code> accepts either <code>nil</code> or a <code>Seqable</code> as a second argument. It is perfectly
|
||||
valid to provide an argument that is always <code>nil</code>, but it's probably not what the user intended.</p><p>So for best results, couple core.typed with all the usual testing/verification techniques.</p><h2 id="before-we-begin">Before we begin</h2><p>There are some details to keep in mind when using core.typed before you jump in to use it.</p><p>Read the <a href="../../quick_guide.html">Quick Guide</a>, and keep a copy handy when you follow along the rest of the tutorial.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../../quick_guide.html">« core.typed - Quick Guide</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../../types/index.html">core.typed - Types »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,338 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - Types</title>
|
||||
|
||||
|
||||
<meta name="description" content="Common typesAny and Nothing">
|
||||
|
||||
<meta property="og:description" content="Common typesAny and Nothing">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/types/" />
|
||||
<meta property="og:title" content="core.typed - Types" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/types/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - Types</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="common-types">Common types</h2><h3 id="any-and-nothing">Any and Nothing</h3><p>Every type is a subtype to <code>Any</code>, written <code>x <: Any</code> for all types <code>x</code>. Equivalently, any place that <code>Any</code> is valid,
|
||||
any other type can be used. <code>Any</code> is also known as the "Top" type.</p><p>Conversely, there are no types that are subtypes to <code>Nothing</code>. However for all types <code>x</code>
|
||||
it holds that <code>Nothing <: x</code>. Put another way, <code>Nothing</code> is a valid type to give use
|
||||
in positions expecting any other type. In practice, <code>Nothing</code> is not as useful as <code>Any</code>,
|
||||
and is usually used internally to detect dead code and other code properties.</p><h3 id="functions">Functions</h3><p>core.typed has a special function type, which is an <em>ordered intersection</em> of arities.
|
||||
It allows us to specify fine grained function invariants.</p><p>Starting simply,</p><pre><code class="clojure">(Fn [Any -> Any])
|
||||
</code></pre><p>is a function taking one argument of type <code>Any</code>. <code>[Any -> Any]</code>
|
||||
is an equivalent shorthand for single-arity function types.</p><h4 id="multiple-arities">Multiple arities</h4><p>We can specify multiple arities:</p><pre><code class="clojure">(Fn [Any -> Any]
|
||||
[Any Any -> Any])
|
||||
</code></pre><p>Here we can call a function of this type with either one or two arguments.
|
||||
In this case, the ordered intersection type acts as a simple overloading on arity.</p><p>Finer invariants can be expressed by specifying multiple signatures of the same arity:</p><pre><code class="clojure">(Fn [Symbol -> Number]
|
||||
[Number -> Symbol])
|
||||
</code></pre><p>This function returns a <code>Number</code> if passed a <code>Symbol</code>, and returns a <code>Symbol</code> if passed a <code>Number</code>.</p><p>The exact return type for a function application expression involving multiple arities
|
||||
is chosen by matching the actual types provided with each arities, top-to-bottom
|
||||
(this explains why our functions are "ordered" intersections).
|
||||
In this case, each arity is disjoint because no combination of arguments could
|
||||
potentially trigger both arities. More concretely, there is no type that is both
|
||||
a <code>Symbol</code> and a <code>Number</code>, so at most one arity triggers for any given arguments.</p><p>Overlapping arities hints at the power of ordered intersections.</p><pre><code class="clojure">(Fn [Long -> Symbol]
|
||||
[Number -> Keyword])
|
||||
</code></pre><p>This type always returns a <code>Symbol</code> for <code>Long</code> arguments.</p><p>Beware, swapping the arities produces different results!</p><pre><code class="clojure">(Fn [Number -> Keyword]
|
||||
[Long -> Symbol])
|
||||
</code></pre><p>The first arity always "wins" because <code>Number</code> is strictly more general than <code>Long</code>.
|
||||
Arities are usually ordered from more-specific parameters to less-specific parameters.</p><p>What about arities that have partially overlapping parameters?
|
||||
Consider:</p><pre><code class="clojure">(Fn [Long Any -> Keyword]
|
||||
[Any Number -> Symbol])
|
||||
</code></pre><p>Calling with <code>Long</code> <code>Long</code> arguments gives <code>Keyword</code>, and <code>Number</code> <code>Long</code> gives <code>Symbol</code>.</p><p>Flipping the arities gives different results:</p><pre><code class="clojure">(Fn [Any Number -> Symbol]
|
||||
[Long Any -> Keyword])
|
||||
</code></pre><p>Now <code>Long</code> <code>Long</code> gives <code>Symbol</code>, and <code>Number</code> <code>Long</code> gives <code>Symbol</code>.
|
||||
Partially overlapping arities can be tricky and can unexpectedly trigger earlier arities,
|
||||
so care must be taken here.</p><p>Finally, a common idiom is to provide a base arity, which has arguments at least as general
|
||||
as the ones above it.</p><p>For example, we might want our function of type <code>(Fn [Long -> Symbol] [Number -> Keyword])</code> to handle the case where
|
||||
the argument is <em>either</em> a <code>Long</code> or a <code>Number</code>.
|
||||
We can express this by using a union (to express a least-upper-bound of <code>Long</code> and <code>Number</code>).</p><pre><code class="clojure">(Fn [Long -> Symbol]
|
||||
[Number -> Keyword]
|
||||
[(U Long Number) -> (U Symbol Keyword)])
|
||||
</code></pre><p>Note the result type is sufficiently general to show the result type is either a <code>Symbol</code> or <code>Keyword</code>.</p><h4 id="rest-parameters">Rest parameters</h4><p>Rest parameters are specified using a <code>*</code>.</p><p>eg.</p><pre><code class="clojure">(Fn [Any Number * -> Any])
|
||||
</code></pre><p>is a function taking at least one parameter, and any number of parameters after it
|
||||
of type <code>Number</code>.</p><h4 id="keyword-parameters">Keyword parameters</h4><p>Keyword parameters are specified using <code>&</code> after the fixed domain.</p><p>eg.</p><pre><code class="clojure">(Fn [Any & {:a Number} -> Any])
|
||||
</code></pre><p>is a function that takes a fixed parameter and an optional keyword argument <code>:a</code>, of
|
||||
type <code>Number</code>.</p><p>We can also specify mandatory keyword parameters:</p><pre><code class="clojure">(Fn [Any & {} :mandatory {:a Number} -> Any])
|
||||
</code></pre><p>is the same function, except the keyword argumetn <code>:a</code> now must be present when calling.</p><p>We can express finer grained invariants by combining keyword types and ordered
|
||||
function intersection types:</p><pre><code class="clojure">(Fn [Any & {} :mandatory {:a Number :b Number} -> Number]
|
||||
[Any & {:a Number :b Number} -> Any])
|
||||
</code></pre><p>This function type returns a <code>Number</code> if provided both <code>:a</code> and <code>:b</code> parameters,
|
||||
otherwise returns <code>Any</code> if some other combination of <code>:a</code> and <code>:b</code> is provided.</p><h3 id="java-classes">Java Classes</h3><p>core.typed reuses Java and clojure.lang.* classes. The normal scoping rules apply in types,
|
||||
e.g., use <code>:import</code> to bring classes into scope.</p><p>Note: <code>java.lang.*</code> classes are implicitly in scope in Clojure namespaces.</p><h3 id="numbers-strings-and-other-java-types">Numbers, Strings and other Java types</h3><p>core.typed follows the normal rules that apply to Clojure code.</p><pre><code class="clojure">clojure.core.typed=> (cf 1 Long)
|
||||
java.lang.Long
|
||||
clojure.core.typed=> (cf 1.1 Double)
|
||||
java.lang.Double
|
||||
clojure.core.typed=> (cf "a" String)
|
||||
java.lang.String
|
||||
clojure.core.typed=> (cf \a Character)
|
||||
java.lang.Character
|
||||
</code></pre><h3 id="symbols-and-keywords">Symbols and Keywords</h3><p>Symbols and Keywords are instances of their corresponding clojure.lang classes.</p><pre><code class="clojure">clojure.core.typed=> (cf 'a clojure.lang.Symbol)
|
||||
clojure.lang.Symbol
|
||||
clojure.core.typed=> (cf :a clojure.lang.Keyword)
|
||||
clojure.lang.Keyword
|
||||
</code></pre><h3 id="seqables">Seqables</h3><p>Seqables extend <code>(Seqable a)</code>, which is covariant in its argument.
|
||||
Types that extend <code>(Seqable a</code>) are capable of creating a sequence
|
||||
(aka. an <code>(ISeq a)</code>) representation of itself via functions like <code>seq</code>.</p><pre><code class="clojure">clojure.core.typed=> (cf {'a 2 'b 3} (Seqable (IMapEntry Symbol Number)))
|
||||
(clojure.lang.Seqable (clojure.lang.IMapEntry clojure.lang.Symbol java.lang.Number))
|
||||
clojure.core.typed=> (cf [1 2 3] (Seqable Number))
|
||||
(clojure.lang.Seqable java.lang.Number)
|
||||
clojure.core.typed=> (cf '#{a b c} (Seqable Symbol))
|
||||
(clojure.lang.Seqable clojure.lang.Symbol)
|
||||
</code></pre><h3 id="seqs">Seqs</h3><p>Seqs extend <code>(ISeq a)</code>, which is covariant in its argument.</p><pre><code class="clojure">clojure.core.typed=> (cf (seq [1 2]) (ISeq Number))
|
||||
(clojure.lang.ISeq java.lang.Number)
|
||||
</code></pre><h3 id="lists">Lists</h3><p>Lists extend <code>(IPersistentList a)</code>, which is covariant in its argument.</p><pre><code class="clojure">clojure.core.typed=> (cf '(1 2) (IPersistentList Number))
|
||||
(clojure.lang.IPersistentList java.lang.Number)
|
||||
</code></pre><h3 id="vectors">Vectors</h3><p>Vectors extend <code>(IPersistentVector a)</code>, which is covariant in its argument.</p><pre><code class="clojure">clojure.core.typed=> (cf [1 2] (IPersistentVector Number))
|
||||
(clojure.lang.IPersistentVector java.lang.Number)
|
||||
</code></pre><h3 id="maps">Maps</h3><p>Maps extend <code>(IPersistentMap a b)</code>, which is covariant in both its arguments.</p><pre><code class="clojure">clojure.core.typed=> (cf {'a 1 'b 3} (IPersistentMap Symbol Long))
|
||||
(clojure.lang.IPersistentMap clojure.lang.Symbol java.lang.Long)
|
||||
</code></pre><h3 id="sets">Sets</h3><p>Sets extend <code>(IPersistentSet a)</code>, which is covariant in its argument.</p><pre><code class="clojure">clojure.core.typed=> (cf #{1 2 3} (IPersistentSet Number))
|
||||
(clojure.lang.IPersistentSet java.lang.Number)
|
||||
</code></pre><h3 id="atoms">Atoms</h3><p>An Atom of type <code>(Atom w r)</code> can accept values of type <code>w</code> and provide values of type <code>r</code>.
|
||||
It is contravariant in <code>w</code> and covariant in <code>r</code>.</p><p>Usually <code>w</code> and <code>r</code> are identical, so an alias <code>(clojure.core.typed/Atom1 wr)</code> is provided,
|
||||
which is equivalent to <code>(Atom wr wr)</code>.</p><pre><code class="clojure">clojure.core.typed=> (cf (atom {}) (Atom1 (IPersistentMap Symbol Number)))
|
||||
(clojure.core.typed/Atom1 (clojure.lang.IPersistentMap clojure.lang.Symbol java.lang.Number))
|
||||
</code></pre><h2 id="type-grammar">Type Grammar</h2><p>A rough grammar for core.typed types.</p><pre><code>Type := nil
|
||||
| true
|
||||
| false
|
||||
| (U Type*)
|
||||
| (I Type+)
|
||||
| FunctionIntersection
|
||||
| (Value CONSTANT-VALUE)
|
||||
| (Rec [Symbol] Type)
|
||||
| (All [Symbol+] Type)
|
||||
| (All [Symbol* Symbol ...] Type)
|
||||
| (HMap {Keyword Type*}) ;eg (HMap {:a (Value 1), :b nil})
|
||||
| '{Keyword Type*} ;eg '{:a (Value 1), :b nil}
|
||||
| (Vector* Type*)
|
||||
| '[Type*]
|
||||
| (Seq* Type*)
|
||||
| (List* Type*)
|
||||
| Symbol ;class/protocol/free resolvable in context
|
||||
|
||||
FunctionIntersection := ArityType
|
||||
| (Fn ArityType+)
|
||||
|
||||
ArityType := [FixedArgs -> Type]
|
||||
| [FixedArgs RestArgs * -> Type]
|
||||
| [FixedArgs DottedType ... Symbol -> Type]
|
||||
|
||||
FixedArgs := Type*
|
||||
RestArgs := Type
|
||||
DottedType := Type
|
||||
</code></pre><h2 id="types">Types</h2><h3 id="value-shorthands">Value shorthands</h3><p><code>nil</code>, <code>true</code> and <code>false</code> resolve to the respective singleton types for those values</p><h3 id="intersections">Intersections</h3><p><code>(I Type+)</code> creates an intersection of types.</p><h3 id="unions">Unions</h3><p><code>(U Type*)</code> creates a union of types.</p><h3 id="functions-1">Functions</h3><p>A function type is an ordered intersection of arity types.</p><p>There is a vector sugar for functions of one arity.</p><h3 id="heterogeneous-maps">Heterogeneous Maps</h3><p><em>Warning</em>: Heterogeneous maps are alpha and their design is subject to change.</p><p>A heterogeneous map type represents a map that has at least a particular set of keyword keys.</p><pre><code class="clojure">clojure.core.typed=> (cf {:a 1})
|
||||
[(HMap {:a (Value 1)}) {:then tt, :else ff}]
|
||||
</code></pre><p>This type can also be written <code>'{:a (Value 1)}</code>.</p><p>Lookups of known keys infer accurate types.</p><pre><code class="clojure">clojure.core.typed=> (cf (-> {:a 1} :a))
|
||||
(Value 1)
|
||||
</code></pre><p>Currently, they are limited (but still quite useful):</p><ul><li>the presence of keys is recorded, but not their absence</li><li>only keyword value keys are allowed.</li></ul><p>These rules have several implications.</p><h4 id="absent-keys">Absent keys</h4><p>Looking up keys that are not recorded as present give inaccurate types</p><pre><code class="clojure">clojure.core.typed=> (cf (-> {:a 1} :b))
|
||||
Any
|
||||
</code></pre><h4 id="non-keyword-keys">Non-keyword keys</h4><p>Literal maps without keyword keys are inferred as <code>APersistentMap</code>.</p><pre><code class="clojure">clojure.core.typed=> (cf {(inc 1) 1})
|
||||
[(clojure.lang.APersistentMap clojure.core.typed/AnyInteger (Value 1)) {:then tt, :else ff}]
|
||||
</code></pre><p>Optional keys can be defined either by constructing a union of map types, or by passing
|
||||
the <code>HMap</code> type constructor an <code>:optional</code> keyword argument with a map of optional keys.</p><h3 id="heterogeneous-vectors">Heterogeneous Vectors</h3><p><code>(Vector* (Value 1) (Value 2))</code> is a IPersistentVector of length 2, essentially
|
||||
representing the value <code>[1 2]</code>. The type <code>'[(Value 1) (Value 2)]</code> is identical.</p><h3 id="polymorphism">Polymorphism</h3><p>The binding form <code>All</code> introduces a number of free variables inside a scope.</p><p>Optionally scopes a dotted variable by adding <code>...</code> after the last symbol in the binder.</p><p>eg. The identity function: <code>(All [x] [x -> x])</code>
|
||||
eg. Introducing dotted variables: `(All [x y ...] [x y ... y -> x])</p><h3 id="recursive-types">Recursive Types</h3><p><code>Rec</code> introduces a recursive type. It takes a vector of one symbol and a type.
|
||||
The symbol is scoped to represent the entire type in the type argument.</p><pre><code class="clojure">; Type for {:op :if
|
||||
; :test {:op :var, :var #'A}
|
||||
; :then {:op :nil}
|
||||
; :else {:op :false}}
|
||||
(Rec [x]
|
||||
(U (HMap {:op (Value :if)
|
||||
:test x
|
||||
:then x
|
||||
:else x})
|
||||
(HMap {:op (Value :var)
|
||||
:var clojure.lang.Var})
|
||||
(HMap {:op (Value :nil)})
|
||||
(HMap {:op (Value :false)})))))
|
||||
</code></pre>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../start/introduction_and_motivation/index.html">« core.typed - Getting Started: Introduction and Motivation</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../start/annotations/index.html">core.typed - Annotations »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,257 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: core.typed - User Documentation</title>
|
||||
|
||||
|
||||
<meta name="description" content="UsageType Aliases">
|
||||
|
||||
<meta property="og:description" content="UsageType Aliases">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/core_typed/user_documentation/" />
|
||||
<meta property="og:title" content="core.typed - User Documentation" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/core_typed/user_documentation/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>core.typed - User Documentation</h2>
|
||||
</div>
|
||||
|
||||
<h1 id="usage">Usage</h1><h2 id="type-aliases">Type Aliases</h2><p><code>clojure.core.typed/def-alias</code> defines a type alias.</p><pre><code class="clojure">(def-alias Term (I IUnifyTerms
|
||||
IUnifyWithNil
|
||||
IUnifyWithObject
|
||||
IUnifyWithLVar
|
||||
IUnifyWithSequential
|
||||
IUnifyWithMap
|
||||
IUnifyWithSet
|
||||
IReifyTerm
|
||||
IWalkTerm
|
||||
IOccursCheckTerm
|
||||
IBuildTerm))
|
||||
</code></pre><h2 id="primitive-java-arrays">Primitive Java Arrays</h2><p>"Typed" arrays can be created with <code>into-array></code>, which has a 2 and 3 arity version.</p><p>The correspondence between core.typed types and Java types is subtle here. Usually
|
||||
<code>into-array></code> accepts a core.typed type as its first argument, followed by a collection (as <code>clojure.core/into-array</code>).</p><pre><code class="clojure">;; `int` is the primitive int in Java. This creates a primitive array.
|
||||
(class (into-array> int [1]))
|
||||
;=> [I
|
||||
|
||||
;; This is a Number array with nullable elements.
|
||||
(class (into-array> (U nil Number) [1]))
|
||||
;=> [Ljava.lang.Number;
|
||||
|
||||
;; This is a Number array with non-nullable elements.
|
||||
;; Notice this generates the same type as before as Java does not distinguish
|
||||
;; non-/nullable arrays. core.typed statically disallows nil to be added
|
||||
;; as an element from any Clojure it checks.
|
||||
(class (into-array> Number [1]))
|
||||
;=> [Ljava.lang.Number;
|
||||
|
||||
;; An array of nullable primitive ints does not make sense in Java,
|
||||
;; so it is generalised to an array of Objects.
|
||||
(class (into-array> (U nil int) [1]))
|
||||
;=> [Ljava.lang.Object;
|
||||
|
||||
;; Unions are often generalised to Object
|
||||
(class (into-array> (U clojure.lang.Symbol Number) [1]))
|
||||
;=> [Ljava.lang.Object;
|
||||
</code></pre><p>When more control is needed of the Java type, the 3 arity version of <code>into-array></code> accepts
|
||||
the Java type (in core.typed syntax) as first argument, followed by the Clojure type, and the collection.</p><pre><code class="clojure">;; Generalising to Number instead of Object.
|
||||
(class (into-array> Number (U Integer Number) [1]))
|
||||
;=> [Ljava.lang.Number;
|
||||
</code></pre><p>The Clojure element type should be a subtype to the Java element type.</p><h2 id="declarations">Declarations</h2><p><code>clojure.core.typed/declare-types</code>, <code>clojure.core.typed/declare-names</code> and <code>clojure.core.typed/declare-protocols</code> are similar
|
||||
to <code>declare</code> in that they allow you to use types before they are defined.</p><pre><code class="clojure">(declare-datatypes Substitutions)
|
||||
(declare-protocols LVar)
|
||||
(declare-names MyAlias)
|
||||
</code></pre><h2 id="checking-typed-namespaces">Checking typed namespaces</h2><p><code>clojure.core.typed/check-ns</code> checks the namespace that its symbol argument represents.</p><pre><code class="clojure">(check-ns 'my.ns)
|
||||
</code></pre><h2 id="debugging">Debugging</h2><p><code>clojure.core.typed/print-env</code> prints the current environment.</p><pre><code class="clojure">(let [a 1]
|
||||
(print-env "Env:")
|
||||
a)
|
||||
; Prints: "Env:" {:env {a (Value 1)}, ....}
|
||||
</code></pre><p><code>clojure.core.typed/cf</code> (pronounced "check form") can be used at the REPL to return the type of a form.</p><pre><code class="clojure">(cf 1)
|
||||
;=> [(Value 1) {:then [top-filter], :else [bot-filter]} empty-object]
|
||||
</code></pre><h2 id="macros--macro-definitions">Macros & Macro Definitions</h2><p>Macro definitions are ignored. The type checker operates on the macroexpanded form from
|
||||
the Compiler's analysis phase.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../home/index.html">« core.typed - User Documentation Home</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../rationale/index.html">core.typed - Rationale »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,208 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Data Processing (Help Wanted)</title>
|
||||
|
||||
|
||||
<meta name="description" content="Help wantedPlease follow the instructions on how to contribute and start writing over here">
|
||||
|
||||
<meta property="og:description" content="Help wantedPlease follow the instructions on how to contribute and start writing over here">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/data_processing/" />
|
||||
<meta property="og:title" content="Data Processing (Help Wanted)" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/data_processing/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Data Processing (Help Wanted)</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="help-wanted">Help wanted</h2><p>Please follow the <a href="https://github.com/clojure-doc/clojure-doc.github.io/tree/source#how-to-contribute">instructions</a> on how to contribute and start writing over <a href="https://github.com/clojure-doc/clojure-doc.github.io/blob/source/content/md/articles/ecosystem/data_processing.md">here</a></p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../generating_documentation/index.html">« Generating Documentation</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../web_development/index.html">Web Development (Overview) »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,231 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Generating Documentation</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide notes some commonly-used tools for generating project
|
||||
documentation.This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.">
|
||||
|
||||
<meta property="og:description" content="This guide notes some commonly-used tools for generating project
|
||||
documentation.This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/generating_documentation/" />
|
||||
<meta property="og:title" content="Generating Documentation" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/generating_documentation/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Generating Documentation</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide notes some commonly-used tools for generating project
|
||||
documentation.</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.4.</p><h2 id="overview">Overview</h2><p>Projects commonly (hopefully?) have at least two types of
|
||||
documentation:</p><ul><li>standalone
|
||||
<a href="http://en.wikipedia.org/wiki/Markdown">markdown</a>-formatted docs
|
||||
in the project's doc directory</li><li>docstrings</li></ul><p>There are a number of tools for generating handsome API docs from
|
||||
docstrings and other project metadata.</p><h2 id="codox">Codox</h2><p>If you'd like to generate nice-looking html API docs for your library,
|
||||
use <a href="https://github.com/weavejester/codox">codox</a>. Usage instructions
|
||||
are in the codox readme. Running codox (it's a lein plug-in and is run
|
||||
via <code>lein codox</code> in your project) will create a "doc" subdirectory
|
||||
containing the resulting html.</p><h2 id="marginalia">Marginalia</h2><p>If you'd like to render API docs side-by-side with the source code
|
||||
it's documenting, use <a href="https://github.com/fogus/lein-marginalia">the marginalia lein
|
||||
plug-in</a>. Usage instructions
|
||||
are in the readme.</p><h2 id="cadastre">Cadastre</h2><p>If you'd like to generate copious raw data from a project (which
|
||||
includes docstrings as well as other metadata), have a look at
|
||||
<a href="https://github.com/dakrone/cadastre">cadastre</a>.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../libraries_authoring/index.html">« Library Development and Distribution</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../data_processing/index.html">Data Processing (Help Wanted) »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
310
clones/clojure-doc.org/articles/ecosystem/java_jdbc/home.html
Normal file
310
clones/clojure-doc.org/articles/ecosystem/java_jdbc/home.html
Normal file
|
@ -0,0 +1,310 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: java.jdbc - Getting Started</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide is intended to help you the Clojure Contrib JDBC wrapper: clojure.java.jdbcA modern JDBC wrapper has since been written (by the same author/maintainer) -- read about next.jdbc on cljdoc.org">
|
||||
|
||||
<meta property="og:description" content="This guide is intended to help you the Clojure Contrib JDBC wrapper: clojure.java.jdbcA modern JDBC wrapper has since been written (by the same author/maintainer) -- read about next.jdbc on cljdoc.org">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/home/" />
|
||||
<meta property="og:title" content="java.jdbc - Getting Started" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/home/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>java.jdbc - Getting Started</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide is intended to help you the Clojure Contrib JDBC wrapper: <code>clojure.java.jdbc</code></p><p><strong>A modern JDBC wrapper has since been written (by the same author/maintainer) -- read about <a href="https://clojure-doc.org/articles/ecosystem/java_jdbc/home/cljdoc.org/d/com.github.seancorfield/next.jdbc/"><code>next.jdbc</code> on cljdoc.org</a></strong></p><h2 id="contents">Contents</h2><ul><li><a href="home.html">Overview</a></li><li><a href="using_sql.html">Using SQL</a></li><li><a href="using_ddl.html">Using DDL</a></li><li><a href="reusing_connections.html">Reusing Connections</a></li></ul><h2 id="overview">Overview</h2><p><code>java.jdbc</code> is intended to be a low-level Clojure wrapper around various Java
|
||||
JDBC drivers and supports a wide range of databases. The <a href="https://github.com/clojure/java.jdbc/"><code>java.jdbc</code> source is
|
||||
on GitHub</a> and there is a dedicated <a href="https://groups.google.com/forum/#!forum/clojure-java-jdbc">java.jdbc mailing
|
||||
list</a>. The detailed <a href="http://clojure.github.io/java.jdbc/"><code>java.jdbc</code> reference</a> is
|
||||
automatically generated from the <code>java.jdbc</code> source.</p><p>Generally, when using <code>java.jdbc</code>, you will set up a data source as a "database
|
||||
spec" and pass that to the various CRUD (create, read, update, delete)
|
||||
functions that <code>java.jdbc</code> provides. These operations are detailed within the
|
||||
<a href="using_sql.html">Using SQL</a> page, but a quick overview is provided by the
|
||||
walkthrough below.</p><p>By default, each operation opens a connection and executes the SQL inside a
|
||||
transaction. You can also run multiple operations against the same connection,
|
||||
either within a transaction or via connection pooling, or just with a shared
|
||||
connection. You can read more about reusing connections on the <a href="reusing_connections.html">Reusing
|
||||
Connections</a> page.</p><h2 id="higher-level-dsl-and-migration-libraries">Higher-level DSL and migration libraries</h2><p>If you need more abstraction than the <code>java.jdbc</code> wrapper provides, you may want
|
||||
to consider using a library that provides a DSL. All of the following libraries
|
||||
are built on top of <code>java.jdbc</code> and provide such abstraction:</p><ul><li><a href="https://github.com/jkk/honeysql">HoneySQL</a></li><li><a href="https://github.com/r0man/sqlingvo">SQLingvo</a></li><li><a href="http://sqlkorma.com">Korma</a></li><li><a href="https://github.com/walkable-server/walkable">Walkable</a></li></ul><p>In particular, <a href="http://sqlkorma.com">Korma</a> goes beyond a SQL DSL to provide "entities" and
|
||||
"relationships" (in the style of classical Object-Relational Mappers, but
|
||||
without the pain).</p><p>Another common need with SQL is for database migration libraries. Some of the
|
||||
more popular options are:</p><ul><li><a href="https://github.com/macourtney/drift">Drift</a></li><li><a href="https://github.com/pjstadig/migratus">Migratus</a></li><li><a href="https://github.com/weavejester/ragtime">Ragtime</a></li></ul><h2 id="a-brief-javajdbc-walkthrough">A brief <code>java.jdbc</code> walkthrough</h2><h3 id="setting-up-a-data-source">Setting up a data source</h3><p>A "database spec" is a Clojure map that specifies how to access the data
|
||||
source. Most commonly, you specify the database type, the database name,
|
||||
and the username and password. For example,</p><pre><code class="clojure">(def db-spec
|
||||
{:dbtype "mysql"
|
||||
:dbname "mydb"
|
||||
:user "myaccount"
|
||||
:password "secret"})
|
||||
</code></pre><p>See <a href="home.html#database-support"><strong>Database Support</strong></a> below for a complete list of
|
||||
databases and drivers supported by <code>java.jdbc</code> out of the box.</p><h3 id="a-hello-world-query">A "Hello World" Query</h3><p>Querying the database can be as simple as:</p><pre><code class="clojure">(ns dbexample
|
||||
(:require [clojure.java.jdbc :as jdbc]))
|
||||
|
||||
(def db-spec ... ) ;; see above
|
||||
|
||||
(jdbc/query db-spec ["SELECT 3*5 AS result"])
|
||||
=> {:result 15}
|
||||
</code></pre><p>Of course, we will want to do more with our database than have it perform
|
||||
simple calculations. Once we can successfully connect to it, we will likely
|
||||
want to create tables and manipulate data.</p><h3 id="creating-tables">Creating tables</h3><p><code>java.jdbc</code> provides <code>create-table-ddl</code> and <code>drop-table-ddl</code> to generate basic
|
||||
<code>CREATE TABLE</code> and <code>DROP TABLE</code> DDL strings. Anything beyond that can be
|
||||
constructed manually as a string.</p><pre><code class="clojure">(ns dbexample
|
||||
(:require [clojure.java.jdbc :as jdbc]))
|
||||
|
||||
(def db-spec ... ) ;; see above
|
||||
|
||||
(def fruit-table-ddl
|
||||
(jdbc/create-table-ddl :fruit
|
||||
[[:name "varchar(32)"]
|
||||
[:appearance "varchar(32)"]
|
||||
[:cost :int]
|
||||
[:grade :real]]))
|
||||
</code></pre><p>We can use the function <code>db-do-commands</code> to create our table and indexes in a
|
||||
single transaction:</p><pre><code class="clojure">(jdbc/db-do-commands db-spec
|
||||
[fruit-table-ddl
|
||||
"CREATE INDEX name_ix ON fruit ( name );"])
|
||||
</code></pre><p>For more details on DDL functionality within <code>java.jdbc</code>, see the <a href="using_ddl.html">Using DDL and
|
||||
Metadata Guide</a>.</p><h3 id="querying-the-database">Querying the database</h3><p>The four basic CRUD operations <code>java.jdbc</code> provides are:</p><pre><code class="clojure">(jdbc/insert! db-spec :table {:col1 42 :col2 "123"}) ;; Create
|
||||
(jdbc/query db-spec ["SELECT * FROM table WHERE id = ?" 13]) ;; Read
|
||||
(jdbc/update! db-spec :table {:col1 77 :col2 "456"} ["id = ?" 13]) ;; Update
|
||||
(jdbc/delete! db-spec :table ["id = ?" 13]) ;; Delete
|
||||
</code></pre><p>The table name can be specified as a string or a keyword.</p><p><code>insert!</code> takes a single record in hash map form to insert. <code>insert!</code> can also
|
||||
take a vector of column names (as strings or keywords), followed by a vector of
|
||||
column values to insert into those respective columns, much like an <code>INSERT</code>
|
||||
statement in SQL. Entries in the map that have the value <code>nil</code> will cause
|
||||
<code>NULL</code> values to be inserted into the corresponding columns.</p><p>If you wish to insert multiple rows (in hash map form) at once, you can use
|
||||
<code>insert-multi!</code>; however, <code>insert-multi!</code> will write a separate insertion
|
||||
statement for each row, so it is suggested you use the column-based form of
|
||||
<code>insert-multi!</code> over the row-based form. Passing multiple column values to
|
||||
<code>insert-multi!</code> will generate a single batched insertion statement and yield
|
||||
better performance.</p><p><code>query</code> allows us to run selection queries on the database. Since you provide
|
||||
the query string directly, you have as much flexibility as you like to perform
|
||||
complex queries.</p><p><code>update!</code> takes a map of columns to update, with their new values, and a SQL
|
||||
clause used to select which rows to update (prepended by <code>WHERE</code> in the
|
||||
generated SQL). As with <code>insert!</code>, <code>nil</code> values in the map cause the
|
||||
corresponding columns to be set to <code>NULL</code>.</p><p><code>delete!</code> takes a SQL clause used to select which rows to delete, similar to
|
||||
<code>update!</code>.</p><p>By default, the table name and column names are converted to strings
|
||||
corresponding to the keyword names in the underlying SQL. We can control how we
|
||||
transform keywords into SQL names using an optional <code>:entities</code> argument which
|
||||
is described in more detail in the <a href="using_sql.html">Using SQL</a> section.</p><h3 id="dropping-our-tables">Dropping our tables</h3><p>To clean out the database from our example, we can generate a the command to
|
||||
drop the fruit table:</p><pre><code class="clojure">(def drop-fruit-table-ddl (jdbc/drop-table-ddl :fruit))
|
||||
</code></pre><p>Ensure you tear down your tables and indexes in the opposite order of creation:</p><pre><code class="clojure">(jdbc/db-do-commands db-spec
|
||||
["DROP INDEX name_ix;"
|
||||
drop-fruit-table-ddl])
|
||||
</code></pre><p>These are all the commands we need to write a simple migration for our database!</p><h2 id="database-support">Database Support</h2><p>Out of the box, <code>java.jdbc</code> understands the following <code>:dbtype</code> values (with
|
||||
their default class names):</p><ul><li><code>"derby"</code> - <code>org.apache.derby.jdbc.EmbeddedDriver</code></li><li><code>"h2"</code> - <code>org.h2.Driver</code></li><li><code>"h2:mem"</code> - <code>org.h2.Driver</code></li><li><code>"hsqldb"</code> or <code>"hsql"</code> - <code>org.hsqldb.jdbcDriver</code></li><li><code>"jtds:sqlserver"</code> or <code>"jtds"</code> - <code>net.sourceforge.jtds.jdbc.Driver</code></li><li><code>"mysql"</code> - <code>com.mysql.jdbc.Driver</code></li><li><code>"oracle:oci"</code> - <code>oracle.jdbc.OracleDriver</code></li><li><code>"oracle:thin"</code> or <code>"oracle"</code> - <code>oracle.jdbc.OracleDriver</code></li><li><code>"postgresql"</code> or <code>"postgres"</code> - <code>org.postgresql.Driver</code></li><li><code>"pgsql"</code> - <code>com.impossibl.postgres.jdbc.PGDriver</code></li><li><code>"redshift"</code> - <code>com.amazon.redshift.jdbc.Driver</code></li><li><code>"sqlite"</code> - <code>org.sqlite.JDBC</code></li><li><code>"sqlserver"</code> - <code>"mssql"</code> - <code>com.microsoft.sqlserver.jdbc.SQLServerDriver</code></li></ul><p>You must specify the appropriate JDBC driver dependency in your project -- these
|
||||
drivers are not included with <code>java.jdbc</code>.</p><p>You can overide the default class name by specifying <code>:classname</code> as well as
|
||||
<code>:dbtype</code>.</p><p>For databases that require a hostname or IP address, <code>java.jdbc</code> assumes
|
||||
<code>"127.0.0.1"</code> but that can be overidden with the <code>:host</code> option.</p><p>For databases that require a port, <code>java.jdbc</code> has the following defaults,
|
||||
which can be overridden with the <code>:port</code> option:</p><ul><li>Microsoft SQL Server - 1433</li><li>MySQL - 3306</li><li>Oracle - 1521</li><li>PostgreSQL - 5432</li></ul><p>Some databases require a different format for the "database spec". Here is an example
|
||||
that was required for an in-memory <a href="http://www.h2database.com">H2 database</a> prior
|
||||
to <code>java.jdbc</code> release 0.7.6:</p><pre><code class="clojure">(def db-spec
|
||||
{:classname "org.h2.Driver"
|
||||
:subprotocol "h2:mem" ; the prefix `jdbc:` is added automatically
|
||||
:subname "demo;DB_CLOSE_DELAY=-1" ; `;DB_CLOSE_DELAY=-1` very important!!!
|
||||
; http://www.h2database.com/html/features.html#in_memory_databases
|
||||
:user "sa" ; default "system admin" user
|
||||
:password "" ; default password => empty string
|
||||
})
|
||||
</code></pre><p>This is the most general form of database spec, that allows you to control each
|
||||
piece of the JDBC connection URL that would be created.</p><p>Note: as of <code>java.jdbc</code> 0.7.6, in-memory H2 databases are supported directly
|
||||
via the simple spec form:</p><pre><code class="clojure">(def db-spec
|
||||
{:dbtype "h2:mem"
|
||||
:dbname "mydb"})
|
||||
</code></pre><p>For file-based databases, such as H2, Derby, SQLite etc, the <code>:dbname</code> will
|
||||
specify the filename:</p><pre><code class="clojure">(def db-spec
|
||||
{:dbtype "h2"
|
||||
:dbname "/path/to/my/database"})
|
||||
</code></pre><h2 id="more-detailed-javajdbc-documentation">More detailed <code>java.jdbc</code> documentation</h2><ul><li><a href="using_sql.html">Using SQL:</a> a more detailed guide on using SQL with <code>java.jdbc</code></li><li><a href="using_ddl.html">Using DDL:</a> how to create your tables using the <code>java.jdbc</code> DDL</li><li><a href="reusing_connections.html">Reusing Connections:</a> how to reuse your database
|
||||
connections</li></ul>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../../cookbooks/middleware/index.html">« Middleware in Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="using_sql.html">java.jdbc - Manipulating data with SQL »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,310 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: java.jdbc - Getting Started</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide is intended to help you the Clojure Contrib JDBC wrapper: clojure.java.jdbcA modern JDBC wrapper has since been written (by the same author/maintainer) -- read about next.jdbc on cljdoc.org">
|
||||
|
||||
<meta property="og:description" content="This guide is intended to help you the Clojure Contrib JDBC wrapper: clojure.java.jdbcA modern JDBC wrapper has since been written (by the same author/maintainer) -- read about next.jdbc on cljdoc.org">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/home/" />
|
||||
<meta property="og:title" content="java.jdbc - Getting Started" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/home/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>java.jdbc - Getting Started</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide is intended to help you the Clojure Contrib JDBC wrapper: <code>clojure.java.jdbc</code></p><p><strong>A modern JDBC wrapper has since been written (by the same author/maintainer) -- read about <a href="https://clojure-doc.org/articles/ecosystem/java_jdbc/home/cljdoc.org/d/com.github.seancorfield/next.jdbc/"><code>next.jdbc</code> on cljdoc.org</a></strong></p><h2 id="contents">Contents</h2><ul><li><a href="../home.html">Overview</a></li><li><a href="../using_sql.html">Using SQL</a></li><li><a href="../using_ddl.html">Using DDL</a></li><li><a href="../reusing_connections.html">Reusing Connections</a></li></ul><h2 id="overview">Overview</h2><p><code>java.jdbc</code> is intended to be a low-level Clojure wrapper around various Java
|
||||
JDBC drivers and supports a wide range of databases. The <a href="https://github.com/clojure/java.jdbc/"><code>java.jdbc</code> source is
|
||||
on GitHub</a> and there is a dedicated <a href="https://groups.google.com/forum/#!forum/clojure-java-jdbc">java.jdbc mailing
|
||||
list</a>. The detailed <a href="http://clojure.github.io/java.jdbc/"><code>java.jdbc</code> reference</a> is
|
||||
automatically generated from the <code>java.jdbc</code> source.</p><p>Generally, when using <code>java.jdbc</code>, you will set up a data source as a "database
|
||||
spec" and pass that to the various CRUD (create, read, update, delete)
|
||||
functions that <code>java.jdbc</code> provides. These operations are detailed within the
|
||||
<a href="../using_sql.html">Using SQL</a> page, but a quick overview is provided by the
|
||||
walkthrough below.</p><p>By default, each operation opens a connection and executes the SQL inside a
|
||||
transaction. You can also run multiple operations against the same connection,
|
||||
either within a transaction or via connection pooling, or just with a shared
|
||||
connection. You can read more about reusing connections on the <a href="../reusing_connections.html">Reusing
|
||||
Connections</a> page.</p><h2 id="higher-level-dsl-and-migration-libraries">Higher-level DSL and migration libraries</h2><p>If you need more abstraction than the <code>java.jdbc</code> wrapper provides, you may want
|
||||
to consider using a library that provides a DSL. All of the following libraries
|
||||
are built on top of <code>java.jdbc</code> and provide such abstraction:</p><ul><li><a href="https://github.com/jkk/honeysql">HoneySQL</a></li><li><a href="https://github.com/r0man/sqlingvo">SQLingvo</a></li><li><a href="http://sqlkorma.com">Korma</a></li><li><a href="https://github.com/walkable-server/walkable">Walkable</a></li></ul><p>In particular, <a href="http://sqlkorma.com">Korma</a> goes beyond a SQL DSL to provide "entities" and
|
||||
"relationships" (in the style of classical Object-Relational Mappers, but
|
||||
without the pain).</p><p>Another common need with SQL is for database migration libraries. Some of the
|
||||
more popular options are:</p><ul><li><a href="https://github.com/macourtney/drift">Drift</a></li><li><a href="https://github.com/pjstadig/migratus">Migratus</a></li><li><a href="https://github.com/weavejester/ragtime">Ragtime</a></li></ul><h2 id="a-brief-javajdbc-walkthrough">A brief <code>java.jdbc</code> walkthrough</h2><h3 id="setting-up-a-data-source">Setting up a data source</h3><p>A "database spec" is a Clojure map that specifies how to access the data
|
||||
source. Most commonly, you specify the database type, the database name,
|
||||
and the username and password. For example,</p><pre><code class="clojure">(def db-spec
|
||||
{:dbtype "mysql"
|
||||
:dbname "mydb"
|
||||
:user "myaccount"
|
||||
:password "secret"})
|
||||
</code></pre><p>See <a href="../home.html#database-support"><strong>Database Support</strong></a> below for a complete list of
|
||||
databases and drivers supported by <code>java.jdbc</code> out of the box.</p><h3 id="a-hello-world-query">A "Hello World" Query</h3><p>Querying the database can be as simple as:</p><pre><code class="clojure">(ns dbexample
|
||||
(:require [clojure.java.jdbc :as jdbc]))
|
||||
|
||||
(def db-spec ... ) ;; see above
|
||||
|
||||
(jdbc/query db-spec ["SELECT 3*5 AS result"])
|
||||
=> {:result 15}
|
||||
</code></pre><p>Of course, we will want to do more with our database than have it perform
|
||||
simple calculations. Once we can successfully connect to it, we will likely
|
||||
want to create tables and manipulate data.</p><h3 id="creating-tables">Creating tables</h3><p><code>java.jdbc</code> provides <code>create-table-ddl</code> and <code>drop-table-ddl</code> to generate basic
|
||||
<code>CREATE TABLE</code> and <code>DROP TABLE</code> DDL strings. Anything beyond that can be
|
||||
constructed manually as a string.</p><pre><code class="clojure">(ns dbexample
|
||||
(:require [clojure.java.jdbc :as jdbc]))
|
||||
|
||||
(def db-spec ... ) ;; see above
|
||||
|
||||
(def fruit-table-ddl
|
||||
(jdbc/create-table-ddl :fruit
|
||||
[[:name "varchar(32)"]
|
||||
[:appearance "varchar(32)"]
|
||||
[:cost :int]
|
||||
[:grade :real]]))
|
||||
</code></pre><p>We can use the function <code>db-do-commands</code> to create our table and indexes in a
|
||||
single transaction:</p><pre><code class="clojure">(jdbc/db-do-commands db-spec
|
||||
[fruit-table-ddl
|
||||
"CREATE INDEX name_ix ON fruit ( name );"])
|
||||
</code></pre><p>For more details on DDL functionality within <code>java.jdbc</code>, see the <a href="../using_ddl.html">Using DDL and
|
||||
Metadata Guide</a>.</p><h3 id="querying-the-database">Querying the database</h3><p>The four basic CRUD operations <code>java.jdbc</code> provides are:</p><pre><code class="clojure">(jdbc/insert! db-spec :table {:col1 42 :col2 "123"}) ;; Create
|
||||
(jdbc/query db-spec ["SELECT * FROM table WHERE id = ?" 13]) ;; Read
|
||||
(jdbc/update! db-spec :table {:col1 77 :col2 "456"} ["id = ?" 13]) ;; Update
|
||||
(jdbc/delete! db-spec :table ["id = ?" 13]) ;; Delete
|
||||
</code></pre><p>The table name can be specified as a string or a keyword.</p><p><code>insert!</code> takes a single record in hash map form to insert. <code>insert!</code> can also
|
||||
take a vector of column names (as strings or keywords), followed by a vector of
|
||||
column values to insert into those respective columns, much like an <code>INSERT</code>
|
||||
statement in SQL. Entries in the map that have the value <code>nil</code> will cause
|
||||
<code>NULL</code> values to be inserted into the corresponding columns.</p><p>If you wish to insert multiple rows (in hash map form) at once, you can use
|
||||
<code>insert-multi!</code>; however, <code>insert-multi!</code> will write a separate insertion
|
||||
statement for each row, so it is suggested you use the column-based form of
|
||||
<code>insert-multi!</code> over the row-based form. Passing multiple column values to
|
||||
<code>insert-multi!</code> will generate a single batched insertion statement and yield
|
||||
better performance.</p><p><code>query</code> allows us to run selection queries on the database. Since you provide
|
||||
the query string directly, you have as much flexibility as you like to perform
|
||||
complex queries.</p><p><code>update!</code> takes a map of columns to update, with their new values, and a SQL
|
||||
clause used to select which rows to update (prepended by <code>WHERE</code> in the
|
||||
generated SQL). As with <code>insert!</code>, <code>nil</code> values in the map cause the
|
||||
corresponding columns to be set to <code>NULL</code>.</p><p><code>delete!</code> takes a SQL clause used to select which rows to delete, similar to
|
||||
<code>update!</code>.</p><p>By default, the table name and column names are converted to strings
|
||||
corresponding to the keyword names in the underlying SQL. We can control how we
|
||||
transform keywords into SQL names using an optional <code>:entities</code> argument which
|
||||
is described in more detail in the <a href="../using_sql.html">Using SQL</a> section.</p><h3 id="dropping-our-tables">Dropping our tables</h3><p>To clean out the database from our example, we can generate a the command to
|
||||
drop the fruit table:</p><pre><code class="clojure">(def drop-fruit-table-ddl (jdbc/drop-table-ddl :fruit))
|
||||
</code></pre><p>Ensure you tear down your tables and indexes in the opposite order of creation:</p><pre><code class="clojure">(jdbc/db-do-commands db-spec
|
||||
["DROP INDEX name_ix;"
|
||||
drop-fruit-table-ddl])
|
||||
</code></pre><p>These are all the commands we need to write a simple migration for our database!</p><h2 id="database-support">Database Support</h2><p>Out of the box, <code>java.jdbc</code> understands the following <code>:dbtype</code> values (with
|
||||
their default class names):</p><ul><li><code>"derby"</code> - <code>org.apache.derby.jdbc.EmbeddedDriver</code></li><li><code>"h2"</code> - <code>org.h2.Driver</code></li><li><code>"h2:mem"</code> - <code>org.h2.Driver</code></li><li><code>"hsqldb"</code> or <code>"hsql"</code> - <code>org.hsqldb.jdbcDriver</code></li><li><code>"jtds:sqlserver"</code> or <code>"jtds"</code> - <code>net.sourceforge.jtds.jdbc.Driver</code></li><li><code>"mysql"</code> - <code>com.mysql.jdbc.Driver</code></li><li><code>"oracle:oci"</code> - <code>oracle.jdbc.OracleDriver</code></li><li><code>"oracle:thin"</code> or <code>"oracle"</code> - <code>oracle.jdbc.OracleDriver</code></li><li><code>"postgresql"</code> or <code>"postgres"</code> - <code>org.postgresql.Driver</code></li><li><code>"pgsql"</code> - <code>com.impossibl.postgres.jdbc.PGDriver</code></li><li><code>"redshift"</code> - <code>com.amazon.redshift.jdbc.Driver</code></li><li><code>"sqlite"</code> - <code>org.sqlite.JDBC</code></li><li><code>"sqlserver"</code> - <code>"mssql"</code> - <code>com.microsoft.sqlserver.jdbc.SQLServerDriver</code></li></ul><p>You must specify the appropriate JDBC driver dependency in your project -- these
|
||||
drivers are not included with <code>java.jdbc</code>.</p><p>You can overide the default class name by specifying <code>:classname</code> as well as
|
||||
<code>:dbtype</code>.</p><p>For databases that require a hostname or IP address, <code>java.jdbc</code> assumes
|
||||
<code>"127.0.0.1"</code> but that can be overidden with the <code>:host</code> option.</p><p>For databases that require a port, <code>java.jdbc</code> has the following defaults,
|
||||
which can be overridden with the <code>:port</code> option:</p><ul><li>Microsoft SQL Server - 1433</li><li>MySQL - 3306</li><li>Oracle - 1521</li><li>PostgreSQL - 5432</li></ul><p>Some databases require a different format for the "database spec". Here is an example
|
||||
that was required for an in-memory <a href="http://www.h2database.com">H2 database</a> prior
|
||||
to <code>java.jdbc</code> release 0.7.6:</p><pre><code class="clojure">(def db-spec
|
||||
{:classname "org.h2.Driver"
|
||||
:subprotocol "h2:mem" ; the prefix `jdbc:` is added automatically
|
||||
:subname "demo;DB_CLOSE_DELAY=-1" ; `;DB_CLOSE_DELAY=-1` very important!!!
|
||||
; http://www.h2database.com/html/features.html#in_memory_databases
|
||||
:user "sa" ; default "system admin" user
|
||||
:password "" ; default password => empty string
|
||||
})
|
||||
</code></pre><p>This is the most general form of database spec, that allows you to control each
|
||||
piece of the JDBC connection URL that would be created.</p><p>Note: as of <code>java.jdbc</code> 0.7.6, in-memory H2 databases are supported directly
|
||||
via the simple spec form:</p><pre><code class="clojure">(def db-spec
|
||||
{:dbtype "h2:mem"
|
||||
:dbname "mydb"})
|
||||
</code></pre><p>For file-based databases, such as H2, Derby, SQLite etc, the <code>:dbname</code> will
|
||||
specify the filename:</p><pre><code class="clojure">(def db-spec
|
||||
{:dbtype "h2"
|
||||
:dbname "/path/to/my/database"})
|
||||
</code></pre><h2 id="more-detailed-javajdbc-documentation">More detailed <code>java.jdbc</code> documentation</h2><ul><li><a href="../using_sql.html">Using SQL:</a> a more detailed guide on using SQL with <code>java.jdbc</code></li><li><a href="../using_ddl.html">Using DDL:</a> how to create your tables using the <code>java.jdbc</code> DDL</li><li><a href="../reusing_connections.html">Reusing Connections:</a> how to reuse your database
|
||||
connections</li></ul>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../../../cookbooks/middleware/index.html">« Middleware in Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../using_sql.html">java.jdbc - Manipulating data with SQL »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,289 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: java.jdbc - How to reuse database connections</title>
|
||||
|
||||
|
||||
<meta name="description" content="Contents">
|
||||
|
||||
<meta property="og:description" content="Contents">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/reusing_connections/" />
|
||||
<meta property="og:title" content="java.jdbc - How to reuse database connections" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/reusing_connections/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>java.jdbc - How to reuse database connections</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="contents">Contents</h2><ul><li><a href="home.html">Overview</a></li><li><a href="using_sql.html">Using SQL</a></li><li><a href="using_ddl.html">Using DDL</a></li><li><a href="reusing_connections.html">Reusing Connections</a></li></ul><h2 id="reusing-connections">Reusing Connections</h2><p>Since you rarely want every database operation to create a new connection,
|
||||
there are two ways to reuse connections:</p><ul><li>Grouping Operations using <code>with-db-connection</code>: If you don't want to deal
|
||||
with a connection pooling library, you can use this macro to automatically open a
|
||||
connection and maintain it for a body of code, with each operation executed in its
|
||||
own transaction, then close the connection.</li><li>Grouping Operations using <code>with-db-transaction</code>: If you want to execute multiple
|
||||
operations in a single transaction, you can use this macro to automatically open a
|
||||
connection, start a transaction, execute multiple operations, commit the transaction,
|
||||
and then close the connection.</li><li>Connection Pooling: This is the recommended approach and is fairly
|
||||
straightforward, with a number of connection pooling libraries available. See
|
||||
<em>How To Use Connection Pooling</em> below for more information.</li></ul><h2 id="using-with-db-connection">Using <code>with-db-connection</code></h2><p>This macro provides the simplest way to reuse connections, without having to
|
||||
add a dependency on an external connection pooling library:</p><pre><code class="clojure">(ns dbexample
|
||||
(:require [clojure.java.jdbc :as jdbc]))
|
||||
|
||||
(def db-spec ... ) ;; see above
|
||||
|
||||
(jdbc/with-db-connection [db-con db-spec]
|
||||
(let [;; fetch some rows using this connection
|
||||
rows (jdbc/query db-con ["SELECT * FROM table WHERE id = ?" 42])]
|
||||
;; insert a copy of the first row using the same connection
|
||||
(jdbc/insert! db-con :table (dissoc (first rows) :id))))
|
||||
</code></pre><p>The <code>query</code> and the <code>insert!</code> are each run in their own transaction and committed
|
||||
if they succeed. If you want to run multiple operations in a single transaction
|
||||
see the next section about <code>with-db-transaction</code>.</p><h2 id="using-with-db-transaction">Using <code>with-db-transaction</code></h2><p>This macro provides a way to reuse connections, committing or rolling back
|
||||
multiple operations in a single transaction:</p><pre><code class="clojure">(ns dbexample
|
||||
(:require [clojure.java.jdbc :as jdbc]))
|
||||
|
||||
(def db-spec ... ) ;; see above
|
||||
|
||||
(jdbc/with-db-transaction [t-con db-spec]
|
||||
(let [;; fetch some rows using this connection
|
||||
rows (jdbc/query t-con ["SELECT * FROM table WHERE id = ?" 42])]
|
||||
;; insert a copy of the first row using the same connection
|
||||
(jdbc/insert! t-con :table (dissoc (first rows) :id))))
|
||||
</code></pre><p>If any operation inside <code>with-db-transaction</code> fails (throws an exception), then
|
||||
all of the operations performed so far are rolled back. If all the operations
|
||||
succeed, then the entire transaction is committed.</p><p>Transactions are not nested (since not all databases support that) so if this is
|
||||
used another active transaction, the outer transaction (and connection) are used
|
||||
as-is. If the isolation levels of the outer and inner transaction do not match
|
||||
you will get an <code>IllegalStateException</code>.</p><p>See also <code>db-set-rollback-only!</code>, <code>db-unset-rollback-only!</code>, and <code>db-is-rollback-only</code>
|
||||
for additional control over the commit/rollback behavior of the enclosing transaction.</p><h2 id="using-connection-pooling">Using Connection Pooling</h2><p><code>java.jdbc</code> does not provide connection pooling directly but it is relatively
|
||||
easy to add to your project. There are several connection pooling libraries out
|
||||
there, but here we will provide instructions for the popular <code>c3p0</code> library.</p><p>The basic idea is to add your chosen connection pooling library to your
|
||||
project, import the appropriate class(es), define a function that consumes a
|
||||
"database spec" and produces a map containing a <code>:datasource</code> key whose value
|
||||
is the constructed pooled <code>DataSource</code> object, then use that hash map in place of your
|
||||
bare <code>db-spec</code> variable. You are responsible for creating the pooled data
|
||||
source object and passing the map containing it into any functions that need a
|
||||
database connection.</p><h3 id="using-the-c3p0-library">Using the c3p0 library</h3><p>For more information on c3p0, consult the <a href="http://www.mchange.com/projects/c3p0/">c3p0
|
||||
documentation</a>.</p><p>If you're using Leiningen, you can add the following to your dependencies:</p><pre><code class="clojure">[com.mchange/c3p0 "0.9.5.2"] ;; check the documentation for latest version
|
||||
</code></pre><p>For a Maven-based project, you would add:</p><pre><code class="xml"><dependency>
|
||||
<groupId>com.mchange</groupId>
|
||||
<artifactId>c3p0</artifactId>
|
||||
<version>0.9.5.2</version>
|
||||
</dependency>
|
||||
</code></pre><h3 id="create-the-pooled-datasource-from-your-db-spec">Create the pooled datasource from your db-spec</h3><p>Define your <code>db-spec</code> using the long form, for example (for MySQL):</p><pre><code class="clojure">(def db-spec
|
||||
{:classname "com.mysql.jdbc.Driver"
|
||||
:subprotocol "mysql"
|
||||
:subname "//127.0.0.1:3306/mydb"
|
||||
:user "myaccount"
|
||||
:password "secret"})
|
||||
</code></pre><p>We have to use the long form here because c3p0 operates on the class name, subprotocol,
|
||||
and subname elements. Of course, you don't really need to define a <code>db-spec</code> here
|
||||
because you're not going to use it with <code>java.jdbc</code> directly, only with c3p0.</p><p>Import the c3p0 class as part of your namespace declaration, for example:</p><pre><code class="clojure">(ns example.db
|
||||
(:import (com.mchange.v2.c3p0 ComboPooledDataSource)))
|
||||
</code></pre><p>Define a function that creates a pooled datasource:</p><pre><code class="clojure">(defn pool
|
||||
[spec]
|
||||
(let [cpds (doto (ComboPooledDataSource.)
|
||||
(.setDriverClass (:classname spec))
|
||||
(.setJdbcUrl (str "jdbc:" (:subprotocol spec) ":" (:subname spec)))
|
||||
(.setUser (:user spec))
|
||||
(.setPassword (:password spec))
|
||||
;; expire excess connections after 30 minutes of inactivity:
|
||||
(.setMaxIdleTimeExcessConnections (* 30 60))
|
||||
;; expire connections after 3 hours of inactivity:
|
||||
(.setMaxIdleTime (* 3 60 60)))]
|
||||
{:datasource cpds}))
|
||||
</code></pre><p>Now you can create a single connection pool:</p><pre><code class="clojure">(def pooled-db (delay (pool db-spec)))
|
||||
|
||||
(defn db-connection [] @pooled-db)
|
||||
</code></pre><p>And then call <code>(db-connection)</code> wherever you need access to it. If you're using
|
||||
a <a href="https://github.com/stuartsierra/component">component</a> lifecycle for your
|
||||
application, you won't need <code>pooled-db</code> or <code>db-connection</code>. You'll just create
|
||||
<code>(pool db-spec)</code> as part of your application's initialization and pass it
|
||||
around as part of your system configuration.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="using_ddl.html">« java.jdbc - Using DDL and Metadata</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../core_typed/home/index.html">core.typed - User Documentation Home »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,289 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: java.jdbc - How to reuse database connections</title>
|
||||
|
||||
|
||||
<meta name="description" content="Contents">
|
||||
|
||||
<meta property="og:description" content="Contents">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/reusing_connections/" />
|
||||
<meta property="og:title" content="java.jdbc - How to reuse database connections" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/reusing_connections/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>java.jdbc - How to reuse database connections</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="contents">Contents</h2><ul><li><a href="../home.html">Overview</a></li><li><a href="../using_sql.html">Using SQL</a></li><li><a href="../using_ddl.html">Using DDL</a></li><li><a href="../reusing_connections.html">Reusing Connections</a></li></ul><h2 id="reusing-connections">Reusing Connections</h2><p>Since you rarely want every database operation to create a new connection,
|
||||
there are two ways to reuse connections:</p><ul><li>Grouping Operations using <code>with-db-connection</code>: If you don't want to deal
|
||||
with a connection pooling library, you can use this macro to automatically open a
|
||||
connection and maintain it for a body of code, with each operation executed in its
|
||||
own transaction, then close the connection.</li><li>Grouping Operations using <code>with-db-transaction</code>: If you want to execute multiple
|
||||
operations in a single transaction, you can use this macro to automatically open a
|
||||
connection, start a transaction, execute multiple operations, commit the transaction,
|
||||
and then close the connection.</li><li>Connection Pooling: This is the recommended approach and is fairly
|
||||
straightforward, with a number of connection pooling libraries available. See
|
||||
<em>How To Use Connection Pooling</em> below for more information.</li></ul><h2 id="using-with-db-connection">Using <code>with-db-connection</code></h2><p>This macro provides the simplest way to reuse connections, without having to
|
||||
add a dependency on an external connection pooling library:</p><pre><code class="clojure">(ns dbexample
|
||||
(:require [clojure.java.jdbc :as jdbc]))
|
||||
|
||||
(def db-spec ... ) ;; see above
|
||||
|
||||
(jdbc/with-db-connection [db-con db-spec]
|
||||
(let [;; fetch some rows using this connection
|
||||
rows (jdbc/query db-con ["SELECT * FROM table WHERE id = ?" 42])]
|
||||
;; insert a copy of the first row using the same connection
|
||||
(jdbc/insert! db-con :table (dissoc (first rows) :id))))
|
||||
</code></pre><p>The <code>query</code> and the <code>insert!</code> are each run in their own transaction and committed
|
||||
if they succeed. If you want to run multiple operations in a single transaction
|
||||
see the next section about <code>with-db-transaction</code>.</p><h2 id="using-with-db-transaction">Using <code>with-db-transaction</code></h2><p>This macro provides a way to reuse connections, committing or rolling back
|
||||
multiple operations in a single transaction:</p><pre><code class="clojure">(ns dbexample
|
||||
(:require [clojure.java.jdbc :as jdbc]))
|
||||
|
||||
(def db-spec ... ) ;; see above
|
||||
|
||||
(jdbc/with-db-transaction [t-con db-spec]
|
||||
(let [;; fetch some rows using this connection
|
||||
rows (jdbc/query t-con ["SELECT * FROM table WHERE id = ?" 42])]
|
||||
;; insert a copy of the first row using the same connection
|
||||
(jdbc/insert! t-con :table (dissoc (first rows) :id))))
|
||||
</code></pre><p>If any operation inside <code>with-db-transaction</code> fails (throws an exception), then
|
||||
all of the operations performed so far are rolled back. If all the operations
|
||||
succeed, then the entire transaction is committed.</p><p>Transactions are not nested (since not all databases support that) so if this is
|
||||
used another active transaction, the outer transaction (and connection) are used
|
||||
as-is. If the isolation levels of the outer and inner transaction do not match
|
||||
you will get an <code>IllegalStateException</code>.</p><p>See also <code>db-set-rollback-only!</code>, <code>db-unset-rollback-only!</code>, and <code>db-is-rollback-only</code>
|
||||
for additional control over the commit/rollback behavior of the enclosing transaction.</p><h2 id="using-connection-pooling">Using Connection Pooling</h2><p><code>java.jdbc</code> does not provide connection pooling directly but it is relatively
|
||||
easy to add to your project. There are several connection pooling libraries out
|
||||
there, but here we will provide instructions for the popular <code>c3p0</code> library.</p><p>The basic idea is to add your chosen connection pooling library to your
|
||||
project, import the appropriate class(es), define a function that consumes a
|
||||
"database spec" and produces a map containing a <code>:datasource</code> key whose value
|
||||
is the constructed pooled <code>DataSource</code> object, then use that hash map in place of your
|
||||
bare <code>db-spec</code> variable. You are responsible for creating the pooled data
|
||||
source object and passing the map containing it into any functions that need a
|
||||
database connection.</p><h3 id="using-the-c3p0-library">Using the c3p0 library</h3><p>For more information on c3p0, consult the <a href="http://www.mchange.com/projects/c3p0/">c3p0
|
||||
documentation</a>.</p><p>If you're using Leiningen, you can add the following to your dependencies:</p><pre><code class="clojure">[com.mchange/c3p0 "0.9.5.2"] ;; check the documentation for latest version
|
||||
</code></pre><p>For a Maven-based project, you would add:</p><pre><code class="xml"><dependency>
|
||||
<groupId>com.mchange</groupId>
|
||||
<artifactId>c3p0</artifactId>
|
||||
<version>0.9.5.2</version>
|
||||
</dependency>
|
||||
</code></pre><h3 id="create-the-pooled-datasource-from-your-db-spec">Create the pooled datasource from your db-spec</h3><p>Define your <code>db-spec</code> using the long form, for example (for MySQL):</p><pre><code class="clojure">(def db-spec
|
||||
{:classname "com.mysql.jdbc.Driver"
|
||||
:subprotocol "mysql"
|
||||
:subname "//127.0.0.1:3306/mydb"
|
||||
:user "myaccount"
|
||||
:password "secret"})
|
||||
</code></pre><p>We have to use the long form here because c3p0 operates on the class name, subprotocol,
|
||||
and subname elements. Of course, you don't really need to define a <code>db-spec</code> here
|
||||
because you're not going to use it with <code>java.jdbc</code> directly, only with c3p0.</p><p>Import the c3p0 class as part of your namespace declaration, for example:</p><pre><code class="clojure">(ns example.db
|
||||
(:import (com.mchange.v2.c3p0 ComboPooledDataSource)))
|
||||
</code></pre><p>Define a function that creates a pooled datasource:</p><pre><code class="clojure">(defn pool
|
||||
[spec]
|
||||
(let [cpds (doto (ComboPooledDataSource.)
|
||||
(.setDriverClass (:classname spec))
|
||||
(.setJdbcUrl (str "jdbc:" (:subprotocol spec) ":" (:subname spec)))
|
||||
(.setUser (:user spec))
|
||||
(.setPassword (:password spec))
|
||||
;; expire excess connections after 30 minutes of inactivity:
|
||||
(.setMaxIdleTimeExcessConnections (* 30 60))
|
||||
;; expire connections after 3 hours of inactivity:
|
||||
(.setMaxIdleTime (* 3 60 60)))]
|
||||
{:datasource cpds}))
|
||||
</code></pre><p>Now you can create a single connection pool:</p><pre><code class="clojure">(def pooled-db (delay (pool db-spec)))
|
||||
|
||||
(defn db-connection [] @pooled-db)
|
||||
</code></pre><p>And then call <code>(db-connection)</code> wherever you need access to it. If you're using
|
||||
a <a href="https://github.com/stuartsierra/component">component</a> lifecycle for your
|
||||
application, you won't need <code>pooled-db</code> or <code>db-connection</code>. You'll just create
|
||||
<code>(pool db-spec)</code> as part of your application's initialization and pass it
|
||||
around as part of your system configuration.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../using_ddl.html">« java.jdbc - Using DDL and Metadata</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../../core_typed/home/index.html">core.typed - User Documentation Home »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,249 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: java.jdbc - Using DDL and Metadata</title>
|
||||
|
||||
|
||||
<meta name="description" content="Contents">
|
||||
|
||||
<meta property="og:description" content="Contents">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_ddl/" />
|
||||
<meta property="og:title" content="java.jdbc - Using DDL and Metadata" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_ddl/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>java.jdbc - Using DDL and Metadata</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="contents">Contents</h2><ul><li><a href="home.html">Overview</a></li><li><a href="using_sql.html">Using SQL</a></li><li><a href="using_ddl.html">Using DDL</a></li><li><a href="reusing_connections.html">Reusing Connections</a></li></ul><h2 id="using-ddl">Using DDL</h2><p>DDL operations can be executed using the <code>db-do-commands</code> function. The general
|
||||
approach is:</p><pre><code class="clojure">(jdbc/db-do-commands db-spec [sql-command-1 sql-command-2 .. sql-command-n])
|
||||
</code></pre><p>The commands are executed as a single, batched statement, wrapped in a
|
||||
transaction. If you want to avoid the transaction, use this approach:</p><pre><code class="clojure">(jdbc/db-do-commands db-spec false [sql-command-1 sql-command-2 .. sql-command-n])
|
||||
</code></pre><p>This is necessary for some databases that do not allow DDL operations to be
|
||||
wrapped in a transaction.</p><h3 id="creating-tables">Creating tables</h3><p>For the common operations of creating and dropping tables, <code>java.jdbc</code> provides a
|
||||
little assistance that recognizes <code>:entities</code> so you can use keywords (or
|
||||
strings) and have your chosen naming strategy applied, just as you can for
|
||||
several of the SQL functions.</p><pre><code class="clojure">(jdbc/create-table-ddl :fruit
|
||||
[[:name "varchar(32)" :primary :key]
|
||||
[:appearance "varchar(32)"]
|
||||
[:cost :int]
|
||||
[:grade :real]]
|
||||
{:table-spec "ENGINE=InnoDB"
|
||||
:entities clojure.string/upper-case})
|
||||
</code></pre><p>This will generate:</p><pre><code class="clojure">CREATE TABLE FRUIT
|
||||
(NAME varchar(32) primary key,
|
||||
APPEARANCE varchar(32),
|
||||
COST int,
|
||||
GRADE real) ENGINE=InnoDB
|
||||
</code></pre><p>which you can pass to <code>db-do-commands</code>.</p><p><code>create-table-ddl</code> also supports a <code>conditional?</code> option which can be a simple
|
||||
<code>Boolean</code>, which, if <code>true</code>, will add <code>IF NOT EXISTS</code> before the table name. If
|
||||
that syntax doesn't work for your database, you can pass a string that will be
|
||||
used instead. If that isn't enough, you can pass a function of two arguments:
|
||||
the first argument will be the table name and the second argument will be the
|
||||
DDL string (this approach is needed for Microsoft SQL Server).</p><h3 id="dropping-tables">Dropping tables</h3><p>Similarly there is a <code>drop-table-ddl</code> function which takes a table name and an
|
||||
optional <code>:entities</code> option to generate DDL to drop a table.</p><pre><code class="clojure">(jdbc/drop-table-ddl :fruit) ; drop table fruit
|
||||
(jdbc/drop-table-ddl :fruit {:entities clojure.string/upper-case}) ; drop table FRUIT
|
||||
</code></pre><p>This will generate:</p><pre><code class="clojure">DROP TABLE FRUIT
|
||||
</code></pre><p><code>drop-table-ddl</code> also supports a <code>conditional?</code> option which can be a simple
|
||||
<code>Boolean</code>, which, if <code>true</code>, will add <code>IF EXISTS</code> before the table name. If
|
||||
that syntax doesn't work for your database, you can pass a string that will be
|
||||
used instead. If that isn't enough, you can pass a function of two arguments:
|
||||
the first argument will be the table name and the second argument will be the
|
||||
DDL string (this approach is needed for Microsoft SQL Server).</p><h2 id="accessing-metadata">Accessing metadata</h2><p><code>java.jdbc</code> provides two functions for working with database metadata:</p><ul><li><code>with-db-metadata</code> for creating an active metadata object backed by an open
|
||||
connection</li><li><code>metadata-result</code> for turning metadata results into Clojure data structures</li></ul><p>For example:</p><pre><code class="clojure">(jdbc/with-db-metadata [md db-spec]
|
||||
(jdbc/metadata-result (.getTables md nil nil nil (into-array ["TABLE" "VIEW"]))))
|
||||
</code></pre><p>This returns a sequence of maps describing all the tables and views in the
|
||||
current database. <code>metadata-result</code> only transforms <code>ResultSet</code> objects, other
|
||||
results are returned as-is. <code>metadata-result</code> can also accept an options map
|
||||
containing <code>:identifiers</code> and <code>:as-arrays?</code>, like the <code>query</code> function,
|
||||
and those options control how the metatadata is transformed and/or returned.</p><p>Both <code>with-db-metadata</code> and <code>metadata-result</code> can accept an options hash map
|
||||
which will be passed through various <code>java.jdbc</code> functions (<code>get-connections</code>
|
||||
for the former and <code>result-set-seq</code> for the latter).</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="using_sql.html">« java.jdbc - Manipulating data with SQL</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="reusing_connections.html">java.jdbc - How to reuse database connections »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,249 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: java.jdbc - Using DDL and Metadata</title>
|
||||
|
||||
|
||||
<meta name="description" content="Contents">
|
||||
|
||||
<meta property="og:description" content="Contents">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_ddl/" />
|
||||
<meta property="og:title" content="java.jdbc - Using DDL and Metadata" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_ddl/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>java.jdbc - Using DDL and Metadata</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="contents">Contents</h2><ul><li><a href="../home.html">Overview</a></li><li><a href="../using_sql.html">Using SQL</a></li><li><a href="../using_ddl.html">Using DDL</a></li><li><a href="../reusing_connections.html">Reusing Connections</a></li></ul><h2 id="using-ddl">Using DDL</h2><p>DDL operations can be executed using the <code>db-do-commands</code> function. The general
|
||||
approach is:</p><pre><code class="clojure">(jdbc/db-do-commands db-spec [sql-command-1 sql-command-2 .. sql-command-n])
|
||||
</code></pre><p>The commands are executed as a single, batched statement, wrapped in a
|
||||
transaction. If you want to avoid the transaction, use this approach:</p><pre><code class="clojure">(jdbc/db-do-commands db-spec false [sql-command-1 sql-command-2 .. sql-command-n])
|
||||
</code></pre><p>This is necessary for some databases that do not allow DDL operations to be
|
||||
wrapped in a transaction.</p><h3 id="creating-tables">Creating tables</h3><p>For the common operations of creating and dropping tables, <code>java.jdbc</code> provides a
|
||||
little assistance that recognizes <code>:entities</code> so you can use keywords (or
|
||||
strings) and have your chosen naming strategy applied, just as you can for
|
||||
several of the SQL functions.</p><pre><code class="clojure">(jdbc/create-table-ddl :fruit
|
||||
[[:name "varchar(32)" :primary :key]
|
||||
[:appearance "varchar(32)"]
|
||||
[:cost :int]
|
||||
[:grade :real]]
|
||||
{:table-spec "ENGINE=InnoDB"
|
||||
:entities clojure.string/upper-case})
|
||||
</code></pre><p>This will generate:</p><pre><code class="clojure">CREATE TABLE FRUIT
|
||||
(NAME varchar(32) primary key,
|
||||
APPEARANCE varchar(32),
|
||||
COST int,
|
||||
GRADE real) ENGINE=InnoDB
|
||||
</code></pre><p>which you can pass to <code>db-do-commands</code>.</p><p><code>create-table-ddl</code> also supports a <code>conditional?</code> option which can be a simple
|
||||
<code>Boolean</code>, which, if <code>true</code>, will add <code>IF NOT EXISTS</code> before the table name. If
|
||||
that syntax doesn't work for your database, you can pass a string that will be
|
||||
used instead. If that isn't enough, you can pass a function of two arguments:
|
||||
the first argument will be the table name and the second argument will be the
|
||||
DDL string (this approach is needed for Microsoft SQL Server).</p><h3 id="dropping-tables">Dropping tables</h3><p>Similarly there is a <code>drop-table-ddl</code> function which takes a table name and an
|
||||
optional <code>:entities</code> option to generate DDL to drop a table.</p><pre><code class="clojure">(jdbc/drop-table-ddl :fruit) ; drop table fruit
|
||||
(jdbc/drop-table-ddl :fruit {:entities clojure.string/upper-case}) ; drop table FRUIT
|
||||
</code></pre><p>This will generate:</p><pre><code class="clojure">DROP TABLE FRUIT
|
||||
</code></pre><p><code>drop-table-ddl</code> also supports a <code>conditional?</code> option which can be a simple
|
||||
<code>Boolean</code>, which, if <code>true</code>, will add <code>IF EXISTS</code> before the table name. If
|
||||
that syntax doesn't work for your database, you can pass a string that will be
|
||||
used instead. If that isn't enough, you can pass a function of two arguments:
|
||||
the first argument will be the table name and the second argument will be the
|
||||
DDL string (this approach is needed for Microsoft SQL Server).</p><h2 id="accessing-metadata">Accessing metadata</h2><p><code>java.jdbc</code> provides two functions for working with database metadata:</p><ul><li><code>with-db-metadata</code> for creating an active metadata object backed by an open
|
||||
connection</li><li><code>metadata-result</code> for turning metadata results into Clojure data structures</li></ul><p>For example:</p><pre><code class="clojure">(jdbc/with-db-metadata [md db-spec]
|
||||
(jdbc/metadata-result (.getTables md nil nil nil (into-array ["TABLE" "VIEW"]))))
|
||||
</code></pre><p>This returns a sequence of maps describing all the tables and views in the
|
||||
current database. <code>metadata-result</code> only transforms <code>ResultSet</code> objects, other
|
||||
results are returned as-is. <code>metadata-result</code> can also accept an options map
|
||||
containing <code>:identifiers</code> and <code>:as-arrays?</code>, like the <code>query</code> function,
|
||||
and those options control how the metatadata is transformed and/or returned.</p><p>Both <code>with-db-metadata</code> and <code>metadata-result</code> can accept an options hash map
|
||||
which will be passed through various <code>java.jdbc</code> functions (<code>get-connections</code>
|
||||
for the former and <code>result-set-seq</code> for the latter).</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../using_sql.html">« java.jdbc - Manipulating data with SQL</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../reusing_connections.html">java.jdbc - How to reuse database connections »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,510 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: java.jdbc - Manipulating data with SQL</title>
|
||||
|
||||
|
||||
<meta name="description" content="Contents">
|
||||
|
||||
<meta property="og:description" content="Contents">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_sql/" />
|
||||
<meta property="og:title" content="java.jdbc - Manipulating data with SQL" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_sql/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>java.jdbc - Manipulating data with SQL</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="contents">Contents</h2><ul><li><a href="home.html">Overview</a></li><li><a href="using_sql.html">Using SQL</a></li><li><a href="using_ddl.html">Using DDL</a></li><li><a href="reusing_connections.html">Reusing Connections</a></li></ul><h2 id="using-sql">Using SQL</h2><p>Here are some examples of using <code>java.jdbc</code> to manipulate data with SQL.
|
||||
These examples assume a simple table called <code>fruit</code> (see <a href="https://clojure-doc.org/articles/ecosystem/java_jdbc/using_sql/using_ddl/">Using DDL and
|
||||
Metadata</a>). These examples all assume the following in your
|
||||
<code>ns</code> declaration:</p><pre><code class="clojure">(:require [clojure.java.jdbc :as jdbc])
|
||||
</code></pre><h2 id="reading-and-processing-rows">Reading and processing rows</h2><p><code>java.jdbc</code> provides a simple <code>query</code> function to allow you to read rows from
|
||||
tables, as well as optionally performing processing on them at the same time.</p><h3 id="reading-rows">Reading rows</h3><p>To obtain a fully realized result set as a sequence of maps, you can use
|
||||
<code>query</code> with a vector containing the SQL string and any parameters needed by
|
||||
the SQL:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit"])
|
||||
;; ({:id 1 :name "Apple" :appearance "red" :cost 59 :grade 87}
|
||||
;; {:id 2 :name "Banana" :appearance "yellow" :cost 29 :grade 92.2}
|
||||
;; ...)
|
||||
|
||||
(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50])
|
||||
;; ({:id 2 :name "Banana" :appearance "yellow" :cost 29 :grade 92.2}
|
||||
;; ...)
|
||||
</code></pre><p>You can also return the result set as a sequence of vectors. The first vector
|
||||
will contain the column names, and each subsequent vector will represent a row
|
||||
of data with values in the same order as the columns.</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:as-arrays? true})
|
||||
;; ([:id :name :appearance :cost :grade]
|
||||
;; [2 "Banana" "yellow" 29 92.2]
|
||||
;; ...)
|
||||
</code></pre><h3 id="processing-a-result-set-lazily">Processing a result set lazily</h3><p>Since <code>query</code> returns a fully realized result set, it can be difficult to
|
||||
process very large results. Fortunately, <code>java.jdbc</code> provides a number of ways to process a
|
||||
large result set lazily while the connection is open, either by passing a function via
|
||||
the <code>:result-set-fn</code> option or, since release 0.7.0, via <code>reducible-query</code>.</p><p><strong><code>query</code> and <code>:result-set-fn</code></strong></p><p><em>If you are using release 0.7.0 or later, consider using <code>reducible-query</code> instead -- see below.</em></p><p>For <code>:result-set-fn</code>, the function you pass must force
|
||||
realization of the result to avoid the connection closing while the result set
|
||||
is still being processed. A <code>reduce</code>-based function is a good choice.</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:result-set-fn (fn [rs]
|
||||
(reduce (fn [total row-map]
|
||||
(+ total (:cost row-map)))
|
||||
0 rs))})
|
||||
;; produces the total cost of all the cheap fruits: 437
|
||||
</code></pre><p>Of course, a simple sum like this could be computed directly in SQL instead:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT SUM(cost) FROM fruit WHERE cost < ?" 50]
|
||||
{:result-set-fn first})
|
||||
;; {:sum(cost) 437}
|
||||
</code></pre><p>We know we will only get one row back so passing <code>first</code> to <code>:result-set-fn</code> is
|
||||
a quick way to get just that row.</p><p>Remember that if you also specify <code>:as-arrays? true</code>, your result set function
|
||||
will be passed a sequence of vectors in which the first vector contains the
|
||||
column names and subsequent vectors represent the values in the rows, matching
|
||||
the order of the column names.</p><p><strong><code>reducible-query</code></strong></p><p>This is the recommended approach since release 0.7.0 but it does come with a few
|
||||
restrictions:</p><p>You cannot use any of the following options that <code>query</code> accepts:
|
||||
<code>as-arrays?</code>, <code>:explain</code>, <code>:explain-fn</code>, <code>:result-set-fn</code>, or <code>:row-fn</code>.</p><p>On the other hand, you have access to a much faster way to process result sets:
|
||||
you can specify <code>:raw? true</code> and no conversion from Java's <code>ResultSet</code> to
|
||||
Clojure's sequence of hash maps will be performed. In particular, it's as if you
|
||||
specified <code>:identifiers identity :keywordize? false :qualifier nil</code>, and the
|
||||
sequence representation of each row is not available. That means no <code>keys</code>,
|
||||
no <code>vals</code>, no <code>seq</code> calls, just simple key lookup (for convenience, you can
|
||||
still use keyword lookup for columns, but you can also call <code>get</code> with either a
|
||||
string or a keyword).</p><p>So how does this work? <code>reducible-query</code> produces a <code>clojure.lang.IReduce</code> which,
|
||||
when reduced with a function <code>f</code>, performs the query and reduces the <code>ResultSet</code>
|
||||
using <code>f</code>, opening and closing the connection and/or transaction during the
|
||||
reduction. For example:</p><pre><code class="clojure">;; our reducing function requires two arguments: we must provide initial val
|
||||
(reduce (fn [total {:keys [cost]}] (+ total cost))
|
||||
0
|
||||
(jdbc/reducible-query db-spec
|
||||
["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:raw? true}))
|
||||
;; separating the key selection from the reducing function: we can omit val
|
||||
(transduce (map :cost)
|
||||
+ ; can be called with 0, 1, or 2 arguments!
|
||||
(jdbc/reducible-query db-spec
|
||||
["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:raw? true}))
|
||||
;; 437
|
||||
</code></pre><p>Since <code>reducible-query</code> doesn't actually run the query until you reduce its result,
|
||||
you can create it once and run it as many times as you want. This will avoid the
|
||||
overhead of option and parameter validation and handling for repeated reductions,
|
||||
since those are performed just once in the call to <code>reducible-query</code>. Note that
|
||||
the SQL parameters are fixed by that call, so this only works for running the
|
||||
<em>identical</em> query multiple times.</p><p>A reducible companion to <code>result-set-seq</code> also exists, in case you already have
|
||||
a Java <code>ResultSet</code> and want to create a <code>clojure.lang.IReduce</code>. <code>reducible-result-set</code>
|
||||
accept almost the same options as <code>result-set-seq</code>: <code>identifiers</code>, <code>keywordize?</code>,
|
||||
<code>qualifier</code>, and <code>read-columns</code>. It does not accept <code>as-arrays?</code> (for the same
|
||||
reason that <code>reducible-query</code> does not). Unlike <code>result-set-seq</code>, which produces
|
||||
a lazy sequence that can be consumed multiple times (with the first pass realizing
|
||||
it for subsequent passes), <code>reducible-result-set</code> is reducible just once: the
|
||||
underlying <code>ResultSet</code> is mutable and is consumed during the first reduction!</p><p>It should go without saying that both <code>reducible-query</code> and
|
||||
<code>reducible-result-set</code> respect <code>reduced</code> / <code>reduced?</code>.</p><p><strong>Additional Options?</strong></p><p>Note: some databases require additional options to be passed in to ensure that
|
||||
result sets are chunked and lazy. In particular, you may need to pass
|
||||
<code>:auto-commit?</code>, set appropriately, as an option to whichever function will open your database
|
||||
connection (<code>with-db-connection</code>, <code>with-db-transaction</code>, or the <code>query</code> / <code>reducible-query</code> itself
|
||||
if you are passing a bare database spec and expecting <code>query</code> / <code>reducible-query</code> to open and close
|
||||
the connection directly). You may also need to specify <code>:fetch-size</code>, <code>:result-type</code>,
|
||||
and possibly other options -- consult your database's documentation for the JDBC
|
||||
driver you are using.</p><h3 id="processing-each-row-lazily">Processing each row lazily</h3><p>As seen above, using <code>reduce</code>, <code>transduce</code>, etc with a <code>reducible-query</code> allow
|
||||
you to easily and efficiently process each row as you process the entire
|
||||
result set, but sometimes you just want a sequence of transformed rows.</p><p>We can process each row with the <code>:row-fn</code> option. Again, like with <code>:result-set-fn</code>,
|
||||
we pass a function but this time it will be
|
||||
invoked on each row, as the result set is realized.</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT name FROM fruit WHERE cost < ?" 50]
|
||||
{:row-fn :name})
|
||||
;; ("Apple" "Banana" ...)
|
||||
</code></pre><p>The result is still a fully realized sequence, but each row has been
|
||||
transformed by the <code>:name</code> function you passed in.</p><p>You can combine this with <code>:result-set-fn</code> to simplify processing of result
|
||||
sets:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:row-fn :cost
|
||||
:result-set-fn (partial reduce +)})
|
||||
;; produces the total cost of all the cheap fruits
|
||||
</code></pre><p>or:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT SUM(cost) AS total FROM fruit WHERE cost < ?" 50]
|
||||
{:row-fn :total
|
||||
:result-set-fn first})
|
||||
;; produces the same result, via SQL
|
||||
</code></pre><p>Here is an example that manipulates rows to add computed columns:</p><pre><code class="clojure">(defn add-tax [row] (assoc row :tax (* 0.08 (:cost row))))
|
||||
|
||||
(jdbc/query db-spec ["SELECT * FROM fruit"]
|
||||
{:row-fn add-tax})
|
||||
;; produces all the rows with a new :tax column added
|
||||
</code></pre><p>All of the above can be achieved via <code>reducible-query</code> and the appropriate
|
||||
reducing function and/or transducer, but with those simple row/result set
|
||||
functions, the result is often longer / uglier:</p><pre><code class="clojure">(into [] (map :name) (jdbc/reducible-query db-spec ["SELECT name FROM fruit WHERE cost < ?" 50]))
|
||||
(transduce (map :cost) + (jdbc/reducible-query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50]))
|
||||
;; :row-fn :total :result-set-fn first left as an exercise for the reader!
|
||||
(into [] (map add-tax) (jdbc/reducible-query db-spec ["SELECT * FROM fruit"]))
|
||||
</code></pre><p>If the result set is likely to be large and the reduction can use a <code>:raw? true</code>
|
||||
result set, <code>reducible-query</code> may be worth the verbosity for the performance gain.</p><h2 id="inserting-data">Inserting data</h2><p>Rows (and partial rows) can be inserted easily using the <code>insert!</code> function.
|
||||
You can insert a single row, or multiple rows. Depending on how you call
|
||||
<code>insert!</code>, the insertion will be done either through multiple SQL statements or
|
||||
through a single, batched SQL statement. That will also determine whether or
|
||||
not you get back any generated keys.</p><p>If you need a more complex form of insertion, you can use <code>execute!</code> and, if
|
||||
your database / driver supports it, you can pass <code>:return-keys</code> as an option
|
||||
to get back the generated keys. As of <code>java.jdbc</code> 0.7.6, this can be a vector
|
||||
of column names to return (for drivers that support that) or a simple Boolean.</p><h3 id="inserting-a-row">Inserting a row</h3><p>If you want to insert a single row (or partial row) and get back the generated
|
||||
keys, you can use <code>insert!</code> and specify the columns and their values as a map.
|
||||
This performs a single insert statement. A single-element sequence containing a
|
||||
map of the generated keys will be returned.</p><pre><code class="clojure">(jdbc/insert! db-spec :fruit {:name "Pear" :appearance "green" :cost 99})
|
||||
;; returns a database-specific map as the only element of a sequence, e.g.,
|
||||
;; ({:generated_key 50}) might be returned for MySQL
|
||||
</code></pre><p>Not all databases are able to return generated keys from an insert.</p><h3 id="inserting-multiple-rows">Inserting multiple rows</h3><p>There are two ways to insert multiple rows: as a sequence of maps, or as a
|
||||
sequence of vectors. In the former case, multiple inserts will be performed and
|
||||
a map of the generated keys will be returned for each insert (as a sequence).
|
||||
In the latter case, a single, batched insert will be performed and a sequence
|
||||
of row insert counts will be returned (generally a sequence of ones). The latter
|
||||
approach is likely to be substantially faster if you are inserting a large number
|
||||
of rows.</p><p>If you use <code>insert-multi!</code> and specify each row as a map of columns and their values,
|
||||
then you can specify a mixture of complete and partial rows, and you will get
|
||||
back the generated keys for each row (assuming the database has that
|
||||
capability).</p><pre><code class="clojure">(jdbc/insert-multi! db-spec :fruit
|
||||
[{:name "Pomegranate" :appearance "fresh" :cost 585}
|
||||
{:name "Kiwifruit" :grade 93}])
|
||||
;; returns a sequence of database-specific maps, e.g., for MySQL:
|
||||
;; ({generated_key 51} {generated_key 52})
|
||||
</code></pre><p>If you use <code>insert-multi!</code> and specify the columns you wish to insert followed by
|
||||
each row as a vector of column values, then you must specify the same columns
|
||||
in each row, and you will not get generated keys back, just row counts. If you
|
||||
wish to insert complete rows, you may omit the column name vector (passing
|
||||
<code>nil</code> instead) but your rows must match the natural order of columns in your
|
||||
table so be careful!</p><pre><code class="clojure">(jdbc/insert-multi! db-spec :fruit
|
||||
nil ; column names not supplied
|
||||
[[1 "Apple" "red" 59 87]
|
||||
[2 "Banana" "yellow" 29 92.2]
|
||||
[3 "Peach" "fuzzy" 139 90.0]
|
||||
[4 "Orange" "juicy" 89 88.6]])
|
||||
;; (1 1 1 1) - row counts modified
|
||||
</code></pre><p>It is generally safer to specify the columns you wish to insert so you can
|
||||
control the order, and choose to omit certain columns:</p><pre><code class="clojure">(jdbc/insert-multi! db-spec :fruit
|
||||
[:name :cost]
|
||||
[["Mango" 722]
|
||||
["Feijoa" 441]])
|
||||
;; (1 1) - row counts modified
|
||||
</code></pre><h2 id="updating-rows">Updating rows</h2><p>If you want to update simple column values in one or more rows based on a
|
||||
simple SQL predicate, you can use <code>update!</code> with a map, representing the column
|
||||
values to set, and a SQL predicate with parameters. If you need a more complex
|
||||
form of update, you can use the <code>execute!</code> function with arbitrary SQL (and
|
||||
parameters).</p><pre><code class="clojure">;; update fruit set cost = 49 where grade < ?
|
||||
(jdbc/update! db-spec :fruit
|
||||
{:cost 49}
|
||||
["grade < ?" 75])
|
||||
;; produces a sequence of the number of rows updated, e.g., (2)
|
||||
</code></pre><p>For a more complex update:</p><pre><code class="clojure">(jdbc/execute! db-spec
|
||||
["update fruit set cost = ( 2 * grade ) where grade > ?" 50.0])
|
||||
;; produces a sequence of the number of rows updated, e.g., (3)
|
||||
</code></pre><h2 id="deleting-rows">Deleting rows</h2><p>If you want to delete any rows from a table that match a simple predicate, the
|
||||
<code>delete!</code> function can be used.</p><pre><code class="clojure">(jdbc/delete! db-spec :fruit ["grade < ?" 25.0])
|
||||
;; produces a sequence of the number of rows deleted, e.g., (1)
|
||||
</code></pre><p>You can also use <code>execute!</code> for deleting rows:</p><pre><code class="clojure">(jdbc/execute! db-spec ["DELETE FROM fruit WHERE grade < ?" 25.0])
|
||||
;; produces a sequence of the number of rows deleted, e.g., (1)
|
||||
</code></pre><h2 id="using-transactions">Using transactions</h2><p>You can write multiple operations in a transaction to ensure they are either
|
||||
all performed, or all rolled back.</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec]
|
||||
(jdbc/update! t-con :fruit
|
||||
{:cost 49}
|
||||
["grade < ?" 75])
|
||||
(jdbc/execute! t-con
|
||||
["update fruit set cost = ( 2 * grade ) where grade > ?" 50.0]))
|
||||
</code></pre><p>The <code>with-db-transaction</code> macro creates a transaction-aware connection from the
|
||||
database specification and that should be used in the body of the transaction
|
||||
code.</p><p>You can specify the transaction isolation level as part of the
|
||||
<code>with-db-transction</code> binding:</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec {:isolation :serializable}]
|
||||
...)
|
||||
</code></pre><p>Possible values for <code>:isolation</code> are <code>:none</code>, <code>:read-committed</code>,
|
||||
<code>:read-uncommitted</code>, <code>:repeatable-read</code>, and <code>:serializable</code>. Be aware that not
|
||||
all databases support all isolation levels. Inside a transaction, you can call
|
||||
<code>get-isolation-level</code> to return the current level.</p><p>In addition, you can also set the current transaction-aware connection to
|
||||
rollback, and reset that setting, as well as test whether the connection is
|
||||
currently set to rollback, using the following functions:</p><pre><code class="clojure">(jdbc/db-set-rollback-only! t-con) ; this transaction will rollback instead of commit
|
||||
(jdbc/db-unset-rollback-only! t-con) ; this transaction will commit if successful
|
||||
(jdbc/db-is-rollback-only t-con) ; returns true if transaction is set to rollback
|
||||
</code></pre><h2 id="updating-or-inserting-rows-conditionally">Updating or Inserting rows conditionally</h2><p><code>java.jdbc</code> does not provide a built-in function for updating existing rows or
|
||||
inserting a new row (the older API supported this but the logic was too
|
||||
simplistic to be generally useful). If you need that functionality, it can
|
||||
sometimes be done like this:</p><pre><code class="clojure">(defn update-or-insert!
|
||||
"Updates columns or inserts a new row in the specified table"
|
||||
[db table row where-clause]
|
||||
(jdbc/with-db-transaction [t-con db]
|
||||
(let [result (jdbc/update! t-con table row where-clause)]
|
||||
(if (zero? (first result))
|
||||
(jdbc/insert! t-con table row)
|
||||
result))))
|
||||
|
||||
(update-or-insert! mysql-db :fruit
|
||||
{:name "Cactus" :appearance "Spiky" :cost 2000}
|
||||
["name = ?" "Cactus"])
|
||||
;; inserts Cactus (assuming none exists)
|
||||
(update-or-insert! mysql-db :fruit
|
||||
{:name "Cactus" :appearance "Spiky" :cost 2500}
|
||||
["name = ?" "Cactus"])
|
||||
;; updates the Cactus we just inserted
|
||||
</code></pre><p>If the <code>where-clause</code> does not uniquely identify a single row, this will update
|
||||
multiple rows which might not be what you want, so be careful!</p><h2 id="exception-handling-and-transaction-rollback">Exception Handling and Transaction Rollback</h2><p>Transactions are rolled back if an exception is thrown, as shown in these
|
||||
examples.</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec]
|
||||
(jdbc/insert-multi! t-con :fruit
|
||||
[:name :appearance]
|
||||
[["Grape" "yummy"]
|
||||
["Pear" "bruised"]])
|
||||
;; At this point the insert! call is complete, but the transaction is
|
||||
;; not. The exception will cause it to roll back leaving the database
|
||||
;; untouched.
|
||||
(throw (Exception. "sql/test exception")))
|
||||
</code></pre><p>As noted above, transactions can also be set explicitly to rollback instead of
|
||||
commit:</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec]
|
||||
(prn "is-rollback-only" (jdbc/db-is-rollback-only t-con))
|
||||
;; is-rollback-only false
|
||||
(jdbc/db-set-rollback-only! t-con)
|
||||
;; the following insert will be rolled back when the transaction ends:
|
||||
(jdbc/insert!-multi t-con :fruit
|
||||
[:name :appearance]
|
||||
[["Grape" "yummy"]
|
||||
["Pear" "bruised"]])
|
||||
(prn "is-rollback-only" (jdbc/db-is-rollback-only t-con))
|
||||
;; is-rollback-only true
|
||||
;; the following will display the inserted rows:
|
||||
(jdbc/query t-con ["SELECT * FROM fruit"]
|
||||
:row-fn println))
|
||||
(prn)
|
||||
;; outside the transaction, the following will show the original rows
|
||||
;; without those two inserted inside the (rolled-back) transaction:
|
||||
(jdbc/query db-spec ["SELECT * FROM fruit"]
|
||||
:row-fn println)
|
||||
</code></pre><h2 id="clojure-identifiers-and-sql-entities">Clojure identifiers and SQL entities</h2><p>As hinted at above, <code>java.jdbc</code> converts SQL entity names in result sets to
|
||||
keywords in Clojure by making them lowercase, and converts strings and keywords
|
||||
that specify table and column names (in maps) to SQL entities <em>as-is</em> by
|
||||
default.</p><p>You can override this behavior by specifying an options map, containing
|
||||
<code>:identifiers</code>, <code>:keywordize?</code> and/or <code>:qualifier</code>, on any function that
|
||||
returns or transforms a result set,
|
||||
and <code>:entities</code>, on any function that transforms Clojure data into SQL.</p><ul><li><code>:identifiers</code> is for converting <code>ResultSet</code> column names to keywords (or strings). It
|
||||
defaults to <code>clojure.string/lower-case</code>.</li><li><code>:keywordize?</code> controls whether to convert identifiers to keywords (the default) or not.</li><li><code>:qualifier</code> optionally specifies a namespace to qualify keywords with (when
|
||||
<code>:keywordize?</code> is not falsey).</li><li><code>:entities</code> is for converting Clojure keywords/string to SQL entity names. It
|
||||
defaults to <code>identity</code> (after calling <code>name</code> on the keyword or string).</li></ul><p>If you want to prevent <code>java.jdbc</code>'s conversion of SQL entity names to lowercase
|
||||
in a <code>query</code> result, you can specify <code>:identifiers identity</code>:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM mixedTable"]
|
||||
{:identifiers identity})
|
||||
;; produces result set with column names exactly as they appear in the DB
|
||||
</code></pre><p>It you're working with a database that has underscores in column names, you
|
||||
might want to specify a function that converts those to dashes in Clojure
|
||||
keywords:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM mixedTable"]
|
||||
{:identifiers #(.replace % \_ \-)})
|
||||
</code></pre><p>For several databases, you will often want entities to be quoted in some way
|
||||
(sometimes referred to as "stropping"). A utility function <code>quoted</code> is provided
|
||||
that accepts either a single character, a vector pair of characters, or a keyword
|
||||
as a symbolic name for the type of quoting you want (<code>:ansi</code>, <code>:mysql</code>,
|
||||
<code>:oracle</code>, <code>:sqlserver</code>), and
|
||||
returns a function suitable for use with the <code>:entities</code> option.</p><p>For example:</p><pre><code class="clojure">(jdbc/insert! db-spec :fruit
|
||||
{:name "Apple" :appearance "Round" :cost 99}
|
||||
{:entities (jdbc/quoted \`)}) ; or (jdbc/quoted :mysql)
|
||||
</code></pre><p>will execute:</p><pre><code class="clojure">INSERT INTO `fruit` ( `name`, `appearance`, `cost` )
|
||||
VALUES ( ?, ?, ? )
|
||||
</code></pre><p>with the parameters <code>"Apple", "Round", "99"</code> whereas:</p><pre><code class="clojure">(jdbc/insert! db-spec :fruit
|
||||
{:name "Apple" :appearance "Round" :cost 99}
|
||||
{:entities (jdbc/quoted [\[ \]])}) ; or (jdbc/quoted :sqlserver)
|
||||
</code></pre><p>will execute:</p><pre><code class="clojure">INSERT INTO [fruit] ( [name], [appearance], [cost] )
|
||||
VALUES ( ?, ?, ? )
|
||||
</code></pre><p>with the parameters <code>"Apple", "Round", "99"</code>.</p><h2 id="protocol-extensions-for-transforming-values">Protocol extensions for transforming values</h2><p>By default, <code>java.jdbc</code> leaves it up to Java interop and the JDBC driver library
|
||||
to perform the appropriate transformations of Clojure values to SQL values and
|
||||
vice versa. When Clojure values are passed through to the JDBC driver,
|
||||
<code>java.jdbc</code> uses <code>PreparedStatement/setObject</code> for all values by default. When
|
||||
Clojure values are read from a <code>ResultSet</code> they are left untransformed, except
|
||||
that <code>Boolean</code> values are coerced to canonical <code>true</code> / <code>false</code> values in
|
||||
Clojure (some driver / data type combinations can produce <code>(Boolean. false)</code>
|
||||
values otherwise, which do not behave like <code>false</code> in all situations).</p><p><code>java.jdbc</code> provides three protocols that you can extend, in order to modify
|
||||
these behaviors.</p><ul><li><code>ISQLValue</code> / <code>sql-value</code> - simple transformations of Clojure values to SQL
|
||||
values</li><li><code>ISQLParameter</code> / <code>set-parameter</code> - a more sophisticated transformation of
|
||||
Clojure values to SQL values that lets you override how the value is stored
|
||||
in the <code>PreparedStatement</code></li><li><code>IResultSetReadColumn</code> / <code>result-set-read-column</code> - simple transformations of
|
||||
SQL values to Clojure values when processing a <code>ResultSet</code> object</li></ul><p>If you are using a database that returns certain SQL types as custom Java types
|
||||
(e.g., PostgreSQL), you can extend <code>IResultSetReadColumn</code> to that type and
|
||||
define <code>result-set-read-column</code> to perform the necessary conversion to a usable
|
||||
Clojure data structure. The <code>result-set-read-column</code> function is called with
|
||||
three arguments:</p><ul><li>The SQL value itself</li><li>The <code>ResultSet</code> metadata object</li><li>The index of the column in the row / metadata</li></ul><p>By default <code>result-set-read-column</code> just returns its first argument (the
|
||||
<code>Boolean</code> implementation ensure the result is either <code>true</code> or <code>false</code>).</p><p>If you are using a database that requires special treatment of null values,
|
||||
e.g., TeraData, you can extend <code>ISQLParameter</code> to <code>nil</code> (and <code>Object</code>) and
|
||||
define <code>set-parameter</code> to use <code>.setNull</code> instead of <code>.setObject</code>. The
|
||||
<code>set-parameter</code> function is called with three arguments:</p><ul><li>The Clojure value itself</li><li>The <code>PreparedStatement</code> object</li><li>The index of the parameter being set</li></ul><p>By default <code>set-parameter</code> calls <code>sql-value</code> on the Clojure value and then
|
||||
calls <code>.setObject</code> to store the result of that call into the specified
|
||||
parameter in the SQL statement.</p><p>For general transformations of Clojure values to SQL values, extending
|
||||
<code>ISQLValue</code> and defining <code>sql-value</code> may be sufficient. The <code>sql-value</code>
|
||||
function is called with a single argument: the Clojure value. By default
|
||||
<code>sql-value</code> just returns its argument.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="home.html">« java.jdbc - Getting Started</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="using_ddl.html">java.jdbc - Using DDL and Metadata »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,510 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: java.jdbc - Manipulating data with SQL</title>
|
||||
|
||||
|
||||
<meta name="description" content="Contents">
|
||||
|
||||
<meta property="og:description" content="Contents">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_sql/" />
|
||||
<meta property="og:title" content="java.jdbc - Manipulating data with SQL" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_sql/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>java.jdbc - Manipulating data with SQL</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="contents">Contents</h2><ul><li><a href="../home.html">Overview</a></li><li><a href="../using_sql.html">Using SQL</a></li><li><a href="../using_ddl.html">Using DDL</a></li><li><a href="../reusing_connections.html">Reusing Connections</a></li></ul><h2 id="using-sql">Using SQL</h2><p>Here are some examples of using <code>java.jdbc</code> to manipulate data with SQL.
|
||||
These examples assume a simple table called <code>fruit</code> (see <a href="https://clojure-doc.org/articles/ecosystem/java_jdbc/using_sql/using_ddl/">Using DDL and
|
||||
Metadata</a>). These examples all assume the following in your
|
||||
<code>ns</code> declaration:</p><pre><code class="clojure">(:require [clojure.java.jdbc :as jdbc])
|
||||
</code></pre><h2 id="reading-and-processing-rows">Reading and processing rows</h2><p><code>java.jdbc</code> provides a simple <code>query</code> function to allow you to read rows from
|
||||
tables, as well as optionally performing processing on them at the same time.</p><h3 id="reading-rows">Reading rows</h3><p>To obtain a fully realized result set as a sequence of maps, you can use
|
||||
<code>query</code> with a vector containing the SQL string and any parameters needed by
|
||||
the SQL:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit"])
|
||||
;; ({:id 1 :name "Apple" :appearance "red" :cost 59 :grade 87}
|
||||
;; {:id 2 :name "Banana" :appearance "yellow" :cost 29 :grade 92.2}
|
||||
;; ...)
|
||||
|
||||
(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50])
|
||||
;; ({:id 2 :name "Banana" :appearance "yellow" :cost 29 :grade 92.2}
|
||||
;; ...)
|
||||
</code></pre><p>You can also return the result set as a sequence of vectors. The first vector
|
||||
will contain the column names, and each subsequent vector will represent a row
|
||||
of data with values in the same order as the columns.</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:as-arrays? true})
|
||||
;; ([:id :name :appearance :cost :grade]
|
||||
;; [2 "Banana" "yellow" 29 92.2]
|
||||
;; ...)
|
||||
</code></pre><h3 id="processing-a-result-set-lazily">Processing a result set lazily</h3><p>Since <code>query</code> returns a fully realized result set, it can be difficult to
|
||||
process very large results. Fortunately, <code>java.jdbc</code> provides a number of ways to process a
|
||||
large result set lazily while the connection is open, either by passing a function via
|
||||
the <code>:result-set-fn</code> option or, since release 0.7.0, via <code>reducible-query</code>.</p><p><strong><code>query</code> and <code>:result-set-fn</code></strong></p><p><em>If you are using release 0.7.0 or later, consider using <code>reducible-query</code> instead -- see below.</em></p><p>For <code>:result-set-fn</code>, the function you pass must force
|
||||
realization of the result to avoid the connection closing while the result set
|
||||
is still being processed. A <code>reduce</code>-based function is a good choice.</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:result-set-fn (fn [rs]
|
||||
(reduce (fn [total row-map]
|
||||
(+ total (:cost row-map)))
|
||||
0 rs))})
|
||||
;; produces the total cost of all the cheap fruits: 437
|
||||
</code></pre><p>Of course, a simple sum like this could be computed directly in SQL instead:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT SUM(cost) FROM fruit WHERE cost < ?" 50]
|
||||
{:result-set-fn first})
|
||||
;; {:sum(cost) 437}
|
||||
</code></pre><p>We know we will only get one row back so passing <code>first</code> to <code>:result-set-fn</code> is
|
||||
a quick way to get just that row.</p><p>Remember that if you also specify <code>:as-arrays? true</code>, your result set function
|
||||
will be passed a sequence of vectors in which the first vector contains the
|
||||
column names and subsequent vectors represent the values in the rows, matching
|
||||
the order of the column names.</p><p><strong><code>reducible-query</code></strong></p><p>This is the recommended approach since release 0.7.0 but it does come with a few
|
||||
restrictions:</p><p>You cannot use any of the following options that <code>query</code> accepts:
|
||||
<code>as-arrays?</code>, <code>:explain</code>, <code>:explain-fn</code>, <code>:result-set-fn</code>, or <code>:row-fn</code>.</p><p>On the other hand, you have access to a much faster way to process result sets:
|
||||
you can specify <code>:raw? true</code> and no conversion from Java's <code>ResultSet</code> to
|
||||
Clojure's sequence of hash maps will be performed. In particular, it's as if you
|
||||
specified <code>:identifiers identity :keywordize? false :qualifier nil</code>, and the
|
||||
sequence representation of each row is not available. That means no <code>keys</code>,
|
||||
no <code>vals</code>, no <code>seq</code> calls, just simple key lookup (for convenience, you can
|
||||
still use keyword lookup for columns, but you can also call <code>get</code> with either a
|
||||
string or a keyword).</p><p>So how does this work? <code>reducible-query</code> produces a <code>clojure.lang.IReduce</code> which,
|
||||
when reduced with a function <code>f</code>, performs the query and reduces the <code>ResultSet</code>
|
||||
using <code>f</code>, opening and closing the connection and/or transaction during the
|
||||
reduction. For example:</p><pre><code class="clojure">;; our reducing function requires two arguments: we must provide initial val
|
||||
(reduce (fn [total {:keys [cost]}] (+ total cost))
|
||||
0
|
||||
(jdbc/reducible-query db-spec
|
||||
["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:raw? true}))
|
||||
;; separating the key selection from the reducing function: we can omit val
|
||||
(transduce (map :cost)
|
||||
+ ; can be called with 0, 1, or 2 arguments!
|
||||
(jdbc/reducible-query db-spec
|
||||
["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:raw? true}))
|
||||
;; 437
|
||||
</code></pre><p>Since <code>reducible-query</code> doesn't actually run the query until you reduce its result,
|
||||
you can create it once and run it as many times as you want. This will avoid the
|
||||
overhead of option and parameter validation and handling for repeated reductions,
|
||||
since those are performed just once in the call to <code>reducible-query</code>. Note that
|
||||
the SQL parameters are fixed by that call, so this only works for running the
|
||||
<em>identical</em> query multiple times.</p><p>A reducible companion to <code>result-set-seq</code> also exists, in case you already have
|
||||
a Java <code>ResultSet</code> and want to create a <code>clojure.lang.IReduce</code>. <code>reducible-result-set</code>
|
||||
accept almost the same options as <code>result-set-seq</code>: <code>identifiers</code>, <code>keywordize?</code>,
|
||||
<code>qualifier</code>, and <code>read-columns</code>. It does not accept <code>as-arrays?</code> (for the same
|
||||
reason that <code>reducible-query</code> does not). Unlike <code>result-set-seq</code>, which produces
|
||||
a lazy sequence that can be consumed multiple times (with the first pass realizing
|
||||
it for subsequent passes), <code>reducible-result-set</code> is reducible just once: the
|
||||
underlying <code>ResultSet</code> is mutable and is consumed during the first reduction!</p><p>It should go without saying that both <code>reducible-query</code> and
|
||||
<code>reducible-result-set</code> respect <code>reduced</code> / <code>reduced?</code>.</p><p><strong>Additional Options?</strong></p><p>Note: some databases require additional options to be passed in to ensure that
|
||||
result sets are chunked and lazy. In particular, you may need to pass
|
||||
<code>:auto-commit?</code>, set appropriately, as an option to whichever function will open your database
|
||||
connection (<code>with-db-connection</code>, <code>with-db-transaction</code>, or the <code>query</code> / <code>reducible-query</code> itself
|
||||
if you are passing a bare database spec and expecting <code>query</code> / <code>reducible-query</code> to open and close
|
||||
the connection directly). You may also need to specify <code>:fetch-size</code>, <code>:result-type</code>,
|
||||
and possibly other options -- consult your database's documentation for the JDBC
|
||||
driver you are using.</p><h3 id="processing-each-row-lazily">Processing each row lazily</h3><p>As seen above, using <code>reduce</code>, <code>transduce</code>, etc with a <code>reducible-query</code> allow
|
||||
you to easily and efficiently process each row as you process the entire
|
||||
result set, but sometimes you just want a sequence of transformed rows.</p><p>We can process each row with the <code>:row-fn</code> option. Again, like with <code>:result-set-fn</code>,
|
||||
we pass a function but this time it will be
|
||||
invoked on each row, as the result set is realized.</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT name FROM fruit WHERE cost < ?" 50]
|
||||
{:row-fn :name})
|
||||
;; ("Apple" "Banana" ...)
|
||||
</code></pre><p>The result is still a fully realized sequence, but each row has been
|
||||
transformed by the <code>:name</code> function you passed in.</p><p>You can combine this with <code>:result-set-fn</code> to simplify processing of result
|
||||
sets:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50]
|
||||
{:row-fn :cost
|
||||
:result-set-fn (partial reduce +)})
|
||||
;; produces the total cost of all the cheap fruits
|
||||
</code></pre><p>or:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT SUM(cost) AS total FROM fruit WHERE cost < ?" 50]
|
||||
{:row-fn :total
|
||||
:result-set-fn first})
|
||||
;; produces the same result, via SQL
|
||||
</code></pre><p>Here is an example that manipulates rows to add computed columns:</p><pre><code class="clojure">(defn add-tax [row] (assoc row :tax (* 0.08 (:cost row))))
|
||||
|
||||
(jdbc/query db-spec ["SELECT * FROM fruit"]
|
||||
{:row-fn add-tax})
|
||||
;; produces all the rows with a new :tax column added
|
||||
</code></pre><p>All of the above can be achieved via <code>reducible-query</code> and the appropriate
|
||||
reducing function and/or transducer, but with those simple row/result set
|
||||
functions, the result is often longer / uglier:</p><pre><code class="clojure">(into [] (map :name) (jdbc/reducible-query db-spec ["SELECT name FROM fruit WHERE cost < ?" 50]))
|
||||
(transduce (map :cost) + (jdbc/reducible-query db-spec ["SELECT * FROM fruit WHERE cost < ?" 50]))
|
||||
;; :row-fn :total :result-set-fn first left as an exercise for the reader!
|
||||
(into [] (map add-tax) (jdbc/reducible-query db-spec ["SELECT * FROM fruit"]))
|
||||
</code></pre><p>If the result set is likely to be large and the reduction can use a <code>:raw? true</code>
|
||||
result set, <code>reducible-query</code> may be worth the verbosity for the performance gain.</p><h2 id="inserting-data">Inserting data</h2><p>Rows (and partial rows) can be inserted easily using the <code>insert!</code> function.
|
||||
You can insert a single row, or multiple rows. Depending on how you call
|
||||
<code>insert!</code>, the insertion will be done either through multiple SQL statements or
|
||||
through a single, batched SQL statement. That will also determine whether or
|
||||
not you get back any generated keys.</p><p>If you need a more complex form of insertion, you can use <code>execute!</code> and, if
|
||||
your database / driver supports it, you can pass <code>:return-keys</code> as an option
|
||||
to get back the generated keys. As of <code>java.jdbc</code> 0.7.6, this can be a vector
|
||||
of column names to return (for drivers that support that) or a simple Boolean.</p><h3 id="inserting-a-row">Inserting a row</h3><p>If you want to insert a single row (or partial row) and get back the generated
|
||||
keys, you can use <code>insert!</code> and specify the columns and their values as a map.
|
||||
This performs a single insert statement. A single-element sequence containing a
|
||||
map of the generated keys will be returned.</p><pre><code class="clojure">(jdbc/insert! db-spec :fruit {:name "Pear" :appearance "green" :cost 99})
|
||||
;; returns a database-specific map as the only element of a sequence, e.g.,
|
||||
;; ({:generated_key 50}) might be returned for MySQL
|
||||
</code></pre><p>Not all databases are able to return generated keys from an insert.</p><h3 id="inserting-multiple-rows">Inserting multiple rows</h3><p>There are two ways to insert multiple rows: as a sequence of maps, or as a
|
||||
sequence of vectors. In the former case, multiple inserts will be performed and
|
||||
a map of the generated keys will be returned for each insert (as a sequence).
|
||||
In the latter case, a single, batched insert will be performed and a sequence
|
||||
of row insert counts will be returned (generally a sequence of ones). The latter
|
||||
approach is likely to be substantially faster if you are inserting a large number
|
||||
of rows.</p><p>If you use <code>insert-multi!</code> and specify each row as a map of columns and their values,
|
||||
then you can specify a mixture of complete and partial rows, and you will get
|
||||
back the generated keys for each row (assuming the database has that
|
||||
capability).</p><pre><code class="clojure">(jdbc/insert-multi! db-spec :fruit
|
||||
[{:name "Pomegranate" :appearance "fresh" :cost 585}
|
||||
{:name "Kiwifruit" :grade 93}])
|
||||
;; returns a sequence of database-specific maps, e.g., for MySQL:
|
||||
;; ({generated_key 51} {generated_key 52})
|
||||
</code></pre><p>If you use <code>insert-multi!</code> and specify the columns you wish to insert followed by
|
||||
each row as a vector of column values, then you must specify the same columns
|
||||
in each row, and you will not get generated keys back, just row counts. If you
|
||||
wish to insert complete rows, you may omit the column name vector (passing
|
||||
<code>nil</code> instead) but your rows must match the natural order of columns in your
|
||||
table so be careful!</p><pre><code class="clojure">(jdbc/insert-multi! db-spec :fruit
|
||||
nil ; column names not supplied
|
||||
[[1 "Apple" "red" 59 87]
|
||||
[2 "Banana" "yellow" 29 92.2]
|
||||
[3 "Peach" "fuzzy" 139 90.0]
|
||||
[4 "Orange" "juicy" 89 88.6]])
|
||||
;; (1 1 1 1) - row counts modified
|
||||
</code></pre><p>It is generally safer to specify the columns you wish to insert so you can
|
||||
control the order, and choose to omit certain columns:</p><pre><code class="clojure">(jdbc/insert-multi! db-spec :fruit
|
||||
[:name :cost]
|
||||
[["Mango" 722]
|
||||
["Feijoa" 441]])
|
||||
;; (1 1) - row counts modified
|
||||
</code></pre><h2 id="updating-rows">Updating rows</h2><p>If you want to update simple column values in one or more rows based on a
|
||||
simple SQL predicate, you can use <code>update!</code> with a map, representing the column
|
||||
values to set, and a SQL predicate with parameters. If you need a more complex
|
||||
form of update, you can use the <code>execute!</code> function with arbitrary SQL (and
|
||||
parameters).</p><pre><code class="clojure">;; update fruit set cost = 49 where grade < ?
|
||||
(jdbc/update! db-spec :fruit
|
||||
{:cost 49}
|
||||
["grade < ?" 75])
|
||||
;; produces a sequence of the number of rows updated, e.g., (2)
|
||||
</code></pre><p>For a more complex update:</p><pre><code class="clojure">(jdbc/execute! db-spec
|
||||
["update fruit set cost = ( 2 * grade ) where grade > ?" 50.0])
|
||||
;; produces a sequence of the number of rows updated, e.g., (3)
|
||||
</code></pre><h2 id="deleting-rows">Deleting rows</h2><p>If you want to delete any rows from a table that match a simple predicate, the
|
||||
<code>delete!</code> function can be used.</p><pre><code class="clojure">(jdbc/delete! db-spec :fruit ["grade < ?" 25.0])
|
||||
;; produces a sequence of the number of rows deleted, e.g., (1)
|
||||
</code></pre><p>You can also use <code>execute!</code> for deleting rows:</p><pre><code class="clojure">(jdbc/execute! db-spec ["DELETE FROM fruit WHERE grade < ?" 25.0])
|
||||
;; produces a sequence of the number of rows deleted, e.g., (1)
|
||||
</code></pre><h2 id="using-transactions">Using transactions</h2><p>You can write multiple operations in a transaction to ensure they are either
|
||||
all performed, or all rolled back.</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec]
|
||||
(jdbc/update! t-con :fruit
|
||||
{:cost 49}
|
||||
["grade < ?" 75])
|
||||
(jdbc/execute! t-con
|
||||
["update fruit set cost = ( 2 * grade ) where grade > ?" 50.0]))
|
||||
</code></pre><p>The <code>with-db-transaction</code> macro creates a transaction-aware connection from the
|
||||
database specification and that should be used in the body of the transaction
|
||||
code.</p><p>You can specify the transaction isolation level as part of the
|
||||
<code>with-db-transction</code> binding:</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec {:isolation :serializable}]
|
||||
...)
|
||||
</code></pre><p>Possible values for <code>:isolation</code> are <code>:none</code>, <code>:read-committed</code>,
|
||||
<code>:read-uncommitted</code>, <code>:repeatable-read</code>, and <code>:serializable</code>. Be aware that not
|
||||
all databases support all isolation levels. Inside a transaction, you can call
|
||||
<code>get-isolation-level</code> to return the current level.</p><p>In addition, you can also set the current transaction-aware connection to
|
||||
rollback, and reset that setting, as well as test whether the connection is
|
||||
currently set to rollback, using the following functions:</p><pre><code class="clojure">(jdbc/db-set-rollback-only! t-con) ; this transaction will rollback instead of commit
|
||||
(jdbc/db-unset-rollback-only! t-con) ; this transaction will commit if successful
|
||||
(jdbc/db-is-rollback-only t-con) ; returns true if transaction is set to rollback
|
||||
</code></pre><h2 id="updating-or-inserting-rows-conditionally">Updating or Inserting rows conditionally</h2><p><code>java.jdbc</code> does not provide a built-in function for updating existing rows or
|
||||
inserting a new row (the older API supported this but the logic was too
|
||||
simplistic to be generally useful). If you need that functionality, it can
|
||||
sometimes be done like this:</p><pre><code class="clojure">(defn update-or-insert!
|
||||
"Updates columns or inserts a new row in the specified table"
|
||||
[db table row where-clause]
|
||||
(jdbc/with-db-transaction [t-con db]
|
||||
(let [result (jdbc/update! t-con table row where-clause)]
|
||||
(if (zero? (first result))
|
||||
(jdbc/insert! t-con table row)
|
||||
result))))
|
||||
|
||||
(update-or-insert! mysql-db :fruit
|
||||
{:name "Cactus" :appearance "Spiky" :cost 2000}
|
||||
["name = ?" "Cactus"])
|
||||
;; inserts Cactus (assuming none exists)
|
||||
(update-or-insert! mysql-db :fruit
|
||||
{:name "Cactus" :appearance "Spiky" :cost 2500}
|
||||
["name = ?" "Cactus"])
|
||||
;; updates the Cactus we just inserted
|
||||
</code></pre><p>If the <code>where-clause</code> does not uniquely identify a single row, this will update
|
||||
multiple rows which might not be what you want, so be careful!</p><h2 id="exception-handling-and-transaction-rollback">Exception Handling and Transaction Rollback</h2><p>Transactions are rolled back if an exception is thrown, as shown in these
|
||||
examples.</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec]
|
||||
(jdbc/insert-multi! t-con :fruit
|
||||
[:name :appearance]
|
||||
[["Grape" "yummy"]
|
||||
["Pear" "bruised"]])
|
||||
;; At this point the insert! call is complete, but the transaction is
|
||||
;; not. The exception will cause it to roll back leaving the database
|
||||
;; untouched.
|
||||
(throw (Exception. "sql/test exception")))
|
||||
</code></pre><p>As noted above, transactions can also be set explicitly to rollback instead of
|
||||
commit:</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec]
|
||||
(prn "is-rollback-only" (jdbc/db-is-rollback-only t-con))
|
||||
;; is-rollback-only false
|
||||
(jdbc/db-set-rollback-only! t-con)
|
||||
;; the following insert will be rolled back when the transaction ends:
|
||||
(jdbc/insert!-multi t-con :fruit
|
||||
[:name :appearance]
|
||||
[["Grape" "yummy"]
|
||||
["Pear" "bruised"]])
|
||||
(prn "is-rollback-only" (jdbc/db-is-rollback-only t-con))
|
||||
;; is-rollback-only true
|
||||
;; the following will display the inserted rows:
|
||||
(jdbc/query t-con ["SELECT * FROM fruit"]
|
||||
:row-fn println))
|
||||
(prn)
|
||||
;; outside the transaction, the following will show the original rows
|
||||
;; without those two inserted inside the (rolled-back) transaction:
|
||||
(jdbc/query db-spec ["SELECT * FROM fruit"]
|
||||
:row-fn println)
|
||||
</code></pre><h2 id="clojure-identifiers-and-sql-entities">Clojure identifiers and SQL entities</h2><p>As hinted at above, <code>java.jdbc</code> converts SQL entity names in result sets to
|
||||
keywords in Clojure by making them lowercase, and converts strings and keywords
|
||||
that specify table and column names (in maps) to SQL entities <em>as-is</em> by
|
||||
default.</p><p>You can override this behavior by specifying an options map, containing
|
||||
<code>:identifiers</code>, <code>:keywordize?</code> and/or <code>:qualifier</code>, on any function that
|
||||
returns or transforms a result set,
|
||||
and <code>:entities</code>, on any function that transforms Clojure data into SQL.</p><ul><li><code>:identifiers</code> is for converting <code>ResultSet</code> column names to keywords (or strings). It
|
||||
defaults to <code>clojure.string/lower-case</code>.</li><li><code>:keywordize?</code> controls whether to convert identifiers to keywords (the default) or not.</li><li><code>:qualifier</code> optionally specifies a namespace to qualify keywords with (when
|
||||
<code>:keywordize?</code> is not falsey).</li><li><code>:entities</code> is for converting Clojure keywords/string to SQL entity names. It
|
||||
defaults to <code>identity</code> (after calling <code>name</code> on the keyword or string).</li></ul><p>If you want to prevent <code>java.jdbc</code>'s conversion of SQL entity names to lowercase
|
||||
in a <code>query</code> result, you can specify <code>:identifiers identity</code>:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM mixedTable"]
|
||||
{:identifiers identity})
|
||||
;; produces result set with column names exactly as they appear in the DB
|
||||
</code></pre><p>It you're working with a database that has underscores in column names, you
|
||||
might want to specify a function that converts those to dashes in Clojure
|
||||
keywords:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM mixedTable"]
|
||||
{:identifiers #(.replace % \_ \-)})
|
||||
</code></pre><p>For several databases, you will often want entities to be quoted in some way
|
||||
(sometimes referred to as "stropping"). A utility function <code>quoted</code> is provided
|
||||
that accepts either a single character, a vector pair of characters, or a keyword
|
||||
as a symbolic name for the type of quoting you want (<code>:ansi</code>, <code>:mysql</code>,
|
||||
<code>:oracle</code>, <code>:sqlserver</code>), and
|
||||
returns a function suitable for use with the <code>:entities</code> option.</p><p>For example:</p><pre><code class="clojure">(jdbc/insert! db-spec :fruit
|
||||
{:name "Apple" :appearance "Round" :cost 99}
|
||||
{:entities (jdbc/quoted \`)}) ; or (jdbc/quoted :mysql)
|
||||
</code></pre><p>will execute:</p><pre><code class="clojure">INSERT INTO `fruit` ( `name`, `appearance`, `cost` )
|
||||
VALUES ( ?, ?, ? )
|
||||
</code></pre><p>with the parameters <code>"Apple", "Round", "99"</code> whereas:</p><pre><code class="clojure">(jdbc/insert! db-spec :fruit
|
||||
{:name "Apple" :appearance "Round" :cost 99}
|
||||
{:entities (jdbc/quoted [\[ \]])}) ; or (jdbc/quoted :sqlserver)
|
||||
</code></pre><p>will execute:</p><pre><code class="clojure">INSERT INTO [fruit] ( [name], [appearance], [cost] )
|
||||
VALUES ( ?, ?, ? )
|
||||
</code></pre><p>with the parameters <code>"Apple", "Round", "99"</code>.</p><h2 id="protocol-extensions-for-transforming-values">Protocol extensions for transforming values</h2><p>By default, <code>java.jdbc</code> leaves it up to Java interop and the JDBC driver library
|
||||
to perform the appropriate transformations of Clojure values to SQL values and
|
||||
vice versa. When Clojure values are passed through to the JDBC driver,
|
||||
<code>java.jdbc</code> uses <code>PreparedStatement/setObject</code> for all values by default. When
|
||||
Clojure values are read from a <code>ResultSet</code> they are left untransformed, except
|
||||
that <code>Boolean</code> values are coerced to canonical <code>true</code> / <code>false</code> values in
|
||||
Clojure (some driver / data type combinations can produce <code>(Boolean. false)</code>
|
||||
values otherwise, which do not behave like <code>false</code> in all situations).</p><p><code>java.jdbc</code> provides three protocols that you can extend, in order to modify
|
||||
these behaviors.</p><ul><li><code>ISQLValue</code> / <code>sql-value</code> - simple transformations of Clojure values to SQL
|
||||
values</li><li><code>ISQLParameter</code> / <code>set-parameter</code> - a more sophisticated transformation of
|
||||
Clojure values to SQL values that lets you override how the value is stored
|
||||
in the <code>PreparedStatement</code></li><li><code>IResultSetReadColumn</code> / <code>result-set-read-column</code> - simple transformations of
|
||||
SQL values to Clojure values when processing a <code>ResultSet</code> object</li></ul><p>If you are using a database that returns certain SQL types as custom Java types
|
||||
(e.g., PostgreSQL), you can extend <code>IResultSetReadColumn</code> to that type and
|
||||
define <code>result-set-read-column</code> to perform the necessary conversion to a usable
|
||||
Clojure data structure. The <code>result-set-read-column</code> function is called with
|
||||
three arguments:</p><ul><li>The SQL value itself</li><li>The <code>ResultSet</code> metadata object</li><li>The index of the column in the row / metadata</li></ul><p>By default <code>result-set-read-column</code> just returns its first argument (the
|
||||
<code>Boolean</code> implementation ensure the result is either <code>true</code> or <code>false</code>).</p><p>If you are using a database that requires special treatment of null values,
|
||||
e.g., TeraData, you can extend <code>ISQLParameter</code> to <code>nil</code> (and <code>Object</code>) and
|
||||
define <code>set-parameter</code> to use <code>.setNull</code> instead of <code>.setObject</code>. The
|
||||
<code>set-parameter</code> function is called with three arguments:</p><ul><li>The Clojure value itself</li><li>The <code>PreparedStatement</code> object</li><li>The index of the parameter being set</li></ul><p>By default <code>set-parameter</code> calls <code>sql-value</code> on the Clojure value and then
|
||||
calls <code>.setObject</code> to store the result of that call into the specified
|
||||
parameter in the SQL statement.</p><p>For general transformations of Clojure values to SQL values, extending
|
||||
<code>ISQLValue</code> and defining <code>sql-value</code> may be sufficient. The <code>sql-value</code>
|
||||
function is called with a single argument: the Clojure value. By default
|
||||
<code>sql-value</code> just returns its argument.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../home.html">« java.jdbc - Getting Started</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../using_ddl.html">java.jdbc - Using DDL and Metadata »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,343 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Library Development and Distribution</title>
|
||||
|
||||
|
||||
<meta name="description" content="This short guide covers how to create your own typical pure Clojure
|
||||
library and distribute it to the community via Clojars. It uses
|
||||
Clojure 1.4 and Leiningen 2.0-previewX, and requires you have git
|
||||
installed (though very little familiarity with git is required).It's assumed that you're already somewhat familiar with Clojure. If
|
||||
not, see the Getting Started and
|
||||
Introduction guides.">
|
||||
|
||||
<meta property="og:description" content="This short guide covers how to create your own typical pure Clojure
|
||||
library and distribute it to the community via Clojars. It uses
|
||||
Clojure 1.4 and Leiningen 2.0-previewX, and requires you have git
|
||||
installed (though very little familiarity with git is required).It's assumed that you're already somewhat familiar with Clojure. If
|
||||
not, see the Getting Started and
|
||||
Introduction guides.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/libraries_authoring/" />
|
||||
<meta property="og:title" content="Library Development and Distribution" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/libraries_authoring/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Library Development and Distribution</h2>
|
||||
</div>
|
||||
|
||||
<p>This short guide covers how to create your own typical pure Clojure
|
||||
library and distribute it to the community via Clojars. It uses
|
||||
Clojure 1.4 and Leiningen 2.0-previewX, and requires you have git
|
||||
installed (though very little familiarity with git is required).</p><p>It's assumed that you're already somewhat familiar with Clojure. If
|
||||
not, see the <a href="https://clojure-doc.org/articles/ecosystem/libraries_authoring/getting_started/">Getting Started</a> and
|
||||
<a href="https://clojure-doc.org/articles/ecosystem/libraries_authoring/introduction/">Introduction</a> guides.</p><p>For the purposes of this guide, the library we'll be making is named
|
||||
"<a href="https://clojars.org/trivial-library-example">trivial-library-example</a>".</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="create-the-project">Create the Project</h2><p>Create your new library project. Names are usually hyphen-separated
|
||||
lowercase words:</p><pre><code>lein new trivial-library-example
|
||||
cd trivial-library-example
|
||||
</code></pre><p>Typical lein usage is <code>lein new <proj-type> <proj-name></code>, but if you
|
||||
leave out <code><proj-type></code> (as we've done above), lein defaults to
|
||||
creating a library project for you.</p><p>Our trivial library example project will have a dependency on
|
||||
<a href="https://clojars.org/org.flatland/useful">flatland's "useful"</a>
|
||||
library.</p><p>Open up our new project.clj file and make a few changes:</p><ol><li>Add our dependency (<code>[org.flatland/useful "0.9.0"]</code>) to the <code>:dependencies</code> vector.</li><li>Remove "-SNAPSHOT" from version string.</li><li>Write a short description.</li><li>Add a url (if not a homepage, then where it's source is hosted online).</li><li>If you're using a different license, change the value for <code>:license</code>.</li></ol><p>Regarding your choice of license, probably the three most common for
|
||||
Clojure libs (along with a grossly oversimplified blurb (by this
|
||||
author) for each) are:</p><ul><li>The <a href="http://directory.fsf.org/wiki/License:EPLv1.0">Eclipse Public License</a> (the default).</li><li>The <a href="http://www.gnu.org/licenses/lgpl.html">LGPL</a> (focused most on
|
||||
code and additions always being free; includes language addressing
|
||||
s/w patent concerns). See the <a href="http://www.gnu.org/licenses/license-recommendations.html">FSF's recommendations</a> and their
|
||||
<a href="http://www.gnu.org/licenses/gpl-howto.html">instructions for use</a>.</li><li>The <a href="http://opensource.org/licenses/MIT">MIT</a> License (focused most on the user's freedom to do what
|
||||
they want with the code). The FSF calls this the <a href="http://directory.fsf.org/wiki/License:Expat">"Expat"
|
||||
License</a>.</li></ul><p>Whichever one you choose, update your project.clj (if necessary) to
|
||||
reflect that choice and save the text of the license as a file named
|
||||
"LICENSE" or "COPYING" in your project directory.</p><h3 id="a-note-regarding-project-naming">A Note Regarding Project Naming</h3><p>The top line of your project.clj includes something like <code>defproject my-project-name</code>. This means that your project has an <em>artifact-id</em>
|
||||
of "my-project-name", but it also implies a <em>group-id</em> of
|
||||
"my-project-name" (group-id = artifact-id).</p><p>The artifact-id is the name of your project. The group-id is used for
|
||||
namespacing (not the same thing as Clojure namespaces) --- it
|
||||
identifies to which group/organization a project belongs. Some
|
||||
examples of group-id's: clojurewerkz, sonian, and org.<em>your-domain</em>.</p><p>You may choose to explicitly use a group-id for your project, if you
|
||||
like. For example:</p><pre><code>(defproject org.my-domain/my-project-name ...
|
||||
...)
|
||||
</code></pre><p>The maintainers of Clojars
|
||||
<a href="https://github.com/clojars/clojars-web/wiki/Verified-Group-Names">require that new libs be published using verified groups</a>,
|
||||
such as org.my-domain.</p><p>Read more about groups at
|
||||
<a href="https://github.com/clojars/clojars-web/wiki/Groups">https://github.com/clojars/clojars-web/wiki/Groups</a>.</p><h2 id="update-the-readme">Update the README</h2><p>Aside from providing a good overview, rationale, and introduction at
|
||||
the top, you're encouraged to provide some usage examples as well. A
|
||||
link to the lib's (future) Clojars page (which we'll get to below)
|
||||
might also be appreciated. Add acknowledgements near the end, if
|
||||
appropriate. Adjust the copyright and license info at the bottom of
|
||||
the README as needed.</p><p>Lein provides you with a doc directory and a starter doc/intro.md
|
||||
file. If you find that you have more to say than will comfortably fit
|
||||
into the README.md, consider moving content into the doc dir.</p><p>Other goodies you might include in your README.md or doc/*.md files:
|
||||
tutorial, news, bugs, limitations, alternatives, troubleshooting,
|
||||
configuration.</p><p>Note that you generally won't add hand-written API documentation into
|
||||
your readme or other docs, as there are tools for creating that
|
||||
directly from your source (discussed later).</p><h2 id="create-your-projects-local-git-repository">Create your project's local git repository</h2><p>Before going much further, you probably want to get your project under
|
||||
version control. Make sure you've got git installed and configured to
|
||||
know your name and email address (i.e., that at some point you've run
|
||||
<code>git config --global user.name "Your Name"</code> and <code>git config --global user.email "your-email@somewhere.org"</code>).</p><p>Then, in your project dir, run:</p><pre><code>git init
|
||||
git add .
|
||||
git commit -m "The initial commit."
|
||||
</code></pre><p>At any time after you've made changes and want to inspect them and
|
||||
commit them to the repository:</p><pre><code>git diff
|
||||
git add -p
|
||||
git commit -m "The commit message."
|
||||
</code></pre><h2 id="write-tests">Write Tests</h2><p>In test/trivial_library_example/core_test.clj, add tests as needed.
|
||||
An example is provided in there to get you started.</p><h2 id="write-code">Write Code</h2><p>Write code to make your tests pass.</p><p>Remember to add a note at the top of each file indicating copyright
|
||||
and the license under which the code is distributed.</p><h2 id="run-tests">Run Tests</h2><p>In your project dir:</p><pre><code>lein test
|
||||
</code></pre><h2 id="commit-any-remaining-changes">Commit any remaining changes</h2><p>Before continuing to the next step, make sure all tests pass and
|
||||
you've committed all your changes. Check to see the status of your
|
||||
repo at any time with <code>git status</code> and view changes with <code>git diff</code>.</p><h2 id="create-github-project-and-upload-there">Create github project and Upload there</h2><p>This guide makes use of <a href="https://github.com/">github</a> to host your
|
||||
project code. If you don't already have a github account, create one,
|
||||
then log into it. Github provides good documentation on how to <a href="https://help.github.com/articles/set-up-git">get
|
||||
started</a> and how to
|
||||
<a href="https://help.github.com/articles/generating-ssh-keys">create an SSH key
|
||||
pair</a>. If you
|
||||
haven't already done so, get that set up before continuing.</p><p>Create a new repo there for your project using the icon/button/link
|
||||
near the top-right.</p><blockquote><p>You will have your local repository, and also a remote duplicate of
|
||||
it at github.</p></blockquote><p>For the repository name, use the same name as your project directory.
|
||||
Provide a one-line description and hit "Create repository".</p><p>Once this remote repo has been created, follow the instructions on the
|
||||
resulting page to "Push an existing repository from the command
|
||||
line". You'll of course run the <code>git</code> commands from your project
|
||||
directory:</p><pre><code>git remote add origin git@github.com:uvtc/trivial-library-example.git
|
||||
git push -u origin master
|
||||
</code></pre><p>You can now access your online repo. For this tutorial, it's
|
||||
<a href="https://github.com/uvtc/trivial-library-example">https://github.com/uvtc/trivial-library-example</a>.</p><p>Any changes you commit to your local repository can now be pushed
|
||||
to the remote one at github:</p><pre><code class="bash"># work work work
|
||||
git add -p
|
||||
git commit -m "commit message here"
|
||||
git push
|
||||
</code></pre><h2 id="create-a-gpg-key-for-signing-your-releases">Create a GPG key for signing your releases</h2><p>You'll need to create a <a href="http://www.gnupg.org/">gpg</a> key pair, which
|
||||
will be used by lein to sign any release you make to Clojars. Make
|
||||
sure you've got gpg installed and kick the tires:</p><pre><code>gpg --list-keys
|
||||
</code></pre><p>(The first time that command is run, you'll see some notices about
|
||||
it creating necessary files in your home dir.)</p><p>To create a key pair:</p><pre><code>gpg --gen-key
|
||||
</code></pre><p>Take the default key type (RSA and RSA), and default key size (2048).
|
||||
When asked for how long the key should remain valid, choose a year or
|
||||
two. Give it your real name and email address. When it prompts you for
|
||||
a comment, you might add one as it can be helpful if you have multiple
|
||||
keys to keep track of. When prompted for a passphrase, come up with one
|
||||
that is different from the one used with your ssh key.</p><p>When gpg has completed generating your keypair, you can have it list
|
||||
what keys it knows about:</p><pre><code>gpg --list-keys
|
||||
</code></pre><p>We'll use that public key in the next section.</p><h2 id="upload-to-clojars">Upload to Clojars</h2><p>If you don't already have an account at <a href="https://clojars.org/">https://clojars.org/</a>, create
|
||||
one. After doing so, you'll need to supply your ssh and gpg public
|
||||
keys to Clojars. For the ssh public key, you can use the same one as
|
||||
used with github. For the gpg public key, get it by running:</p><pre><code>gpg --export -a <your-key-id>
|
||||
</code></pre><p>where <code><your-key-id></code> is in the output of <code>gpg --list-keys</code> (the
|
||||
8-character part following the forward slash on the line starting with
|
||||
"pub"). Copy/paste that output (including the "-----BEGIN PGP PUBLIC
|
||||
KEY BLOCK-----" and "-----END PGP PUBLIC KEY BLOCK-----") into the
|
||||
form on your Clojars profile page.</p><p>For more info on working with Clojars, see <a href="https://github.com/clojars/clojars-web/wiki/About">the Clojars
|
||||
wiki</a>.</p><p>Once your Clojars account is all set up, and it has your public keys,
|
||||
upload your library to Clojars like so:</p><pre><code>lein deploy clojars
|
||||
</code></pre><p>You will be asked for your (Clojars) username and password.</p><p>Then you'll be asked for your gpg passphrase. (You won't be asked for
|
||||
your ssh passphrase because <code>lein deploy clojars</code> uses http rather
|
||||
than scp --- though Clojars supports both.)</p><p>You should now be able to see your lib's Clojars page: for example,
|
||||
<a href="https://clojars.org/trivial-library-example">https://clojars.org/trivial-library-example</a>!</p><h2 id="generate-api-docs-optional">Generate API docs (optional)</h2><p>For larger library projects, you may want to automatically generate
|
||||
API docs (from your docstrings). See
|
||||
<a href="https://github.com/weavejester/codox">codox</a>. If your library project
|
||||
is hosted at github, you can use <a href="http://pages.github.com/">github
|
||||
pages</a> to host the resulting docs.</p><h2 id="announce-optional">Announce (optional)</h2><p>You're welcome to announce the availability of your new library
|
||||
on the <a href="https://groups.google.com/forum/?fromgroups#!forum/clojure">Clojure Mailing List</a>.</p><h2 id="make-updates-to-your-library">Make Updates to your library</h2><p>Making updates to your lib follows the same pattern as described above:</p><pre><code class="bash"># work test work test
|
||||
# update version string in project.clj
|
||||
git add -p
|
||||
git commit
|
||||
git push
|
||||
lein deploy clojars
|
||||
</code></pre><p>And optionally announce the release on the ML.</p><h3 id="merging-pull-requests">Merging pull-requests</h3><p>Note that if you receive a pull-request at github, you can easily
|
||||
merge those changes into your project (right there, via the web page
|
||||
describing the pull-request). Afterwards, update your local repo to
|
||||
grab those changes as well:</p><pre><code>git pull
|
||||
</code></pre><h2 id="see-also">See Also</h2><p>For more detailed documentation on various aspects of the procedures
|
||||
described here, see:</p><ul><li>the <a href="https://github.com/clojars/clojars-web/wiki">Clojars wiki</a></li><li>the
|
||||
<a href="https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md">Leiningen tutorial</a>
|
||||
and <a href="https://github.com/technomancy/leiningen/blob/master/doc/DEPLOY.md">deploy
|
||||
guide</a></li></ul><h2 id="contributors">Contributors</h2><p>John Gabriele <a href="mailto:jmg3000@gmail.com">jmg3000@gmail.com</a> (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../libraries_directory/index.html">« A Directory of Clojure Libraries</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../generating_documentation/index.html">Generating Documentation »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
395
clones/clojure-doc.org/articles/ecosystem/maven/index.html
Normal file
395
clones/clojure-doc.org/articles/ecosystem/maven/index.html
Normal file
|
@ -0,0 +1,395 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: How to use Maven to build Clojure projects</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide describes how to use Maven to build projects written in Clojure (or in Clojure,
|
||||
and other languages, such as Java). Although Leiningen is more popular build tool in the
|
||||
Clojure community, Maven is also used for some projects, such as Clojure Contrib
|
||||
libraries, and may be useful when you need to perform some special tasks during build,
|
||||
that aren't covered by Leiningen's plugins, or when you're integrating Clojure code into
|
||||
an existing Maven project.What is Maven?">
|
||||
|
||||
<meta property="og:description" content="This guide describes how to use Maven to build projects written in Clojure (or in Clojure,
|
||||
and other languages, such as Java). Although Leiningen is more popular build tool in the
|
||||
Clojure community, Maven is also used for some projects, such as Clojure Contrib
|
||||
libraries, and may be useful when you need to perform some special tasks during build,
|
||||
that aren't covered by Leiningen's plugins, or when you're integrating Clojure code into
|
||||
an existing Maven project.What is Maven?">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/maven/" />
|
||||
<meta property="og:title" content="How to use Maven to build Clojure projects" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/maven/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>How to use Maven to build Clojure projects</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide describes how to use Maven to build projects written in Clojure (or in Clojure,
|
||||
and other languages, such as Java). Although Leiningen is more popular build tool in the
|
||||
Clojure community, Maven is also used for some projects, such as Clojure Contrib
|
||||
libraries, and may be useful when you need to perform some special tasks during build,
|
||||
that aren't covered by Leiningen's plugins, or when you're integrating Clojure code into
|
||||
an existing Maven project.</p><h2 id="what-is-maven">What is Maven?</h2><p><a href="http://maven.apache.org">Maven</a> is a software project life cycle management tool. It implements dependencies
|
||||
resolution (with automatic download of missing dependencies from repositories), building &
|
||||
testing of code, deployment of software, etc. Maven's functionality is extensible with
|
||||
plugins, so it's possible to use it not only for Java code (primary goal of this tool),
|
||||
but also for code, written in other languages. You can read more about Maven in
|
||||
<a href="http://www.sonatype.com/products/maven/documentation/book-defguide">following, freely available book</a>.</p><p>Maven differs from other tools, such as Ant - it describes <em>what</em> we want to do, in
|
||||
contrast with Ant, that describes <em>how</em> to it. Maven uses declarative style to
|
||||
describe tasks that we want to execute, and all described tasks are performed by
|
||||
the corresponding plugins.</p><p>Description of software lifecycle and information about project is stored in <code>pom.xml</code>,
|
||||
a file that should exist in root directory of the project (and in root directories of
|
||||
sub-projects, if your project is separated into several modules). Project's information
|
||||
includes name, identifier and version of the project, and often includes more information:
|
||||
URL of project's site, information about source code repository (so you can use <code>mvn scm:update</code> goal to update code, for example), etc.</p><h2 id="lifecycle-phases">Lifecycle Phases</h2><p>Project Object Model (POM) defines set of stages for project's lifecycle - they are
|
||||
called "lifecycle phases". Each phase can include several tasks (goals), that define what
|
||||
will be performed on given stage. There are several common stages: compilation (<code>compile</code>),
|
||||
testing (<code>test</code>), creation of package (<code>package</code>), and installation (<code>install</code>). Each of these
|
||||
phases has dependencies on other phases, that should be executed before its invocation
|
||||
(compilation should be executed before testing, testing before packaging, etc.).</p><p>Usually developer uses phase's name to start a process. For example, <code>mvn package</code>, or <code>mvn install</code>, etc. But developer can also execute concrete Maven's goal. To do this, he
|
||||
should specify name of plugin, that implements concrete goal, and task name in given
|
||||
plugin. For example, <code>mvn clojure:run</code> will start Clojure and execute script, specified in
|
||||
configuration. We need to mention, that list of goals, that are executed for concrete
|
||||
lifecycle phase isn't constant - you can change this list by modifying plugin's
|
||||
configuration.</p><h2 id="maven-and-clojure">Maven and Clojure</h2><p>Clojure's support in Maven is provided by
|
||||
<a href="https://github.com/talios/clojure-maven-plugin">clojure-maven-plugin</a>, that is available
|
||||
in Maven's central repository, so it always available. (Besides <code>clojure-maven-plugin</code>,
|
||||
there is also <a href="https://github.com/pallet/zi">Zi</a> plugin, that was developed as part of
|
||||
<a href="http://palletops.com/">Pallet</a> project. In contrast to <code>clojure-maven-plugin</code> it's
|
||||
written in Clojure, and more tightly integrated with Clojure-specific subsystems, such
|
||||
Marginalia, Ritz, etc.)</p><p>As a base for your projects you can use <code>pom.xml</code> file from
|
||||
<a href="https://github.com/talios/clojure-maven-example">clojure-maven-example</a> project.</p><p>If you already have <code>pom.xml</code> in your project, then to enable this plugin, you will need to
|
||||
add following code into <code><plugins></code> section of <code>pom.xml</code>:</p><pre><code class="xml"> <plugin>
|
||||
<groupId>com.theoryinpractise</groupId>
|
||||
<artifactId>clojure-maven-plugin</artifactId>
|
||||
<version>1.3.10</version>
|
||||
</plugin>
|
||||
</code></pre><p><em>Attention:</em> version number could be changed as development continues. To find latest
|
||||
plugin's version number you can use sites <a href="http://mvnrepository.com/artifact/com.theoryinpractise/clojure-maven-plugin">mvnrepository</a> or <a href="http://jarvana.com/jarvana/">Jarvana</a>, that contains
|
||||
information about packages, registered in Maven's repositories. Besides this, you can
|
||||
omit plugin version - in this case, Maven will automatically use latest available version
|
||||
(although this isn't always good idea).</p><p>Declaration of this plugin will give you all implemented functionality - compilation,
|
||||
testing & running of code, written in Clojure, etc. Although, out of box you'll need to
|
||||
use complete goals names, such as <code>clojure:compile</code>, <code>clojure:test</code> & <code>clojure:run</code>.</p><p>But you can make your life easier if you'll add these goals into list of goals for
|
||||
concrete lifecycle phases (<code>compile</code> and <code>test</code>). To do this you need to add section
|
||||
<code><executions></code> into plugin's description, as in following example:</p><pre><code class="xml"> <plugin>
|
||||
<groupId>com.theoryinpractise</groupId>
|
||||
<artifactId>clojure-maven-plugin</artifactId>
|
||||
<version>1.3.10</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</code></pre><p>In this case, source code, written in Clojure will be compiled - this useful if you
|
||||
implement <code>gen-class</code>, that will be used from Java, or if you don't want to provide source
|
||||
code for your application. But sometimes it's much better just to pack source code into
|
||||
jar, and it will compiled during loading of package (this is default behaviour when you're
|
||||
declaring <code>clojure</code> packaging type) - this allows to avoid binary incompatibility between
|
||||
different versions of Clojure. To put source code into jar, you need to add following
|
||||
code into <code>resources</code> section (or change packaging type to <code>clojure</code>):</p><pre><code class="xml"> <resource>
|
||||
<directory>src/main/clojure</directory>
|
||||
</resource>
|
||||
</code></pre><p>By default, Clojure's source code is placed in the <code>src/main/clojure</code> directory of the
|
||||
project's tree, while source code for tests is placed in the <code>src/test/clojure</code> directory.
|
||||
These default values could be changed in <a href="index.html#configure">plugin's configuration</a>.</p><h3 id="goals-in-the-clojure-maven-plugin">Goals in the Clojure Maven Plugin</h3><p><code>clojure-maven-plugin</code> implements several commands (goals) that could be divided into
|
||||
following groups:</p><ul><li><p>Goals that work with source code (usually they are linked with corresponding phases of
|
||||
lifecycle, as it's shown above):</p><ul><li><code>clojure:compile</code>: compiles source code, written in Clojure;</li><li><code>clojure:test</code>: executes tests, written in Clojure.</li><li><code>clojure:test-with-junit</code>: executes tests using JUnit;</li><li><code>clojure:add-source</code>: adds directory with source code to archive <code>...-sources.jar</code>;</li><li><code>clojure:add-testsource</code>: add directory with tests source code into archive
|
||||
<code>...-testsources.jar</code>.</li></ul></li><li><p>Goals for execution of project's code:</p><ul><li><code>clojure:run</code>: executes script (or scripts) defined by <code>script</code> and/or <code>scripts</code>
|
||||
configuration directives. This goals is often used to run project with correct
|
||||
dependencies;</li><li><code>clojure:repl</code>: starts Clojure REPL with all dependencies, specified in project. If
|
||||
necessary, it also executes script specified in configuration option <code>replScript</code> - for
|
||||
example, you can put some initialization code into it. If the JLine library was
|
||||
specified in dependencies, then it will be loaded automatically, making your work in
|
||||
REPL more comfortable;</li><li><code>clojure:swank</code>: starts Swank server, so you can connect to it from Emacs SLIME. By
|
||||
default, this server is running on port 4005 (this value could be changed with system
|
||||
option <code>clojure.swank.port</code>);</li><li><code>clojure:nailgun</code>: starts Nailgun server, so you can connect to it from Vim with
|
||||
<a href="http://kotka.de/projects/clojure/vimclojure.html">vimclojure</a>. By default, this server is running on port 2113 (this value could be
|
||||
changed with system option <code>clojure.nailgun.port</code>).</li></ul></li><li><p>Auxiliary tasks:</p><ul><li><code>clojure:marginalia</code>: generates documentation using <a href="http://fogus.github.com/marginalia/">Marginalia</a>;</li><li><code>clojure:autodoc</code>: generates documentation using <a href="http://tomfaulhaber.github.com/autodoc/">autodoc</a>;</li><li><code>clojure:gendoc</code>: generates documentation using gendoc.</li></ul></li></ul><h3 id="clojure-related-repositories">Clojure-related repositories</h3><p>There are several Clojure-related repositories. All Clojure versions (stable &
|
||||
development) are published at <a href="http://dev.clojure.org/display/doc/Maven+Settings+and+Repositories">Sonatype repository</a> that is periodically synchronized with
|
||||
Maven Central. <a href="http://clojars.org">Clojars</a> is repository that is used by Clojure community to publish their
|
||||
projects.</p><p>To use repository you need to add following code into <code>repositories</code> section in <code>pom.xml</code>:</p><pre><code class="xml"> <repository>
|
||||
<id>clojars</id>
|
||||
<url>http://clojars.org/repo/</url>
|
||||
</repository>
|
||||
</code></pre><h3 id="dependencies-management">Dependencies Management</h3><p>Maven automatically downloads the all necessary dependencies from the default repository (known as
|
||||
Maven Central), and
|
||||
repositories, specified by user (as shown above). Downloaded packages are stored in
|
||||
user's home directory and could be used by other projects without additional downloading.
|
||||
Each package is uniquely identified by combination of three parameters - group's name
|
||||
(the <code>groupId</code> tag), artifact's name (the <code>artifactId</code> tag), and version (the <code>version</code> tag).</p><p>To use Clojure in your project you need at least specify dependency on language itself.
|
||||
Right now, the stable version of Clojure is 1.4.0. To declare this dependency, add
|
||||
following code into <code>dependencies</code> section of <code>pom.xml</code> file:</p><pre><code class="xml"> <dependency>
|
||||
<groupId>org.clojure</groupId>
|
||||
<artifactId>clojure</artifactId>
|
||||
<version>1.4.0</version>
|
||||
</dependency>
|
||||
</code></pre><p>If you want to use the latest version of the language, then you need to add corresponding
|
||||
repository (snapshots) and use version number like <code>1.5.0-master-SNAPSHOT</code> instead of version
|
||||
<code>1.4.0</code>.</p><p>To perform some tasks, implemented by <code>clojure-maven-plugin</code>, you need to specify additional
|
||||
dependencies.</p><p>If you will use <code>clojure:swank</code> goal, then you need to specify dependency on <code>swank-clojure</code> package:</p><pre><code class="xml"> <dependency>
|
||||
<groupId>swank-clojure</groupId>
|
||||
<artifactId>swank-clojure</artifactId>
|
||||
<version>1.4.2</version>
|
||||
</dependency>
|
||||
</code></pre><p>If you will use <code>clojure:nailgun</code> task, then you need to download distribution from
|
||||
<a href="http://kotka.de/projects/clojure/vimclojure.html">vimclojure</a>'s site, build it, as described in documentation, and install into local
|
||||
Maven repository. And after this, you need to add following dependency on
|
||||
<code>vimclojure</code> with following code:</p><pre><code class="xml"><dependency>
|
||||
<groupId>de.kotka</groupId>
|
||||
<artifactId>vimclojure</artifactId>
|
||||
<version>X.Y.Z</version>
|
||||
</dependency>
|
||||
</code></pre><p>The JLine library isn't required, but it could be useful if you plan to use the REPL -
|
||||
this library implements support for command history and other nice things. Presence of this library is detected
|
||||
automatically when <code>mvn clojure:repl</code> goal is executed. You can add dependency for this
|
||||
library with following code:</p><pre><code class="xml"> <dependency>
|
||||
<groupId>jline</groupId>
|
||||
<artifactId>jline</artifactId>
|
||||
<version>0.9.94</version>
|
||||
</dependency>
|
||||
</code></pre><h3 id="plugins-configuration">Plugin's Configuration</h3><p>Developer can change plugin's configuration options, such as location of source code,
|
||||
scripts names, etc. To change some parameter, you need to add its new value into
|
||||
<code>configuration</code> section of the plugin's description. For example, you can specify name of
|
||||
the script, that will be executed during testing, using following code:</p><pre><code class="xml"><plugin>
|
||||
<groupId>com.theoryinpractise</groupId>
|
||||
<artifactId>clojure-maven-plugin</artifactId>
|
||||
<version>1.3.10</version>
|
||||
<configuration>
|
||||
<testScript>src/test/clojure/test.clj</testScript>
|
||||
</configuration>
|
||||
.....
|
||||
</plugin>
|
||||
</code></pre><p>Following options are used to specify options related to source code & compilation:</p><ul><li><code>sourceDirectories</code> - this option defines list of directories (each of them should be
|
||||
wrapped into <code>sourceDirectory</code> tag) that contains source code written in Clojure, and that
|
||||
will be packed into resulting jar (and compiled, if corresponding option is specified);</li><li><code>testSourceDirectories</code> - defines list of directories (each of them should be wrapped into
|
||||
<code>testSourceDirectory</code> tag) with tests, written in Clojure;</li><li><code>warnOnReflection</code> - option that enables (<code>true</code>) or disables (<code>false</code>) warnings about
|
||||
reflection during compilation of source code.</li></ul><p>Besides this, you can control which namespaces will be compiled and/or for which
|
||||
namespaces testing of source code will be performed. To do this, you need to add
|
||||
<code>namespaces</code> tag into configuration and list corresponding namespaces inside it (each of
|
||||
item should be wrapped into <code>namespace</code> tag). You can use regular expressions to specify
|
||||
all necessary namespaces, and you can also use <code>!</code> to exclude namespaces from this list. In
|
||||
addition to this option, you can use other two: <code>compileDeclaredNamespaceOnly</code> and
|
||||
<code>testDeclaredNamespaceOnly</code> (with values <code>true</code> or <code>false</code>) - they control, will be these
|
||||
namespace limitations applied during compilation and/or testing.</p><p>There are also several options that are used to specify parameters for execution of your
|
||||
code and/or tests:</p><ul><li><code>script</code> and <code>scripts</code> - defines one (<code>script</code> tag) or several (<code>scripts</code> tag with nested <code>script</code>
|
||||
tags) names of scripts with code, that will executed when you'll execute the
|
||||
<code>clojure:run</code> task;</li><li><code>testScript</code>: defines name of script that will executed when you'll execute <code>clojure:test</code>
|
||||
task. If there was no value specified in plugin's configuration, then plugin will
|
||||
automatically generate run script for all tests, that was found in project;</li><li><code>replScript</code> - defines name of script, that will executed if you'll execute <code>clojure:repl</code> task
|
||||
(it's also used by <code>clojure:swank</code> and <code>clojure:nailgun</code> tasks). This code will executed
|
||||
before entering into REPL, so you can use it to specify initialization code for your
|
||||
working environment;</li><li><code>runWithTests</code> - enables (<code>true</code>) or disables (<code>false</code>) executions of tests if you run REPL or
|
||||
your code via Maven. You can also change this value by using Maven's command-line
|
||||
option. For example, using following command <code>mvn clojure:repl -Dclojure.runwith.test=false</code>;</li><li><code>clojureOptions</code> - using this option you can specify command-line options that will be
|
||||
passed to <code>java</code> process on every invocation.</li></ul><h2 id="wrapping-up">Wrapping Up</h2><p>I think, that this article provides enough information for you to start use Maven together
|
||||
with Clojure. If you have Clojure-only project, and you don't plan to use all power of
|
||||
Maven, then may be you can look to the <a href="https://clojure-doc.org/articles/ecosystem/maven/leiningen.md">Leiningen</a> - this tool was created to build
|
||||
projects, written mostly in Clojure. Another interesting project is <a href="http://polyglot.sonatype.org/">Polyglot Maven</a>, the
|
||||
main goal of it is creation of special DSL (Domain Specificl Language) using different
|
||||
languages (Clojure, Scala, Groovy) for description of Maven's configurations (for Clojure
|
||||
this language is almost the same as language implemented in Leiningen).</p><p>Other examples of using Maven with Clojure you can find in different projects: <a href="https://github.com/liebke/incanter/tree/1.0.x">Incanter</a>
|
||||
(as example of project, consisting from several modules), <a href="https://github.com/relevance/labrepl">labrepl</a> and
|
||||
the <a href="https://github.com/talios/clojure-maven-example">clojure-maven-example</a>.</p><h2 id="where-to-learn-more">Where To Learn More</h2><p>More information on Clojure and Maven you can also find in
|
||||
following blog posts:</p><ul><li><a href="http://muckandbrass.com/web/display/~cemerick/2010/03/25/Why+using+Maven+for+Clojure+builds+is+a+no-brainer">Why using Maven for Clojure builds is a no-brainer</a> (including video, that shows how to work with the <code>clojure-maven-plugin</code>)</li><li><a href="http://pupeno.com/blog/how-to-create-a-clojure-application/">How to create a Clojure application</a></li><li><a href="http://stuartsierra.com/2009/09/03/mavens-not-so-bad">Maven’s Not So Bad</a>.</li></ul><h2 id="contributors">Contributors</h2><p><a href="http://alexott.net/en/index.html">Alex Ott</a>, 2012 (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../web_development/index.html">« Web Development (Overview)</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../community/index.html">Clojure Community »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,209 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Running a Clojure User Group</title>
|
||||
|
||||
|
||||
<meta name="description" content="About this tutorialThis guide covers:">
|
||||
|
||||
<meta property="og:description" content="About this tutorialThis guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/running_cljug/" />
|
||||
<meta property="og:title" content="Running a Clojure User Group" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/running_cljug/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Running a Clojure User Group</h2>
|
||||
</div>
|
||||
|
||||
<h2 id="about-this-tutorial">About this tutorial</h2><p>This guide covers:</p><ul><li>Starting a user group</li><li>Tips for keeping it going</li><li>Meeting ideas</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="starting-a-user-group">Starting a user group</h2><p>Learning Clojure is easier (and more fun) if you can do it with others. Some of the essentials you will need to work out before your first meeting:</p><h3 id="meeting-time">Meeting time</h3><p>If you have a handful of interested parties, it's best to run a quick poll with some options for day of the week to find the most promising candidates and then unilaterally pick one. If evenings are challenging, consider a breakfast or lunch get together!</p><h3 id="location">Location</h3><p>Location can sometimes be the hardest part of creating a new group. Some ideas:</p><ul><li>Ask a software, consulting, or recruiting company in the area. Hosting a group is a great way for potential hires to learn about a company.</li><li>Many libraries and public spaces can be reserved for meetings.</li><li>A get together at a local bar or coffee shop can be enough at the beginning.</li></ul><h3 id="meeting-format">Meeting format</h3><p>Most Clojure user groups follow one of three models:</p><ul><li>Talk with a speaker</li><li>Coding exercises (dojo, swarm coding, pairing, etc)</li><li>Informal chat</li></ul><h3 id="speakers">Speakers</h3><p>If you have trouble getting speakers, try assigning a topic (a Clojure feature, library, etc) to someone to present at the next meeting.</p><h3 id="meeting-organization">Meeting organization</h3><p>By far the two most popular ways to organize your group are <a href="http://meetup.com">Meetup</a> (use discount code "clojure" for 50% off!) or Google group mailing lists.
|
||||
Also consider creating a GitHub organization where attendees can find each others' code repos.</p><h2 id="keeping-it-going">Keeping it going</h2><p>Once you get the first meeting or two under your belt, you have to worry about how to keep it going. Consistency is one of the most important things in getting a group going - as much as possible try to stick to a stable meeting date and location.</p><p>When the group is young you’ll need to spend some effort marketing to help it grow — this is one of the reasons that Meetup.com shines. If there are local calendars, get your group listed.</p><p>Create a web site! Domain names and hosting are cheap — it’s totally worth creating a blog site dedicated to the group on your own domain name.</p><p>Create a Twitter account for the group and post info related to the group as well as specific to your topic. Ask all attendees to post about meetings on Twitter and blogs. Record your talks and put them on the net.</p><p>Consider using a private mailing list for those that attend the meetings. This is a somewhat unusual choice these days but having the limited membership means that you generally know the people that write on the mailing list and having it closed means that people can be a bit more free in asking newbie questions. Both factors contribute to a closer-knit feeling of local community.</p><p>Once you get to a certain size (or if you are fortunate to have good companies involved), you can find sponsors that provide food for your group.</p><h2 id="meeting-ideas">Meeting ideas</h2><p>Looking for meeting ideas? Here's some ideas....</p><ul><li>Work through through the <a href="https://github.com/functional-koans/clojure-koans">Clojure Koans</a></li><li>Work through problems from <a href="http://www.4clojure.com/">4Clojure</a></li><li>Run a session on getting set up on Emacs with Clojure (or Vim, or ...)</li><li>Work through <a href="http://projecteuler.net/">Project Euler</a> problems</li><li>Work through a <a href="http://codekata.pragprog.com/">Code Kata</a></li><li>Implement a game (Tic-Tac-Toe, Rock-Paper-Scissors, Checkers, Othello, etc)</li><li>Build a web site for your group in Clojure and deploy it to Heroku!</li><li>Review and expand <a href="https://clojure-doc.github.io">Clojure documentation</a> guides</li><li>Look through the <a href="http://dev.clojure.org/jira/secure/Dashboard.jspa">Clojure JIRA</a> for bugs to work on</li></ul><p>And some tips:</p><ul><li><a href="http://otfrom.wordpress.com/2012/07/04/how-to-run-a-london-clojure-dojo-in-20ish-easy-steps/">Running a Clojure dojo</a></li><li><a href="http://www.infoq.com/presentations/Swarm-Coding">Swarm coding how-to</a></li></ul><h2 id="troubleshooting">Troubleshooting</h2><h3 id="i-cant-find-enough-people-for-a-group">I can't find enough people for a group</h3><p>You might think of broadening the scope to pull in people that are interested in something similar but not exactly the same. If you can't find enough
|
||||
people for a Clojure user group, maybe a functional programming group would capture other people interested in Erlang, Scala, Haskell, F#, etc.</p><h2 id="contributors">Contributors</h2><p>Alex Miller <a href="mailto:alex@puredanger.com">alex@puredanger.com</a> (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../user_groups/index.html">« Clojure User Groups</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../books/index.html">Books about Clojure and ClojureScript »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
212
clones/clojure-doc.org/articles/ecosystem/user_groups/index.html
Normal file
212
clones/clojure-doc.org/articles/ecosystem/user_groups/index.html
Normal file
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,239 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Web Development (Overview)</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/web_development/" />
|
||||
<meta property="og:title" content="Web Development (Overview)" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/web_development/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Web Development (Overview)</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>popular tools and libraries for web development</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.4.</p><h2 id="some-options">Some Options</h2><p>Below are some of the various options available for web development
|
||||
with Clojure, listed roughly by size.</p><h3 id="ring-and-compojure">Ring and Compojure</h3><p>Perhaps the simplest and most minimal setup is to use only Ring and
|
||||
Compojure. To get started, see the <a href="../../tutorials/basic_web_development/index.html">basic web development
|
||||
tutorial</a>.</p><h3 id="lib-noir">lib-noir</h3><p>In addition to Ring and Compojure, you might also make use of
|
||||
<a href="https://github.com/noir-clojure/lib-noir">lib-noir</a>.</p><h3 id="luminus">Luminus</h3><p><a href="http://www.luminusweb.net/">Luminus</a> is <a href="https://github.com/yogthos/luminus-template">a lein
|
||||
template</a> for creating
|
||||
batteries-included web applications. It makes use of Ring, Compojure,
|
||||
lib-noir, and optionally (as described in its documentation) other
|
||||
libraries.</p><h2 id="templating-libraries">Templating Libraries</h2><p>Clojure has many options for building HTML.</p><h3 id="hiccup">Hiccup</h3><p><a href="https://github.com/weavejester/hiccup">Hiccup</a> represents HTML as
|
||||
Clojure data structures, allowing you to create and manipulate your
|
||||
HTML easily.</p><p><a href="https://github.com/davidsantiago/tinsel">Tinsel</a> is a library that
|
||||
extends Hiccup with selectors and transformers, so that you can write
|
||||
a template separate from the insertion of your data into the template.</p><h3 id="mustache">Mustache</h3><p><a href="https://github.com/fhd/clostache">Clostache</a> implements the
|
||||
<a href="http://mustache.github.com/">Mustache</a> templating language for
|
||||
Clojure.</p><p><a href="https://github.com/davidsantiago/stencil">Stencil</a> is another
|
||||
implementation of Mustache.</p><h3 id="fleet">Fleet</h3><p><a href="https://github.com/Flamefork/fleet">Fleet</a> embeds Clojure inside HTML
|
||||
templates, much like Java's JSPs, or Ruby's ERb.</p><h3 id="clabango">Clabango</h3><p><a href="https://github.com/danlarkin/clabango">Clabango</a> is modeled after the
|
||||
<a href="https://docs.djangoproject.com/en/1.4/topics/templates/">Django templating system</a>. It
|
||||
embeds special tags and filters inside HTML templates to insert and
|
||||
manipulate data.</p><h3 id="selmer">Selmer</h3><p><a href="https://github.com/yogthos/Selmer">Selmer</a> is also modeled after the Django
|
||||
templating system with a primary goal of performance.</p><h3 id="enlive-and-laser">Enlive and Laser</h3><p><a href="https://github.com/cgrand/enlive">Enlive</a> and
|
||||
<a href="https://github.com/Raynes/laser">Laser</a> are similar libraries. They
|
||||
both manipulate plain HTML, and can be used for screen scraping as
|
||||
well as templating. They work with HTML templates with no special
|
||||
embedded tags or code. They use selector functions to find pieces of
|
||||
HTML and transformation function to change the HTML into the way you
|
||||
want.</p><p>See the
|
||||
<a href="https://github.com/Raynes/laser/blob/master/docs/guide.md">Laser guide</a>
|
||||
to see if this style of templating works for you. It is powerful, but
|
||||
different from most other languages' templating libraries.</p><h2 id="see-also">See Also</h2><ul><li><p>the <a href="../libraries_directory/index.html#web_development">web development section of the library
|
||||
directory</a>.</p></li><li><p><a href="http://brehaut.net/blog/2012/clojure_web_and_the_crud_stack">The Clojure Web Stack and the CRUD Stack</a></p></li><li><p><a href="http://brehaut.net/blog/2011/ring_introduction">A Brief Overview of the Clojure Web Stack</a></p></li></ul><h2 id="contributors">Contributors</h2><ul><li>John Gabriele</li><li>Clinton Dreisbach</li></ul>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../data_processing/index.html">« Data Processing (Help Wanted)</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../maven/index.html">How to use Maven to build Clojure projects »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,648 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Collections and Sequences in Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/collections_and_sequences/" />
|
||||
<meta property="og:title" content="Collections and Sequences in Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/collections_and_sequences/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Collections and Sequences in Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>Collections in Clojure</li><li>Sequences in Clojure</li><li>Core collection types</li><li>Key operations on collections and sequences</li><li>Other topics related to collections and sequences</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="overview">Overview</h2><p>Clojure provides a number of powerful abstractions including <em>collections</em> and <em>sequences</em>.
|
||||
When working with Clojure,
|
||||
many operations are expressed as a series of operations on collections or sequences.</p><p>Most of Clojure's core library treats collections and sequences the same way, although
|
||||
sometimes a distinction has to be made (e.g. with lazy infinite sequences).</p><p><code>clojure.core</code> provides many fundamental operations on collections, such as: <code>map</code>, <code>filter</code>,
|
||||
<code>remove</code>, <code>take</code>, and <code>drop</code>. Basic operations on collections and sequences are combined
|
||||
to implement more complex operations.</p><h3 id="clojure-collections-are-immutable-persistent">Clojure Collections are Immutable (Persistent)</h3><p>Clojure collections are <em>immutable</em> (<em>persistent</em>). The term "persistent data structures" has
|
||||
nothing to do with durably storing them on disk. What it means is that collections are
|
||||
mutated (updated) by producing new collections. To quote Wikipedia:</p><blockquote><p>In computing, a persistent data structure is a data structure that always preserves
|
||||
the previous version of itself when it is modified. Such data structures are effectively
|
||||
immutable, as their operations do not (visibly) update the structure in-place, but instead
|
||||
always yield a new updated structure.</p></blockquote><p>Clojure's persistent data structures are implemented as trees and <a href="https://en.wikipedia.org/wiki/Hash_array_mapped_trie"><em>tries</em></a> and
|
||||
have O(log<sub>32</sub> <em>n</em>) access complexity where <em>n</em> is the number of elements.</p><h2 id="the-collection-abstraction">The Collection Abstraction</h2><p>Clojure has a collection abstraction with several key operations supported for
|
||||
all collection implementations. They are</p><ul><li><code>=</code>: checks value equality of a collection compared to other collections</li><li><code>count</code>: returns number of elements in a collection</li><li><code>conj</code>: adds an item to a collection in the most efficient way</li><li><code>empty</code>: returns an empty collection of the same type as the argument</li><li><code>seq</code>: gets a sequence of a collection</li></ul><p>These functions work on all core Clojure collection types.</p><h2 id="core-collection-types">Core Collection Types</h2><p>Clojure has several core collection types:</p><ul><li>Maps (called hashes or dictionaries in some other languages)</li><li>Vectors</li><li>Lists</li><li>Sets</li></ul><h3 id="maps">Maps</h3><p>Maps associate keys with values. Boths keys and values can be of any type, but
|
||||
keys must be comparable. There are several implementations of maps with
|
||||
different guarantees about ordering. Hash maps are typically instantiated with literals:</p><pre><code class="clojure">{:language "Clojure" :creator "Rich Hickey"}
|
||||
</code></pre><p>Commas can be used in map literals (Clojure compiler treats the comma as whitespace):</p><pre><code class="clojure">{:language "Clojure", :creator "Rich Hickey"}
|
||||
</code></pre><p><code>clojure.core/sorted-map</code> and <code>clojure.core/array-map</code> produce ordered maps:</p><pre><code class="klipse-clojure nohighlight">(sorted-map :language "Clojure" :creator "Rich Hickey")
|
||||
;; ⇒ {:creator "Rich Hickey", :language "Clojure"}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(array-map :language "Clojure" :creator "Rich Hickey")
|
||||
;; ⇒ {:creator "Rich Hickey", :language "Clojure"}
|
||||
</code></pre><p>Unsurprisingly, map literals must contain an even number of forms (as many keys as values). Otherwise
|
||||
the code will not compile:</p><pre><code class="klipse-clojure nohighlight">{:language "Clojure" :creator}
|
||||
</code></pre><p>In general, the only major difference between Clojure maps and maps/hashes/dictionaries in some other languages
|
||||
is that Clojure maps are <em>immutable</em>. When a Clojure map is modified, the result is a new map that internally
|
||||
has structural sharing (for efficiency reasons) but semantically is a separate immutable value.</p><h4 id="maps-as-functions">Maps As Functions</h4><p>Maps in Clojure can be used as functions on their keys. See the <a href="../functions/index.html#maps_as_functions">Functions guide</a>
|
||||
for more information.</p><h4 id="keywords-as-functions">Keywords As Functions</h4><p>Keywords in Clojure can be used as functions on maps. See the <a href="../functions/index.html#keywords_as_functions">Functions guide</a>
|
||||
for more information.</p><h3 id="vectors">Vectors</h3><p>Vectors are collections that offer efficient random access (by index). They are typically instantiated with
|
||||
literals:</p><pre><code class="clojure">[1 2 3 4]
|
||||
|
||||
["clojure" "scala" "erlang" "f#" "haskell" "ocaml"]
|
||||
</code></pre><p>Commas can be used to separate vector elements (Clojure compiler treats
|
||||
the comma as whitespace):</p><pre><code class="clojure">["clojure", "scala", "erlang", "f#", "haskell", "ocaml"]
|
||||
</code></pre><p>Unlike lists, vectors are not used for function invocation. They are, however, used to make certain
|
||||
forms (e.g. the list of locals in <code>let</code> or parameters in <code>defn</code>) stand out visually. This was
|
||||
an intentional decision in Clojure design.</p><h3 id="lists">Lists</h3><p>Lists in Clojure are singly linked lists. Access or modifications of list head is efficient, random access
|
||||
is not.</p><p>Lists in Clojure are special because they represent code forms, from function calls to macro calls to special forms.
|
||||
Code is data in Clojure and it is represented primarily as lists:</p><pre><code class="klipse-clojure nohighlight">(empty? [])
|
||||
</code></pre><p>First item on the list is said to be in the <em>calling position</em>.</p><p>When used as "just" data structures, lists are typically instantiated with literals with quoting:</p><pre><code class="klipse-clojure nohighlight">'(1 2 3 4)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">'("clojure" "scala" "erlang" "f#" "haskell" "ocaml")
|
||||
</code></pre><p>Or you can explicitly use the <code>list</code> form:</p><pre><code class="klipse-clojure nohighlight">(list 1 2 3 4)
|
||||
;; ⇒ (1 2 3 4)
|
||||
</code></pre><p>Commas can be used to separate list elements (Clojure compiler treats
|
||||
the comma as whitespace):</p><pre><code class="klipse-clojure nohighlight">'("clojure", "scala", "erlang", "f#", "haskell", "ocaml")
|
||||
</code></pre><h4 id="lists-and-metaprogramming-in-clojure">Lists and Metaprogramming in Clojure</h4><p>Metaprogramming in Clojure (and other Lisp dialects) is different from metaprogramming in, say, Ruby, because
|
||||
in Ruby metaprogramming is <em>primarily</em> about producing strings while in Clojure it is about producing
|
||||
<em>data structures</em> (mostly <em>lists</em>). For sophisticated DSLs, producing data structures directly lets
|
||||
developers avoid a lot of incidental complexity that string generation brings along.</p><p>This topic is covered in detail in the <a href="../macros/index.html">Macros and Metaprogramming</a>.</p><h3 id="sets">Sets</h3><p>Sets are collections that offer efficient membership check operation and only allow each element to appear in the collection
|
||||
once. They are typically instantiated with literals:</p><pre><code class="klipse-clojure nohighlight">#{1 2 3 4}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">#{"clojure" "scala" "erlang" "f#" "haskell" "ocaml"}
|
||||
</code></pre><p>Commas can be used to separate set elements (Clojure compiler treats the as whitespace):</p><pre><code class="klipse-clojure nohighlight">#{"clojure", "scala", "erlang", "f#", "haskell", "ocaml"}
|
||||
</code></pre><h4 id="sets-as-functions">Sets As Functions</h4><p>Sets in Clojure can be used as functions on their elements. See the <a href="../functions/index.html#sets_as_functions">Functions guide</a>
|
||||
for more information.</p><h4 id="set-membership-checks">Set Membership Checks</h4><p>The most common way of checking if an element is in a set is by using set as a function:</p><pre><code class="klipse-clojure nohighlight">(#{1 2 3 4} 1)
|
||||
;; ⇒ 1
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(#{1 2 3 4} 10)
|
||||
;; ⇒ nil
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(if (#{1 2 3 4} 1)
|
||||
:hit
|
||||
:miss)
|
||||
;; ⇒ :hit
|
||||
</code></pre><h2 id="sequences">Sequences</h2><p>The sequence abstraction represents a sequential view of a collection or collection-like
|
||||
entity (computation result).</p><p><code>clojure.core/seq</code> is a function that produces a sequence over the given argument.
|
||||
Data types that <code>clojure.core/seq</code> can produce a sequence over are called <em>seqable</em>:</p><ul><li>Clojure collections</li><li>Java maps</li><li>All iterable types (types that implement <code>java.util.Iterable</code>)</li><li>Java collections (<code>java.util.Set</code>, <code>java.util.List</code>, etc)</li><li>Java arrays</li><li>All types that implement <code>java.lang.CharSequence</code> interface, including Java strings</li><li>All types that implement <code>clojure.lang.Seqable</code> interface</li><li>nil</li></ul><p>The sequence abstraction supports several operations:</p><ul><li><code>first</code></li><li><code>rest</code></li><li><code>next</code></li></ul><p>and there are two ways to produce a sequence:</p><ul><li><code>seq</code> produces a sequence over its argument (often a collection)</li><li><code>lazy-seq</code> creates a <em>lazy sequence</em> (that is produced by performing computation)</li></ul><h3 id="seq-cons-list">seq, cons, list*</h3><p><code>clojure.core/seq</code> takes a single argument and returns a sequential view over it:</p><pre><code class="klipse-clojure nohighlight">(seq [1 2 3])
|
||||
;; ⇒ (1 2 3)
|
||||
</code></pre><p>When given an empty collection or sequence, <code>clojure.core/seq</code> returns nil:</p><pre><code class="klipse-clojure nohighlight">(seq [])
|
||||
;; ⇒ nil
|
||||
</code></pre><p>this is commonly used in the following pattern:</p><pre><code class="klipse-clojure nohighlight">(if (seq xs)
|
||||
(comment "Do something with this sequence")
|
||||
(comment "Do something else"))
|
||||
</code></pre><p>Another function that constructs sequences is <code>clojure.core/cons</code>. It prepends values to the head of
|
||||
the given sequence:</p><pre><code class="klipse-clojure nohighlight">(cons 0 (range 1 3))
|
||||
;; ⇒ (0 1 2)
|
||||
</code></pre><p><code>clojure.core/list*</code> does the same for a number of values:</p><pre><code class="klipse-clojure nohighlight">(list* 0 1 (range 2 5))
|
||||
;; ⇒ (0 1 2 3 4)
|
||||
</code></pre><p><code>clojure.core/cons</code> and <code>clojure.core/list*</code> are primarily used to produce lazy sequences and in metaprogramming (when writing
|
||||
macros). As far as metaprogramming goes, sequences and lists are the same and it is common to
|
||||
add items in the beginning of the list (into the <em>calling position</em>).</p><p>Note that <code>clojure.core/cons</code> does not create cons cells and lists in Clojure are not implemented
|
||||
as linked cons cells (like in many other dialects of Lisp).</p><h3 id="first-rest-next">first, rest, next</h3><p><code>clojure.core/first</code> returns the first item in the sequence. <code>clojure.core/next</code> and <code>clojure.core/rest</code>
|
||||
return the rest:</p><pre><code class="klipse-clojure nohighlight">(first (seq [1 2 3 4 5 6]))
|
||||
;; ⇒ 1
|
||||
|
||||
(rest (seq [1 2 3 4 5 6]))
|
||||
;; ⇒ (2 3 4 5 6)
|
||||
</code></pre><p>the difference between them is what they return on a single element sequence:</p><pre><code class="klipse-clojure nohighlight">(rest (seq [:one]))
|
||||
;; ⇒ ()
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(next (seq [:one]))
|
||||
;; ⇒ nil
|
||||
</code></pre><h3 id="lazy-sequences-in-clojure">Lazy Sequences in Clojure</h3><p><em>Lazy sequences</em> are produced by performing computation or I/O. They can be infinite
|
||||
or not have exact length (e.g. a sequence of all powers of 2 or an audio stream).</p><p>Lazy sequences is an broad topic and covered in the <a href="../laziness/index.html">Laziness</a> guide.</p><h2 id="key-operations-on-collections-and-sequences">Key Operations on Collections and Sequences</h2><p>Below is an overview of <code>clojure.core</code> functions that work on collections and sequences. Most of them
|
||||
work the same way for all types of collections, however, there are exception to this rule. For example,
|
||||
functions like <code>clojure.core/assoc</code>, <code>clojure.core/dissoc</code> and <code>clojure.core/get-in</code> only really
|
||||
make sense in the context of maps and other associative data structures (for example, records).</p><p><code>clojure.core/conj</code> adds elements to a collection in the most efficient manner, which depends on
|
||||
collection implementation details and won't be the same for vectors and lists.</p><p>In general, Clojure design emphasizes that operations on collections and sequences should be uniform and
|
||||
follow the principle of least surprise. In real world projects, however, the difference between
|
||||
algorithmic complexity and other runtime characteristics of various collection types often cannot
|
||||
be ignored. Keep this in mind.</p><p>You can find more information in the <a href="../core_overview/index.html">clojure.core Overview</a> and <a href="http://clojure.org/cheatsheet">Clojure cheatsheet</a>.</p><h3 id="count">count</h3><p>Returns a count of the number of items in a collection. An argument of nil returns 0.</p><pre><code class="klipse-clojure nohighlight">(count "Hello")
|
||||
;; ⇒ 5
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(count [1 2 3 4 5 6 7])
|
||||
;; ⇒ 7
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(count nil)
|
||||
;; ⇒ 0
|
||||
</code></pre><p>Note that count does not return in constant time for all collections. This can be determined with <code>counted?</code>.
|
||||
Keep in mind that lazy sequences must be realized to get a count of the items. This is often not intended and
|
||||
can cause a variety of otherwise cryptic errors.</p><pre><code class="klipse-clojure nohighlight">(counted? "Hello")
|
||||
;; ⇒ false
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; will be fully realized when using (count (range 10))
|
||||
(counted? (range 10))
|
||||
;; ⇒ false
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; Constant time return of (count)
|
||||
(counted? [1 2 3 4 5])
|
||||
;; ⇒ true
|
||||
</code></pre><h3 id="conj">conj</h3><p><code>conj</code> is short for "conjoin". As the name implies, <code>conj</code> takes a collection and argument(s) and returns the collection with those arguments added.</p><p>Adding items to a collection occurs at different places depending on the concrete type of collection.</p><p>List addition occurs at the beginning of the list. This is because accessing the head of the list is a constant time operation, and accessing
|
||||
the tail requires traversal of the entire list.</p><pre><code class="klipse-clojure nohighlight">(conj '(1 2) 3)
|
||||
;; ⇒ (3 1 2)
|
||||
</code></pre><p>Vectors have constant time access across the entire data structure. `'conj' thusly appends to the end of a vector.</p><pre><code class="klipse-clojure nohighlight">(conj [1 2] 3)
|
||||
;; ⇒ [1 2 3]
|
||||
</code></pre><p>Maps do not have guaranteed ordering, so the location that items are added is irrelevant. <code>conj</code> requires vectors of [key value] pairs to be
|
||||
added to the map.</p><pre><code class="klipse-clojure nohighlight">(conj {:a 1 :b 2 :c 3} [:d 4])
|
||||
;; ⇒ {:d 4, :a 1, :c 3, :b 2}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(conj {:cats 1 :dogs 2} [:ants 400] [:giraffes 13])
|
||||
;; ⇒ {:giraffes 13, :ants 400, :cats 1, :dogs 2}
|
||||
</code></pre><p>Sets also do not have guaranteed ordering. <code>conj</code> returns a set with the item added. As the concept of sets implies, added items will not duplicate equivalent items if they are present in the set.</p><pre><code class="klipse-clojure nohighlight">(conj #{1 4} 5)
|
||||
;; ⇒ #{1 4 5}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(conj #{:a :b :c} :b :c :d :e)
|
||||
;; ⇒ #{:a :c :b :d :e}
|
||||
</code></pre><h3 id="get">get</h3><p><code>get</code> returns the value for the specified key in a map or record, index of a vector or value in a set. If the key is not present,
|
||||
<code>get</code> returns nil or a supplied default value.</p><pre><code class="klipse-clojure nohighlight">;; val of a key in a map
|
||||
(get {:a 1 :b 2 :c 3} :b)
|
||||
;; ⇒ 2
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; index of a vector
|
||||
(get [10 15 20 25] 2)
|
||||
;; ⇒ 20
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; in a set, returns the value itself if present
|
||||
(get #{1 10 100 2 20 200} 1)
|
||||
;; ⇒ 1
|
||||
|
||||
```klipse-clojure
|
||||
;; returns nil if key is not present
|
||||
(get {:a 1 :b 2} :c)
|
||||
;; ⇒ nil
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; vector does not have an _index_ of 4. nil is returned
|
||||
(get [1 2 3 4] 4)
|
||||
;; ⇒ nil
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defrecord Hand [index middle ring pinky thumb])
|
||||
(get (Hand. 3 4 3.5 2 2) :index)
|
||||
;; ⇒ 3
|
||||
</code></pre><p><code>get</code> also supports a default return value supplied as the last argument.</p><pre><code class="klipse-clojure nohighlight">;; index 4 does not exist. return default value
|
||||
(get [1 2 3 4] 4 "Not Found")
|
||||
;; ⇒ "Not Found"
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; key :c does not exist, so return default value of 3
|
||||
(get {:a 1 :b 2} :c 3)
|
||||
;; ⇒ 3
|
||||
</code></pre><h3 id="assoc">assoc</h3><p><code>assoc</code> takes a key and a value and returns a collection of the same type as the supplied collection with the key mapped to the new value.</p><p><code>assoc</code> is similar to <code>get</code> in how it works with maps, records or vectors. When applied to a map or record, the same type is returned with the key/value pairs added or modified. When applied to a vector, a vector is returned with the key acting as an index and the index being replaced by the value.</p><p>Since maps and records can not contain multiple equivalent keys, supplying <code>assoc</code> with a key/value that exists in the one will cause <code>assoc</code> to return modify the key at that value in the result and not duplicate the key.</p><pre><code class="klipse-clojure nohighlight">(assoc {:a 1} :b 2)
|
||||
;; ⇒ {:b 2, :a 1}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(assoc {:a 1 :b 45 :c 3} :b 2)
|
||||
;; ⇒ {:a 1, :c 3, :b 2}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defrecord Hand [index middle ring pinky thumb])
|
||||
(assoc (Hand. 3 4 3.5 2 2) :index 3.75)
|
||||
;; ⇒ #user.Hand{:index 3.75, :middle 4, :ring 3.5, :pinky 2, :thumb 2}
|
||||
</code></pre><p>When using <code>assoc</code> with a vector, the key is the index and the value is the value to assign to that index in the returned vector.
|
||||
The key must be <= (count vector) or an index out of bounds error will occur.</p><pre><code class="klipse-clojure nohighlight">(assoc [1 2 76] 2 3) ; ⇒ [1 2 3]
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; index 5 does not exist. valid indexes for this vector are: 0, 1, 2
|
||||
(assoc [1 2 3] 5 6)
|
||||
;; the error here is slightly different in Clojure/Script
|
||||
</code></pre><p>When the key is equal to (count vector) <code>assoc</code> will add an item to the vector.</p><pre><code class="klipse-clojure nohighlight">(assoc [1 2 3] 3 4) ; ⇒ [1 2 3 4]
|
||||
</code></pre><h3 id="dissoc">dissoc</h3><p><code>dissoc</code> returns a map with the supplied keys, and subsequently their values, removed. Unlike <code>assoc</code>, <code>dissoc</code> does not work on vectors. When a record is provided, <code>dissoc</code> returns a map. For similar functionality with vectors, see <code>subvec</code> and <code>concat</code>.</p><pre><code class="klipse-clojure nohighlight">(dissoc {:a 1 :b 2 :c 3} :b)
|
||||
;; ⇒ {:a 1, :c 3}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(dissoc {:a 1 :b 14 :c 390 :d 75 :e 2 :f 51} :b :c :e)
|
||||
;; ⇒ {:a 1, :f 51, :d 75}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; note that a map is returned, not a record.
|
||||
(defrecord Hand [index middle ring pinky thumb])
|
||||
;; always be careful with the bandsaw!
|
||||
(dissoc (Hand. 3 4 3.5 2 2) :ring)
|
||||
;; ⇒ {:index 3, :middle 4, :pinky 2, :thumb 2}
|
||||
</code></pre><h3 id="first">first</h3><p><code>first</code> returns the first item in the collection. <code>first</code> returns nil if the argument is empty or is nil.</p><p>Note that for collections that do not guarantee order like some maps and sets, the behaviour of <code>first</code> should not be relied on.</p><pre><code class="klipse-clojure nohighlight">(first (range 10))
|
||||
;; ⇒ 0
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(first [:floor :piano :seagull])
|
||||
;; ⇒ :floor
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(first [])
|
||||
;; ⇒ nil
|
||||
</code></pre><h3 id="rest">rest</h3><p><code>rest</code> returns a seq of items starting with the second element in the collection. <code>rest</code> returns an empty seq if the collection only contains a single item.</p><p><code>rest</code> should also not be relied on when using maps and sets unless you are sure ordering is guaranteed.</p><pre><code class="klipse-clojure nohighlight">(rest [13 1 16 -4])
|
||||
;; ⇒ (1 16 -4)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(rest '(:french-fry))
|
||||
;; ⇒ '()
|
||||
</code></pre><p>The behaviour of <code>rest</code> should be contrasted with <code>next</code>. <code>next</code> returns nil if the collection only has a single item. This is important when considering "truthiness" of values since an empty seq is "true" but nil is not.</p><pre><code class="klipse-clojure nohighlight">(if (rest '("stuff"))
|
||||
(println "Does this print?"))
|
||||
;; yes, it prints.
|
||||
</code></pre><pre><code class="clojure">;; NEVER FINISHES EXECUTION!!!
|
||||
;; "done" is never reached because (rest x) is always a "true" value
|
||||
(defn inf
|
||||
[x]
|
||||
(if (rest x)
|
||||
(inf (rest x))
|
||||
"done"))
|
||||
</code></pre><h3 id="empty">empty?</h3><p><code>empty?</code> returns true if the collection has no items, or false if it has 1 or more items.</p><pre><code class="klipse-clojure nohighlight">(empty? [])
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(empty? '(1 2 3))
|
||||
;; ⇒ false
|
||||
</code></pre><p>Do not confuse <code>empty?</code> with <code>empty</code>. This can be a source of great confusion:</p><pre><code class="klipse-clojure nohighlight">(if (empty [1 2 3]) ;; empty returns an empty seq, which is true! use empty? here.
|
||||
"It's empty"
|
||||
"It's not empty")
|
||||
;; ⇒ "It's empty"
|
||||
</code></pre><h3 id="empty-1">empty</h3><p><code>empty</code> returns an empty collection of the same type as the collection provided.</p><pre><code class="klipse-clojure nohighlight">(empty [1 2 3])
|
||||
;; ⇒ []
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(empty {:a 1 :b 2 :c 3})
|
||||
;; ⇒ {}
|
||||
</code></pre><h3 id="not-empty">not-empty</h3><p><code>not-empty</code> returns nil if the collection has no items. If the collection contains items, the collection is returned.</p><pre><code class="klipse-clojure nohighlight">(not-empty '(:mice :elephants :children))
|
||||
;; ⇒ (:mice :elephants :children)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(not-empty '())
|
||||
;; ⇒ nil
|
||||
</code></pre><h3 id="contains">contains?</h3><p><code>contains</code> returns true if the provided <em>key</em> is present in a collection. <code>contains</code> is similar to <code>get</code> in that vectors treat the key as an index. <code>contains</code> will always return false for lists.</p><pre><code class="klipse-clojure nohighlight">(contains? {:a 1 :b 2 :c 3} :c)
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; true if index 2 exists
|
||||
(contains? ["John" "Mary" "Paul"] 2)
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; false if index 5 does not exist
|
||||
(contains? ["John" "Mary" "Paul"] 5)
|
||||
;; ⇒ false
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; "Paul" does not exist as an index
|
||||
(contains? ["John" "Mary" "Paul"] "Paul")
|
||||
;; ⇒ false
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; lists are not supported. contains? won't traverse a collection for a result.
|
||||
(contains? '(1 2 3) 0)
|
||||
;; ⇒ java.lang.IllegalArgumentException: contains? not supported on type: clojure.lang.PersistentList
|
||||
</code></pre><h3 id="some">some</h3><p><code>some</code> will apply a predicate to each value in a collection until a non-false/nil result is returned then immediately return that result.</p><p>Since collections are "true" values, this makes it possible to return the first result itself rather than simply <code>true</code>.</p><pre><code class="klipse-clojure nohighlight">(some even? [1 2 3 4 5])
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; predicate returns the value rather than simply true
|
||||
(some #(if (even? %) %) [1 2 3 4 5])
|
||||
;; ⇒ 2
|
||||
</code></pre><p>Since maps can be used as functions, you can use a map as a predicate. This will return the value of the first key in the collection that is also in the map.</p><pre><code class="klipse-clojure nohighlight">(some {:a 1 :b 5} [:h :k :d :b])
|
||||
;; ⇒ 5
|
||||
</code></pre><p>Sets can also be used as functions and will return the first item in the collection that is present in the set.</p><pre><code class="klipse-clojure nohighlight">(some #{4} (range 20))
|
||||
;; ⇒ 4
|
||||
</code></pre><h3 id="every">every?</h3><p><code>every</code> returns true if the predicate returns true for every item in the collection, otherwise it returns false.</p><pre><code class="klipse-clojure nohighlight">(every? even? (range 0 10 2))
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; set can be used to see if collection only contains items in the set.
|
||||
(every? #{2 3 4} [2 3 4 2 3 4])
|
||||
;; ⇒ true
|
||||
</code></pre><h3 id="map">map</h3><p><code>map</code> is used to sequence of values and generate a new sequence of
|
||||
values.</p><p>Essentially, you're creating a <em>mapping</em> from an old sequence of values
|
||||
to a new sequence of values.</p><pre><code class="klipse-clojure nohighlight">(def numbers
|
||||
(range 1 10))
|
||||
;; ⇒ (1 2 3 4 5 6 7 8 9)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(map (partial * 2) numbers)
|
||||
;; ⇒ (2 4 6 8 10 12 14 16 18)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(def scores
|
||||
{:clojure 10
|
||||
:scala 9
|
||||
:jruby 8})
|
||||
|
||||
(map #(str "Team " (name (key %)) " has scored " (val %)) scores)
|
||||
;; ⇒ ("Team scala has scored 9" "Team jruby has scored 8" "Team clojure has scored 10")
|
||||
</code></pre><h3 id="reduce">reduce</h3><p><code>reduce</code> takes a sequence of values and a function. It applies that
|
||||
function repeatedly with the sequence of values to <em>reduce</em> it to a
|
||||
single value.</p><pre><code class="klipse-clojure nohighlight">(def numbers
|
||||
(range 1 10))
|
||||
;; ⇒ (1 2 3 4 5 6 7 8 9)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(reduce + numbers)
|
||||
;; ⇒ 45
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(def scores
|
||||
{:clojure 10
|
||||
:scala 9
|
||||
:jruby 8})
|
||||
|
||||
(reduce + (vals scores))
|
||||
;; ⇒ 27
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; Provide an initial value for the calculation
|
||||
(reduce + 10 (vals scores))
|
||||
;; ⇒ 37
|
||||
</code></pre><h3 id="filter">filter</h3><p><code>filter</code> returns a lazy sequence of items that return <code>true</code> for the provided predicate. Contrast to <code>remove</code>.</p><pre><code class="klipse-clojure nohighlight">(filter even? (range 10))
|
||||
;; ⇒ (0 2 4 6 8)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(filter #(if (< (count %) 5) %) ["Paul" "Celery" "Computer" "Rudd" "Tayne"])
|
||||
;; ⇒ ("Paul" "Rudd")
|
||||
</code></pre><p>When using sets with <code>filter</code>, remember that if nil or false is in the set and in the collection, then the predicate will return itself: <code>nil</code>.</p><p>In this example, when nil and false are tested with the predicate, the predicate returns nil. This is because if the item is present in the set it is returned. This will cause that item to /not/ be included in the returned lazy-sequence.</p><pre><code class="klipse-clojure nohighlight">(filter #{:nothing :something nil}
|
||||
[:nothing :something :things :someone nil false :pigeons])
|
||||
;; ⇒ (:nothing :something)
|
||||
</code></pre><h3 id="remove">remove</h3><p><code>remove</code> returns a lazy sequence of items that return <code>false</code> or <code>nil</code> for the provided predicate. Contrast to <code>filter</code>.</p><pre><code class="klipse-clojure nohighlight">(remove even? (range 10))
|
||||
;; ⇒ (1 3 5 7 9)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; relative complement. probably useless?
|
||||
(remove {:a 1 :b 2} [:h :k :z :b :s])
|
||||
;; ⇒ (:h :k :z :s)
|
||||
</code></pre><p>When using sets with <code>remove</code>, remember that if nil or false is in the set and in the collection, then the predicate will return itself: <code>nil</code>.
|
||||
This will cause that item to be included in the returned lazy sequence.</p><p>In this example, when nil and false are tested with the predicate, the predicate returns nil. This is because if the item is present in the set it is returned.</p><pre><code class="klipse-clojure nohighlight">(remove #{:nothing :something nil}
|
||||
[:nothing :something :things :someone nil false :pigeons])
|
||||
;; ⇒ (:things :someone nil false :pigeons)
|
||||
</code></pre><h3 id="iterate">iterate</h3><p><code>iterate</code> takes a function and an initial value, returns the result of
|
||||
applying the function on that initial value, then applies the function
|
||||
again on the resultant value, and repeats forever, lazily. Note that the
|
||||
function <em>iterates</em> on the value.</p><pre><code class="klipse-clojure nohighlight">(take 5 (iterate inc 1))
|
||||
;; ⇒ (1 2 3 4 5)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defn multiply-by-two
|
||||
[value]
|
||||
(* 2 value))
|
||||
|
||||
(take 10 (iterate multiply-by-two 1))
|
||||
;; ⇒ (1 2 4 8 16 32 64 128 256 512)
|
||||
</code></pre><h3 id="get-in">get-in</h3><p><code>get-in</code> is used to <em>get</em> a value that is deep <em>inside</em> a data
|
||||
structure.</p><p>You have to provide the data structure and a sequence of keys, where a
|
||||
key is valid at each subsequent level of the nested data structure.</p><p>If the sequence of keys does not lead to a valid path, <code>nil</code> is
|
||||
returned.</p><pre><code class="klipse-clojure nohighlight">(def family
|
||||
{:dad {:shirt 5
|
||||
:pants 6
|
||||
:shoes 4}
|
||||
:mom {:dress {:work 6
|
||||
:casual 7}
|
||||
:book 3}
|
||||
:son {:toy 5
|
||||
:homework 1}})
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(get-in family [:dad :shirt])
|
||||
;; ⇒ 5
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(get-in family [:mom :dress])
|
||||
;; ⇒ {:work 6, :casual 7}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(get-in family [:mom :dress :casual])
|
||||
;; ⇒ 7
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(get-in family [:son :pants])
|
||||
;; ⇒ nil
|
||||
|
||||
```klipse-clojure
|
||||
(def locations
|
||||
[:office :home :school])
|
||||
|
||||
(get-in locations [1])
|
||||
;; ⇒ :home
|
||||
</code></pre><h3 id="update-in">update-in</h3><p><code>update-in</code> is used to <em>update</em> a value deep inside a structure
|
||||
<em>in-place</em>.</p><p>Note that since data structures are immutable, it only returns a
|
||||
"modified" data structure, it does not actually alter the original
|
||||
reference.</p><p>The "update" function takes the old value and returns a new value which
|
||||
<code>update-in</code> uses in the new modified data structure.</p><pre><code class="klipse-clojure nohighlight">(def family
|
||||
{:dad {:shirt 5
|
||||
:pants 6
|
||||
:shoes 4}
|
||||
:mom {:dress {:work 6
|
||||
:casual 7}
|
||||
:book 3}
|
||||
:son {:toy 5
|
||||
:homework 1}})
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(update-in family [:dad :pants] inc)
|
||||
|
||||
;; ⇒ {:son {:toy 5, :homework 1}, :mom {:dress {:work 6, :casual 7}, :book 3}, :dad {:shoes 4, :shirt 5, :pants 7}}
|
||||
</code></pre><p>Notice that "pants" gets incremented</p><pre><code class="klipse-clojure nohighlight">(def locations
|
||||
[:office :home :school])
|
||||
|
||||
(update-in locations [2] #(keyword (str "high-" (name %))))
|
||||
;; ⇒ [:office :home :high-school]
|
||||
</code></pre><h3 id="assoc-in">assoc-in</h3><p><code>assoc-in</code> is used to <em>associate</em> a new value deep inside a structure
|
||||
<em>in-place</em>.</p><p>Note that since data structures are immutable, it only returns a
|
||||
"modified" data structure, it does not actually alter the original
|
||||
reference.</p><p>Note the difference between <code>update-in</code> and <code>assoc-in</code>: <code>update-in</code>
|
||||
takes a function that applies on the old value to return a new value,
|
||||
whereas <code>assoc-in</code> takes a new value as-is.</p><pre><code class="klipse-clojure nohighlight">(def family
|
||||
{:dad {:shirt 5
|
||||
:pants 6
|
||||
:shoes 4}
|
||||
:mom {:dress {:work 6
|
||||
:casual 7}
|
||||
:book 3}
|
||||
:son {:toy 5
|
||||
:homework 1}})
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(assoc-in family [:son :crayon] 3)
|
||||
;; ⇒ {:son {:toy 5, :crayon 3, :homework 1}, :mom {:dress {:work 6, :casual 7}, :book 3}, :dad {:shoes 4, :shirt 5, :pants 6}}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(def locations
|
||||
[:office :home :school])
|
||||
|
||||
(assoc-in locations [3] :high-school)
|
||||
;; ⇒ [:office :home :school :high-school]
|
||||
</code></pre><h3 id="keys">keys</h3><p><code>keys</code> returns a sequence of the keys in a map or record.</p><pre><code class="klipse-clojure nohighlight">(keys {1 "one" 2 "two" 3 "three"})
|
||||
;; ⇒ (1 2 3)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defrecord Hand [index middle ring pinky thumb])
|
||||
(keys (Hand. 2 4 3 1 2))
|
||||
;; ⇒ (:index :middle :ring :pinky :thumb)
|
||||
</code></pre><h3 id="vals">vals</h3><p><code>vals</code> returns a sequence of vals in a map or record.</p><pre><code class="klipse-clojure nohighlight">(vals {:meows 20 :barks 2 :moos 5})
|
||||
;; ⇒ (5 2 20)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defrecord Hand [index middle ring pinky thumb])
|
||||
(vals (Hand. 1 2 3 4 5))
|
||||
;; ⇒ (1 2 3 4 5)
|
||||
</code></pre><h3 id="select-keys">select-keys</h3><p><code>select-keys</code> is used to extract a subset of a map:</p><pre><code class="klipse-clojure nohighlight">(def family
|
||||
{:dad {:shirt 5
|
||||
:pant 6
|
||||
:shoes 4}
|
||||
:mom {:dress {:work 6
|
||||
:casual 7}
|
||||
:book 3}
|
||||
:son {:toy 5
|
||||
:homework 1}})
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(select-keys family [:dad])
|
||||
;; ⇒ {:dad {:shoes 4, :shirt 5, :pant 6}}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(select-keys family [:mom :son])
|
||||
;; ⇒ {:son {:toy 5, :homework 1}, :mom {:dress {:work 6, :casual 7}, :book 3}}
|
||||
</code></pre><h3 id="take">take</h3><p><code>take</code> returns a lazy sequence of the first <code>n</code> items of a collection <code>coll</code>.</p><pre><code class="klipse-clojure nohighlight">(take 3 [1 3 5 7 9])
|
||||
;; ⇒ (1 3 5)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(type (take 3 (range)))
|
||||
;; ⇒ clojure.lang.LazySeq
|
||||
</code></pre><p>If there are fewer than <code>n</code> items in <code>coll</code>, all items will be returned.</p><pre><code class="klipse-clojure nohighlight">(take 5 [1 2 3])
|
||||
;; ⇒ (1 2 3)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(take 3 nil)
|
||||
;; ⇒ ()
|
||||
</code></pre><h3 id="drop">drop</h3><p><code>drop</code> drops <code>n</code> items from a collection <code>coll</code> and returns a lazy sequence of the rest of it.</p><pre><code class="klipse-clojure nohighlight">(drop 3 '(0 1 2 3 4 5 6))
|
||||
;; ⇒ (3 4 5 6)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(drop 2 [1 2])
|
||||
;; ⇒ ()
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(drop 2 nil)
|
||||
;; ⇒ ()
|
||||
</code></pre><h3 id="take-while">take-while</h3><p><code>take-while</code> returns a lazy sequence of items from a collection as long
|
||||
as the predicate returns <code>true</code> for each item:</p><pre><code class="klipse-clojure nohighlight">(take-while #(< % 5) (range))
|
||||
;; ⇒ (0 1 2 3 4)
|
||||
</code></pre><h3 id="drop-while">drop-while</h3><p><code>drop-while</code> drops items from a collection as long as the predicate
|
||||
returns <code>false</code> for the item and when the first non-false item is found,
|
||||
it returns a lazy sequence from that item onwards:</p><pre><code class="klipse-clojure nohighlight">(drop-while #(< % 5) (range 10))
|
||||
;; ⇒ (5 6 7 8 9)
|
||||
</code></pre><h2 id="transients">Transients</h2><p>Clojure data structures are immutable, they do not change. Mutating them produces
|
||||
a new data structure that internally has structural sharing with the original
|
||||
one. This makes a whole class of concurrency hazards go away but has some
|
||||
performance penalty and additional GC pressure.</p><p>For cases when raw performance for a piece of code is more important than safety,
|
||||
Clojure provides mutable versions of vectors and unsorted maps. They are known
|
||||
as <em>transients</em> and should only be used for locals and as an optimization
|
||||
technique after profiling.</p><p>Transients are produced from immutable data structures using the <code>clojure.core/transient</code>
|
||||
function:</p><pre><code class="klipse-clojure nohighlight">(let [m (transient {})]
|
||||
(assoc! m :key "value") ;; mutates the transient in place!
|
||||
(count m))
|
||||
;; ⇒ 1
|
||||
</code></pre><p>Note that <code>clojure.core/transient</code> does not affect nested collections, for
|
||||
example, values in a map of keywords to vectors.</p><p>To mutate transients, use <code>clojure.core/assoc!</code>, <code>clojure.core/dissoc!</code> and
|
||||
<code>clojure.core/conj!</code>. The exclamation point at the end hints that these
|
||||
functions work on transients and modify data structures in place, which
|
||||
is not safe of data structures are shared between threads.</p><p>To create an immutable data structure out of a transient, use <code>clojure.core/persistent!</code>:</p><pre><code class="klipse-clojure nohighlight">(let [m (transient {})]
|
||||
(assoc! m :key "value")
|
||||
(persistent! m)) ;; ⇒ {:key "value"}
|
||||
</code></pre><p>In conclusion: use transients only as an optimization technique and only
|
||||
after profiling and identifying hot spots in your code. Guessing is the
|
||||
shortest way we know to blowing the performance.</p><h2 id="custom-collections-and-sequences">Custom Collections and Sequences</h2><p>It is possible to develop custom collection types in Clojure or Java and have
|
||||
<code>clojure.core</code> functions work on them just like they do on builtin types.</p><p>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></p><h2 id="wrapping-up">Wrapping Up</h2><p>When working with Clojure, it is common to operate and transform collections and sequences.
|
||||
Clojure's core library unify operations on collections and sequences where possible.
|
||||
This extends to Java collections, arrays and iterable objects for seamless interoperability.</p><p>Most of the time, whenever you need a function that transforms sequences, chances are, there is
|
||||
one already that does that in <code>clojure.core</code> or you can compose more than one <code>clojure.core</code> function
|
||||
to achieve the same result.</p><h2 id="contributors">Contributors</h2><p>Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a>
|
||||
Robert Randolph <a href="mailto:audiolabs@gmail.com">audiolabs@gmail.com</a>
|
||||
satoru <a href="mailto:satorulogic@gmail.com">satorulogic@gmail.com</a></p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../namespaces/index.html">« Clojure Namespaces and Vars</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../functions/index.html">Functions in Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
|
||||
<script>
|
||||
window.klipse_settings = {
|
||||
"selector" : ".klipse-clojure"
|
||||
};
|
||||
</script>
|
||||
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,755 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Concurrency and Parallelism in Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/concurrency_and_parallelism/" />
|
||||
<meta property="og:title" content="Concurrency and Parallelism in Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/concurrency_and_parallelism/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Concurrency and Parallelism in Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>Clojure's identity/value separation</li><li>Clojure reference types and their concurrency semantics: atoms, refs, agents, vars</li><li>Dereferencing</li><li>Delays, futures and promises</li><li>Watches and validators</li><li>How to use java.util.concurrent from Clojure</li><li>Other approaches to concurrency available on the JVM</li><li>Other topics related to concurrency</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="before-you-read-this-guide">Before You Read This Guide</h2><p>This is one of the most hardcore guides of the entire Clojure documentation
|
||||
project. It describes concepts that are simple but may seem foreign at first.
|
||||
These concepts are some of the key points of Clojure
|
||||
design. Understanding them may take some time for folks without
|
||||
a concurrent programming background. Don't let this learning curve
|
||||
discourage you.</p><p>If some parts are not clear, please ask for clarification <a href="https://groups.google.com/forum/?fromgroups#!forum/clojure">on the
|
||||
mailing
|
||||
list</a> or
|
||||
<a href="https://github.com/clojure-doc/clojure-doc.github.io/issues">file an issue</a> on GitHub.
|
||||
We will work hard on making this guide easy to follow with edits and
|
||||
images to illustrate the concepts.</p><h2 id="introduction-and-terminology">Introduction and Terminology</h2><p>Before we get to the Clojure features related to concurrency, lets lay a foundation and briefly
|
||||
cover some terminology.</p><table class="table-striped table-bordered table"><thead><tr><th>Term</th><th>Definition This Guide Uses</th></tr></thead><tbody><tr><td>Concurrency</td><td>When multiple threads are making progress, whether it is via time-slicing or parallelism</td></tr><tr><td>Parallelism</td><td>A condition that arises when at least two threads are executing simultaneously, e.g., on multiple cores or CPUs.</td></tr><tr><td>Shared State</td><td>When multiple threads of execution need to mutate (modify) one or more pieces of program state (e.g., variables, identities)</td></tr><tr><td>Mutable Data Structures</td><td>Data structures that, when changed, are updated "in place"</td></tr><tr><td>Immutable Data Structures</td><td>Data structures that, when changed, produce new data structures (copies), possibly with optimizations such as internal structural sharing</td></tr><tr><td>Concurrency Hazards</td><td>Conditions that occur in concurrent programs that prevent program from being correct (behaving the way its authors intended).</td></tr><tr><td>Shared Mutable State</td><td>When shared state is made of mutable data structures. A ripe ground for concurrency hazards.</td></tr></tbody></table><p>There are many concurrency hazards, some of the most common and well known are:</p><table class="table-striped table-bordered table"><thead><tr><th>Concurrency Hazard</th><th>Brief Description</th></tr></thead><tbody><tr><td>Race Condition</td><td>A condition when the outcome is dependent on timing or relative ordering of events</td></tr><tr><td>Deadlock</td><td>When two or more threads are waiting on each other to finish or release a shared resource, thus waiting forever and not making any progress</td></tr><tr><td>Livelock</td><td>When two or more threads are technically performing computation but not doing any useful work (not making progress), for example,
|
||||
because they endlessly pass a piece of data to each other but never actually process it</td></tr><tr><td>Starvation</td><td>When a thread is not given regular access to a shared resource and cannot make progress.</td></tr></tbody></table><p>These hazards are not exclusive to threads and can happen with OS
|
||||
processes, runtime processes and any other execution processes. They
|
||||
are also not specific to a particular runtime or VM (e.g., the JVM) or
|
||||
programming language. Admittedly, some languages make it significantly
|
||||
easier to write correct, safe concurrent programs, but none are
|
||||
completely immune to concurrency hazards. More often than not,
|
||||
concurrency hazards are algorithmic problems, languages just encourage
|
||||
or discourage certain practices and techniques.</p><p><em>Thread-safe</em> code is code that is always executed correctly and does
|
||||
not suffer from concurrency hazards even when executed concurrently
|
||||
from multiple threads.</p><h2 id="overview">Overview</h2><p>One of Clojure design goals was to make concurrent programming
|
||||
easier. The thinking is that as modern CPUs add more and more cores
|
||||
and the number of CPUs is increasing as well, the biggest contributor
|
||||
to application throughput will come from making use of those
|
||||
resources.</p><p>The key design decision was making Clojure data structures immutable
|
||||
(persistent) and separating the concepts of <em>identity</em> and
|
||||
<em>value</em>. The importance of immutability cannot be over-emphasized:
|
||||
immutable values can be safely shared between threads, eliminate many
|
||||
concurrency hazards, and ultimately make it easier for developers to
|
||||
reason about their programs.</p><p>However, a language that only has immutable data structures and no way
|
||||
to change (mutate) program state is not very useful. The
|
||||
identity/value separation makes state mutations (e.g., incrementing a
|
||||
counter or adding an element to a list) possible in ways that
|
||||
have known guarantees with respect to concurrency. This separation largely
|
||||
eliminates the need for explicit use of locks, which is possible in Clojure
|
||||
but typically not necessary.</p><p>To put it another way: "changing variables" in Clojure happens
|
||||
differently from many other languages; in ways that are predictable
|
||||
from the concurrency perspective and which eliminate many concurrency hazards.</p><p>Next lets take a closer look to the identity/value separation.</p><h2 id="identityvalue-separation-on-state-and-identity">Identity/Value Separation ("on State and Identity")</h2><p>In Clojure, <em>values</em> are immutable. They never change. For example, a number is a value.
|
||||
A map <code>{:language "Clojure"}</code> is a value. A vector with 3 elements is a value.</p><p>When you attempt to modify a value (a data structure), a new value is produced instead. These
|
||||
are known as <em>persistent data structures</em> (the word "persistent" has nothing to do with
|
||||
storing data on disk).</p><p>An <em>identity</em> is a named entity (e.g., a list of active chat group
|
||||
members or a counter) that changes over time and at any given moment references a value.
|
||||
For example, the current value of a counter may be <code>42</code>. After incrementing it, the value
|
||||
is <code>43</code> but it is still the same counter --- the same identity. This is different from, say, Java
|
||||
or Ruby, where variables serve as identities that (typically) point to a mutable value
|
||||
and which are modified in place.</p><p><img src="https://clojure-doc.org/assets/images/language/concurrency_and_parallelism/identity_value.png" alt="identity_value" /></p><p>Identities in Clojure can be of several types, known as <em>reference types</em>.</p><h2 id="clojure-reference-types">Clojure Reference Types</h2><h3 id="overview-1">Overview</h3><p>In Clojure's world view, concurrent operations can be roughly
|
||||
classified as coordinated or uncoordinated, and synchronous or
|
||||
asynchronous. Different reference types in Clojure have their own
|
||||
concurrency semantics and cover different kind of operations:</p><table class="table-bordered table"><thead><tr><th></th><th>Coordinated</th><th>Uncoordinated</th></tr></thead><tbody><tr><td style="font-weight: bold;">Synchronous</td><td><a href="index.html#refs">Refs</a></td><td><a href="index.html#atoms">Atoms</a></td></tr><tr><td style="font-weight: bold;">Asynchronous</td><td>—</td><td><a href="index.html#agents">Agents</a></td></tr></tbody></table><dl><dt>Coordinated</dt><dd>An operation that depends on cooperation from other operations (possibly, other operations at least do not interfere with it)
|
||||
in order to produce correct results. For example, a banking operation that involves more than one account.
|
||||
</dd><dt>Uncoordinated</dt><dd>An operation that does not affect other operations in any way. For example, when downloading 100 Web pages concurrently,
|
||||
each operation does not affect the others.
|
||||
</dd><dt>Synchronous</dt><dd>When the caller's thread waits, blocks, or sleeps until it has access to a given resource or context.</dd><dt>Asynchronous</dt><dd>Operations that can be started or scheduled without blocking the caller's thread.</dd></dl><p>One more reference type, <a href="index.html#vars">vars</a>, supports dynamic scoping and thread-local storage.</p><h3 id="atoms">Atoms</h3><p>Atoms are references that change atomically (changes become immediately visible to all threads,
|
||||
changes are guaranteed to be synchronized by the JVM). If you come from a Java background,
|
||||
atoms are basically atomic references from <code>java.util.concurrent</code> with a functional twist
|
||||
to them. Atoms are identities that implement synchronous, uncoordinated, atomic updates.</p><p>Lets jump right in and demonstrate how atoms work using an example. We know that Clojure data
|
||||
structures are immutable by default. Adding an element to a collection really produces a new
|
||||
collection. In such case, how does one keep a shared list (say, of active connections to a server
|
||||
or recently crawled URLs) and mutate it in a thread-safe manner? We will demonstrate how to
|
||||
accomplish this with an atom.</p><p>To create an atom, use the <code>clojure.core/atom</code> function. Its argument will serve as the atom's
|
||||
initial value:</p><pre><code class="clojure">(def currently-connected (atom []))
|
||||
</code></pre><p>The line above makes the atom <code>currently-connected</code> an empty vector. To access an atom's value, use
|
||||
<code>clojure.core/deref</code> or the <code>@atom</code> reader form:</p><pre><code class="clojure">(def currently-connected (atom []))
|
||||
|
||||
@currently-connected
|
||||
;; ⇒ []
|
||||
(deref currently-connected)
|
||||
;; ⇒ []
|
||||
currently-connected
|
||||
;; ⇒ #<Atom@614b6b5d: []>
|
||||
</code></pre><p>As the returned values demonstrate, the atom itself is a reference. To
|
||||
access its current value, you <em>dereference</em> it. Dereferencing will be
|
||||
covered in more detail later in this guide. For now, it is sufficient
|
||||
to say that dereferencing returns the current value of an atom. (Other
|
||||
Clojure reference types as well as a few specialized data structures
|
||||
can be dereferenced as well.)</p><p>Locals can be atoms, too:</p><pre><code class="clojure">(let [xs (atom [])]
|
||||
@xs)
|
||||
;; ⇒ []
|
||||
</code></pre><p>Now to the most interesting part: adding elements to the collection.</p><p>To mutate an atom, we can use <code>clojure.core/swap!</code>.</p><p><code>swap!</code> takes an atom, a function and optionally some other args, swaps the current value of the atom to be the return value of calling the function with the current value of the atom and the args:</p><pre><code class="clojure">(swap! currently-connected conj "chatty-joe")
|
||||
;; ⇒ ["chatty-joe"]
|
||||
currently-connected
|
||||
;; ⇒ #<Atom@614b6b5d: ["chatty-joe"]>
|
||||
@currently-connected
|
||||
;; ⇒ ["chatty-joe"]
|
||||
</code></pre><p>To demonstrate this graphically, initial atom state looks like this:</p><p><img src="https://clojure-doc.org/assets/images/language/concurrency_and_parallelism/atom_state1.png" alt="Atom state 1" /></p><p>and then we mutated it with <code>swap!</code>:</p><p><img src="https://clojure-doc.org/assets/images/language/concurrency_and_parallelism/atom_state2.png" alt="Atom state 2" /></p><p>For the readers familiar with the atomic types from the <a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html">java.util.concurrent.atomic</a> package,
|
||||
this should sound very familiar. The only difference is that instead of setting a value, atoms are mutated
|
||||
with a function. This is both because Clojure is a functional language and because with this approach,
|
||||
<code>clojure.core/swap!</code> can <em>retry the operation</em> safely. This implies that the function you provide to
|
||||
<code>swap!</code> is <em>pure</em> (has no side effects).</p><p>Occasionally you will need to mutate the value of an atom the same way you would with atomic references in Java:
|
||||
by setting them to a specific value. This is what <code>clojure.core/reset!</code> does. It takes an atom and the new value:</p><pre><code class="clojure">@currently-connected
|
||||
;; ⇒ ["chatty-joe"]
|
||||
(reset! currently-connected [])
|
||||
;; ⇒ []
|
||||
@currently-connected
|
||||
;; ⇒ []
|
||||
</code></pre><p><code>reset!</code> may be useful in test suites to reset an atom's state between test executions, but it should be
|
||||
used sparingly in your implementation code. Consider using <code>swap!</code> first.</p><p><em>TBD: demonstrate retries under high update rates</em></p><h4 id="summary-and-use-cases">Summary and Use Cases</h4><p>Atoms is the most commonly used concurrent feature in Clojure. It covers many cases and lets developers
|
||||
avoid explicit locking. Atoms cover a lot of use cases and are very fast. It's fair to say that
|
||||
when you need uncoordinated reference types (e.g., not Software Transactional Memory), the rule of
|
||||
thumb is, "start with an atom, then see".</p><p>It is not uncommon to initialize an atom in a local and then return it from the function and share
|
||||
a piece of state with other functions and/or threads.</p><h3 id="agents">Agents</h3><p>Agents are references that are updated asynchronously: updates happen at a later, unknown point
|
||||
in time, in a thread pool. Agents are identities that implement uncoordinated, asynchronous updates.</p><p>A small but useful example of using an agent is as a counter. For
|
||||
example, suppose we want to track how often page downloads in a Web
|
||||
crawler respond with 40x and 50x status codes. The simplest version
|
||||
can look like this:</p><pre><code class="clojure">(def errors-counter (agent 0))
|
||||
;; ⇒ #'user/errors-counter
|
||||
errors-counter
|
||||
;; ⇒ #<Agent@6a6287b2: 0>
|
||||
@errors-counter
|
||||
;; ⇒ 0
|
||||
(deref errors-counter)
|
||||
;; ⇒ 0
|
||||
</code></pre><p>One can immediately make several observations: just like atoms, agents are references. To get
|
||||
the current value of an agent, we need to <em>dereference</em> it using <code>clojure.core/deref</code> or
|
||||
the <code>@agent</code> reader macro.</p><p>To mutate an agent, we use <code>clojure.core/send</code> and <code>clojure.core/send-off</code>:</p><pre><code class="clojure">@errors-counter
|
||||
;; ⇒ 0
|
||||
(send errors-counter inc)
|
||||
;; ⇒ #<Agent@6a6287b2: 0>
|
||||
@errors-counter
|
||||
;; ⇒ 1
|
||||
|
||||
;; 10 is an additional parameter. The + function will be invoked as `(+ @errors-counter 10)`.
|
||||
(send errors-counter + 10)
|
||||
;; ⇒ #<Agent@6a6287b2: 1>
|
||||
@errors-counter
|
||||
;; ⇒ 11
|
||||
</code></pre><p><code>send</code> and <code>send-off</code> are largely similar. The difference is in how they are implemented. <code>send</code> uses a
|
||||
fixed-size thread pool so using blocking operations with it won't yield good throughput. <code>send-off</code>
|
||||
uses a growing thread-pool so blocking operations is not a problem for it as long as there are resources
|
||||
available to the JVM to create and run all the threads. On a 4-8 GB machine with 4 cores and stock
|
||||
OS settings you can expect up to a couple of thousand I/O-bound threads to work without running
|
||||
the system out of kernel resources.</p><h4 id="using-custom-executors-with-agents">Using Custom Executors With Agents</h4><p>Agents can be used (and abused) for arbitrary code execution in a thread pool. Because the default
|
||||
thread pool Clojure maintains will not be a good fit for all use cases, Clojure 1.5 introduced
|
||||
a function that lets you control what thread pool (executor) is used by <code>clojure.core/send</code>:
|
||||
<code>clojure.core/set-agent-send-executor!</code>.</p><pre><code class="clojure">(import java.util.concurrent.Executors)
|
||||
|
||||
(set-agent-send-executor! (Executors/newFixedThreadPool 32))
|
||||
;; clojure.core/send now will use the fixed size thread pool with 32 threads
|
||||
</code></pre><p>The default thread pool size is <code>number of available CPU cores + 2</code>.</p><p><code>clojure.core/set-agent-send-off-executor!</code> is a similar function that controls what
|
||||
thread pool <code>clojure.core/send-off</code> will use.</p><p>Finally, another new function in 1.5 is <code>clojure.core/send-via</code> which is like `` but lets you specify
|
||||
an executor to be used on a case-by-case basis:</p><pre><code class="clojure">(import java.util.concurrent.Executors)
|
||||
|
||||
(def custom-pool (Executors/newFixedThreadPool 32))
|
||||
;; just like clojure.core/send but will use custom-pool instead
|
||||
;; of an internally maintained one
|
||||
(send-via custom-pool stream-agent inc)
|
||||
</code></pre><h3 id="agents-and-software-transactional-memory">Agents and Software Transactional Memory</h3><p>We haven't introduced refs and the concept of Software Transactional Memory yet. It will be covered later in this
|
||||
guide. Here it's sufficient to mention that agents are STM-aware and can be safely used inside transactions.</p><h3 id="agents-and-error-handling">Agents and Error Handling</h3><p>Functions that modify an agent's state will not always return successfully in the real world. Sometimes they
|
||||
will fail. For example:</p><pre><code class="clojure">@errors-counter
|
||||
;; ⇒ 11
|
||||
(send errors-counter / 0)
|
||||
;; Evaluation aborted.
|
||||
;; ⇒ nil
|
||||
</code></pre><p>This puts the agent into the <em>failed</em> state. Failed agents will re-raise the exception that caused them
|
||||
to fail every time their state changed is attempted:</p><pre><code class="clojure">(send errors-counter / 0)
|
||||
;; ⇒ #<Agent@6a6287b2: 10>
|
||||
(send errors-counter inc)
|
||||
;; Evaluation aborted.
|
||||
</code></pre><p>To access the exception that occured during the agent's state mutation, use <code>clojure.core/agent-error</code>:</p><pre><code class="clojure">(send errors-counter / 0)
|
||||
;; Evaluation aborted.
|
||||
;; ⇒ nil
|
||||
(agent-error errors-counter)
|
||||
;; ⇒ #<ArithmeticException java.lang.ArithmeticException: Divide by zero>
|
||||
</code></pre><p>It returns an exception. Agents can be restarted with <code>clojure.core/restart-agent</code> that takes an agent
|
||||
and a new initial value:</p><pre><code class="clojure">(restart-agent errors-counter 0)
|
||||
;; ⇒ 0
|
||||
(send errors-counter + 10)
|
||||
;; ⇒ #<Agent@6a6287b2: 0>
|
||||
@errors-counter
|
||||
;; ⇒ 10
|
||||
</code></pre><p>If you'd prefer an agent to ignore exceptions instead of going into the <em>failure mode</em>, <code>clojure.core/agent</code>
|
||||
takes an option that controls this behavior: <code>:error-mode</code>. Because completely ignoring errors is rarely a good
|
||||
idea, when the error mode is set to <code>:continue</code> you must also pass an error handler function:</p><pre><code class="clojure">(def errors-counter (agent 0
|
||||
:error-mode :continue
|
||||
:error-handler (fn [failed-agent ^Exception exception]
|
||||
(println (.getMessage exception)))))
|
||||
;; ⇒ #'user/errors-counter
|
||||
(send errors-counter inc)
|
||||
;; ⇒ #<Agent@5620e147: 1>
|
||||
(send errors-counter inc)
|
||||
;; ⇒ #<Agent@5620e147: 2>
|
||||
(send errors-counter / 0)
|
||||
;; output: "Divide by zero"
|
||||
;; ⇒ #<Agent@5620e147: 2>
|
||||
(send errors-counter inc)
|
||||
;; ⇒ #<Agent@5620e147: 3>
|
||||
@errors-counter
|
||||
;; ⇒ 3
|
||||
</code></pre><p>The handler function takes two arguments: an agent and the exception that occured.</p><h4 id="summary-and-use-cases-1">Summary and Use Cases</h4><p>Agents are asynchronously updated references. They can be used for anything that does
|
||||
not require strict consistency for reads:</p><ul><li>Counters (e.g. message rates in event processing)</li><li>Collections (e.g. recently processed events)</li></ul><p>Agents can be used for offloading arbitrary computations to a thread pool, however,
|
||||
only starting with Clojure 1.5 they can provide the same flexiblity as JDK executors
|
||||
(thread pools).</p><h3 id="refs">Refs</h3><p>Refs are the only <em>coordinated</em> reference type Clojure has. They help ensure that multiple
|
||||
identities can be modified concurrently within a <em><a href="https://clojure-doc.org/articles/language/concurrency_and_parallelism/glossary.html#transaction">transaction</a></em>:</p><ul><li>Either all refs are modified or none are</li><li>No race conditions between involved refs</li><li>No possibility of deadlocks between involved refs</li></ul><p>Refs provide ACI of <a href="http://en.wikipedia.org/wiki/ACID">ACID</a>. Refs
|
||||
are backed by Clojure's implementation of <a href="https://clojure-doc.org/articles/language/concurrency_and_parallelism/glossary.html#stm"><em>software transactional
|
||||
memory</em> (STM)</a>.</p><p>To instantiate a ref, use the <code>clojure.core/ref</code> function:</p><pre><code class="clojure">(def account-a (ref 0))
|
||||
;; ⇒ #'user/account-a
|
||||
(def account-b (ref 0))
|
||||
;; ⇒ #'user/account-b
|
||||
</code></pre><p>Like atoms and agents covered earlier, to get the current value of a ref, use <code>clojure.core/deref</code> or the "<code>@</code>"
|
||||
reader macro:</p><pre><code class="clojure">(deref account-a)
|
||||
;; ⇒ 0
|
||||
@account-b
|
||||
;; ⇒ 0
|
||||
</code></pre><p>Refs are for coordinated concurrent operations and so it does not make much sense to use a single ref
|
||||
(in that case, an atom would be sufficient). Refs are modified in a transaction in the <code>clojure.core/dosync</code>
|
||||
body.</p><p><code>clojure.core/dosync</code> starts a transaction, performs all modifications and commits changes. If a concurrently
|
||||
running transaction modifies a ref in the current transaction before the current transaction commits,
|
||||
the current transaction will be <em>retried</em> to make sure that the most recent value of the modified
|
||||
ref is used.</p><p><em>TBD: a picture that visualizes retries and serializability.</em></p><h4 id="alter">alter</h4><p>Refs are modified using <code>clojure.core/alter</code> which is very similar to
|
||||
<code>clojure.core/swap!</code> in the arguments it takes: a ref, a function that
|
||||
takes an old value and returns a new value of the ref, and any number
|
||||
of optional arguments to pass to the function.</p><p>In the following example, two refs are initialized at 1000,
|
||||
representing two bank accounts. Then 100 units are transferred from
|
||||
one account to the other, atomically:</p><pre><code class="clojure">(def account-a (ref 1000))
|
||||
;; ⇒ #'user/account-a
|
||||
(def account-b (ref 1000))
|
||||
;; ⇒ #'user/account-b
|
||||
|
||||
(dosync
|
||||
;; will be executed as (+ @account-a 100)
|
||||
(alter account-a + 100)
|
||||
;; will be executed as (- @account-b 100)
|
||||
(alter account-b - 100))
|
||||
;; ⇒ 900
|
||||
@account-a
|
||||
;; ⇒ 1100
|
||||
@account-b
|
||||
;; ⇒ 900
|
||||
</code></pre><h4 id="conflicts-and-retries">Conflicts and Retries</h4><p><em>TBD: explain transaction conflicts, demonstrate transaction retries</em></p><h4 id="commute">commute</h4><p>With a high number of concurrently running transactions, retries
|
||||
overhead can become noticeable. Some modifications, however, can be
|
||||
applied in any order. Clojure's STM implementation acknowledges this
|
||||
fact and provides an alternative way to modify refs:
|
||||
<code>clojure.core/commute</code>. <code>commute</code> must only be used for operations
|
||||
that <a href="http://mathforum.org/dr.math/faq/faq.property.glossary.html#commutative">commute in the mathematical
|
||||
sense</a>:
|
||||
the order can be changed without affecting the result. For example,
|
||||
addition is commutative (1 + 10 produces the same result as 10 + 1)
|
||||
but substraction is not (1 − 10 does not equal 10 − 1).</p><p><code>clojure.core/commute</code> has the same signature as <code>clojure.core/alter</code>:</p><pre><code class="clojure">@account-a
|
||||
;; ⇒ 1100
|
||||
@account-b
|
||||
;; ⇒ 900
|
||||
(dosync
|
||||
(commute account-a + 300)
|
||||
(commute account-b + 300))
|
||||
;; ⇒ 1200
|
||||
@account-a
|
||||
;; ⇒ 1400
|
||||
@account-b
|
||||
;; ⇒ 1200
|
||||
</code></pre><p>Note that a change made to a ref by <code>commute</code> will never cause a transaction
|
||||
to retry. <code>commute</code> does not cause <em>transaction conflicts</em>.</p><h4 id="using-refs-with-clojure-data-structures">Using Refs With Clojure Data Structures</h4><p><em>TBD: demonstrate more complex changes, e.g., to game characters</em></p><h4 id="limitations-of-refs">Limitations of Refs</h4><p>Software transactional memory is a powerful but highly specialized tool. Because transactions can be retried,
|
||||
you must only use pure functions with STM. I/O operations cannot be undone by the runtime and very often are
|
||||
not <a href="https://clojure-doc.org/articles/language/concurrency_and_parallelism/glossary.html#idempotent">idempotent</a>.</p><p>Structuring your application code as <em>pure core</em> and <em>edge code</em> that interacts with the user or other
|
||||
services (performing I/O operations and other side-effects) helps with this. In that case, the pure core
|
||||
can use STM without issues.</p><p>For example, in a Web or network server, incoming requests are the edge code: they do I/O. The pure core
|
||||
is then called to modify server state, do any calculations necessary, return a result that is returned
|
||||
back to the client by the edge code:</p><p><em>TBD: a picture to demonstrate</em></p><p>Unlike some other languages and runtimes (for example, Haskell), Clojure <em>will not prevent you from
|
||||
doing I/O in transactions</em>. It is left as a matter of discipline on the programmer's part. It does provide
|
||||
a helper function, though: <code>clojure.core/io!</code> will raise an exception if there is an STM transaction
|
||||
running and has no effect otherwise.</p><p>First, an example with pure code:</p><pre><code class="clojure">(io!
|
||||
;; pure code, clojure.core/io! has no effect
|
||||
(reduce + (range 0 100)))
|
||||
;; ⇒ 4950
|
||||
</code></pre><p>And an example that invokes functions that are guarded with <code>clojure.core/io!</code> in an STM
|
||||
transaction:</p><pre><code class="clojure">(defn render-results
|
||||
"Prints results to the standard output"
|
||||
[]
|
||||
(io!
|
||||
(println "Results:")
|
||||
(comment ...)))
|
||||
;; ⇒ #'user/render-results
|
||||
(dosync
|
||||
(alter account-a + 100)
|
||||
(alter account-b - 100)
|
||||
(render-results))
|
||||
;; throws java.lang.IllegalStateException, "I/O in transaction!"
|
||||
</code></pre><h4 id="summary-and-use-cases-2">Summary and Use Cases</h4><p><em>TBD</em></p><h3 id="vars">Vars</h3><p>Vars are the reference type you are already familiar with. You define them via the <code>def</code> special form:</p><pre><code class="clojure">(def url "http://en.wikipedia.org/wiki/Margarita")
|
||||
</code></pre><p>Functions defined via <code>defn</code> are also stored in vars. Vars can be dynamically scoped. They have
|
||||
<em>root bindings</em> that are initially visible to all threads. When defining a var
|
||||
with <code>def</code>, you define a var that only has root binding, so its value will be the same no matter
|
||||
what thread you use it from:</p><pre><code class="clojure">(def url "http://en.wikipedia.org/wiki/Margarita")
|
||||
;; ⇒ #'user/url
|
||||
(.start (Thread. (fn []
|
||||
(println (format "url is %s" url)))))
|
||||
;; outputs "url is http://en.wikipedia.org/wiki/Margarita"
|
||||
;; ⇒ nil
|
||||
(.start (Thread. (fn []
|
||||
(println (format "url is %s" url)))))
|
||||
;; outputs "url is http://en.wikipedia.org/wiki/Margarita"
|
||||
;; ⇒ nil
|
||||
</code></pre><h4 id="dynamic-scoping-and-thread-local-bindings">Dynamic Scoping and Thread-local Bindings</h4><p>To temporarily change var value, we need to make the var dynamic by adding <code>:dynamic true</code> to its
|
||||
metadata and then use <code>clojure.core/binding</code>:</p><pre><code class="clojure">(def ^:dynamic *url* "http://en.wikipedia.org/wiki/Margarita")
|
||||
;; ⇒ #'user/*url*
|
||||
(println (format "*url* is now %s" *url*))
|
||||
;; outputs "*url* is now http://en.wikipedia.org/wiki/Margarita"
|
||||
|
||||
(binding [*url* "http://en.wikipedia.org/wiki/Cointreau"]
|
||||
(println (format "*url* is now %s" *url*)))
|
||||
;; outputs "*url* is now http://en.wikipedia.org/wiki/Cointreau"
|
||||
;; ⇒ nil
|
||||
</code></pre><p>Note that, by convention, vars which are supposed to or may be dynamically scoped are named with leading
|
||||
and trailing asterisks <code>*</code> (often referred to as "earmuffs").</p><p>In the example above, <code>binding</code> temporarily changed the var's current value to a different URL. But that happened only
|
||||
in the same thread as the var was originally defined in. What makes vars interesting from the concurrency
|
||||
point of view is that their bindings can be <em>thread-local</em> (yes, if you are familiar with thread-local variables
|
||||
in Java or Ruby, it is very similar and serves largely the same purpose). To demonstrate, let's change
|
||||
the example to spin up 3 threads and alter the var's value from them:</p><pre><code class="clojure">(def ^:dynamic *url* "http://en.wikipedia.org/wiki/Margarita")
|
||||
;; ⇒ #'user/*url*
|
||||
(println (format "*url* is now %s" *url*))
|
||||
;; outputs "*url* is now http://en.wikipedia.org/wiki/Margarita"
|
||||
;; ⇒ nil
|
||||
(.start (Thread. (fn []
|
||||
(binding [*url* "http://en.wikipedia.org/wiki/Cointreau"]
|
||||
(println (format "*url* is now %s" *url*))))))
|
||||
;; outputs "*url* is now http://en.wikipedia.org/wiki/Cointreau"
|
||||
;; ⇒ nil
|
||||
(.start (Thread. (fn []
|
||||
(binding [*url* "http://en.wikipedia.org/wiki/Guignolet"]
|
||||
(println (format "*url* is now %s" *url*))))))
|
||||
;; outputs "*url* is now http://en.wikipedia.org/wiki/Guignolet"
|
||||
;; ⇒ nil
|
||||
(.start (Thread. (fn []
|
||||
(binding [*url* "http://en.wikipedia.org/wiki/Apéritif"]
|
||||
(println (format "*url* is now %s" *url*))))))
|
||||
;; outputs "*url* is now http://en.wikipedia.org/wiki/Apéritif"
|
||||
;; ⇒ nil
|
||||
(println (format "*url* is now %s" *url*))
|
||||
;; outputs "*url* is now http://en.wikipedia.org/wiki/Margarita"
|
||||
;; ⇒ nil
|
||||
</code></pre><p>As you can see, var scoping in different threads did not modify the var's value in the thread it was
|
||||
originally defined in (its <em>root binding</em>). In real-world cases, for example, it means that a multi-threaded
|
||||
Web crawler can store some crawling state specific to a particular thread in a var and not
|
||||
modify its initial (global) value.</p><h4 id="how-to-alter-var-root">How to Alter Var Root</h4><p>Sometimes, however, modifying the root binding is necessary. This is done via <code>clojure.core/alter-var-root</code>
|
||||
which takes a var (not its value) and a function that takes the old var value and returns a new one:</p><pre><code class="clojure">*url*
|
||||
;; ⇒ "http://en.wikipedia.org/wiki/Margarita"
|
||||
(.start (Thread. (fn []
|
||||
(alter-var-root (var user/*url*) (fn [_] "http://en.wikipedia.org/wiki/Apéritif"))
|
||||
(println (format "*url* is now %s" *url*)))))
|
||||
;; outputs "*url* is now http://en.wikipedia.org/wiki/Apéritif"
|
||||
;; ⇒ nil
|
||||
*url*
|
||||
;; ⇒ "http://en.wikipedia.org/wiki/Apéritif"
|
||||
</code></pre><p><code>clojure.core/var</code> is used to locate the var (<code>user/*url*</code> in our example executed in the REPL). Note that it
|
||||
finds the var itself (the reference, the "box"), not its value (what the var evalutes to).</p><p>In the example above the function we use to alter var root ignores the current value and simply returns a
|
||||
predefined string:</p><pre><code class="clojure">(fn [_] "http://en.wikipedia.org/wiki/Apéritif")
|
||||
</code></pre><p>Such functions are common enough for <code>clojure.core</code> to provide a convenience higher-order function called
|
||||
<code>clojure.core/constantly</code>. It takes a value and returns a function that, when executed, ignores all its parameters
|
||||
and returns that value. So, the function above would be more idiomatically written as</p><pre><code class="clojure">*url*
|
||||
;; ⇒ "http://en.wikipedia.org/wiki/Margarita"
|
||||
(.start (Thread. (fn []
|
||||
(alter-var-root (var user/*url*) (constantly "http://en.wikipedia.org/wiki/Apéritif"))
|
||||
(println (format "*url* is now %s" *url*)))))
|
||||
;; outputs "*url* is now http://en.wikipedia.org/wiki/Apéritif"
|
||||
;; ⇒ nil
|
||||
*url*
|
||||
;; ⇒ "http://en.wikipedia.org/wiki/Apéritif"
|
||||
</code></pre><p>When is <code>alter-var-root</code> used in real world scenarios? Some Clojure data store and API clients stores active connection
|
||||
in a var, so initial connection requires root binding modification.</p><h4 id="summary-and-use-cases-3">Summary and Use Cases</h4><p>To summarize: vars can have dynamic scope. They have a root binding and can have thread-local bindings as well.
|
||||
As such, vars are good for storing pieces of program state that vary between threads but cannot
|
||||
be stored in a function local. <code>alter-var-root</code> is used to alter root binding of a var. It is done
|
||||
the functional way: by providing a function that takes the old var value and returns a new one.</p><p>To alter var root to a specific known value, use <code>clojure.core/constantly</code>.</p><h2 id="dereferencing">Dereferencing</h2><p>Earlier sections demonstrated the concept of <em>dereferencing</em>. Dereferencing means retrieving the current
|
||||
value of a reference (an atom, an agent, a ref, etc). To dereference a Clojure reference, use
|
||||
<code>clojure.core/deref</code> or the <code>@reference</code> reader macro:</p><pre><code class="clojure">(let [xs (atom [])]
|
||||
@xs)
|
||||
;; ⇒ []
|
||||
</code></pre><p>Besides atoms, agents, and refs, Clojure has several other concurrency-oriented data structures
|
||||
that can be dereferenced: delays, futures, and promises. They will be covered later in this
|
||||
guide.</p><h3 id="dereferencing-support-for-data-types-implemented-in-java">Dereferencing Support For Data Types Implemented In Java</h3><p>It is possible to make custom data types implemented in Java support dereferencing by
|
||||
making them implement the <code>clojure.lang.</code> interface:</p><pre><code class="java">package clojure.lang;
|
||||
|
||||
public interface IDeref{
|
||||
Object deref();
|
||||
}
|
||||
</code></pre><p>This can be done to make data types implemented in Java look and feel more like built-in
|
||||
Clojure data types, or make it possible to pass said types to a function that expects
|
||||
its arguments to be dereferenceable.</p><h2 id="delays">Delays</h2><p>In Clojure, a <em>delay</em> is a data structure that is evaluated the first time it is dereferenced.
|
||||
Subsequent dereferencing will use the cached value. Delays are instantiated with the <code>clojure.core/delay</code>
|
||||
function.</p><p>In the following example a delay is used to calculate a timestamp that is later used
|
||||
as a cached value:</p><pre><code class="clojure">(def d (delay (System/currentTimeMillis)))
|
||||
;; ⇒ #'user/d
|
||||
d
|
||||
;; ⇒ #<Delay@21ed22af: :pending>
|
||||
;; dereferencing causes the value to be realized, it happens only once
|
||||
@d
|
||||
;; ⇒ 1350997814621
|
||||
@d
|
||||
;; ⇒ 1350997814621
|
||||
@d
|
||||
;; ⇒ 1350997814621
|
||||
</code></pre><p><code>clojure.core/realized?</code> can be used to check whether a delay instance has been realized
|
||||
or not:</p><pre><code class="clojure">(def d (delay (System/currentTimeMillis)))
|
||||
;; ⇒ #'user/d
|
||||
(realized? d)
|
||||
;; ⇒ false
|
||||
@d
|
||||
;; ⇒ 1350997967984
|
||||
(realized? d)
|
||||
;; ⇒ true
|
||||
</code></pre><h2 id="futures">Futures</h2><p>A Clojure future evaluates a piece of code in another thread. To instantiate a future,
|
||||
use <code>clojure.core/future</code>. The <code>future</code> function will return immediately (it never blocks
|
||||
the current thread). To obtain the result of computation, dereference the future:</p><pre><code class="clojure">(def ft (future (+ 1 2 3 4 5 6)))
|
||||
;; ⇒ #'user/ft
|
||||
ft
|
||||
;; ⇒ #<core$future_call$reify__6110@effa25e: 21>
|
||||
@ft
|
||||
;; ⇒ 21
|
||||
</code></pre><p>Dereferencing a future blocks the current thread. Because some operations may take
|
||||
a very long time or get blocked forever, futures support a timeout specified
|
||||
when you dereference them:</p><pre><code class="clojure">;; will block the current thread for 10 seconds, returns :completed
|
||||
(def ft (future (Thread/sleep 10000) :completed))
|
||||
;; ⇒ #'user/ft
|
||||
(deref ft 2000 :timed-out)
|
||||
;; ⇒ :timed-out
|
||||
</code></pre><p>Subsequent access to futures using <code>deref</code> will use the cached value, just like it
|
||||
does for delays.</p><p>Just like delays, it is possible to check whether a future is realized or not
|
||||
with <code>clojure.core/realized?</code>:</p><pre><code class="clojure">(def ft (future (reduce + (range 0 10000))))
|
||||
;; ⇒ #'user/ft
|
||||
(realized? ft)
|
||||
;; ⇒ true
|
||||
@ft
|
||||
;; ⇒ 49995000
|
||||
</code></pre><p>Clojure futures are evaluated in an unbounded, cached thread pool that is also used by agents
|
||||
(updated via <code>clojure.core/send-off</code>). This works well in many cases but may result in
|
||||
exhaustion of the heap or thrashing if too many futures are created.</p><p>Finally, Clojure futures implement <code>java.util.concurrent.Future</code> and can be used with Java APIs
|
||||
that accept them.</p><h2 id="promises">Promises</h2><p>Promises are yet another take on asynchronously realized values. They are similar to futures in
|
||||
certain ways:</p><ul><li>Can be dereferenced with a timeout</li><li>Caches the realized value</li><li>Supported by <code>clojure.core/realized?</code></li></ul><p>However, promises are realized not by evaluating a piece of code but by calling <code>clojure.core/deliver</code>
|
||||
on a promise along with a value:</p><pre><code class="clojure">;; promises have no code body (no code to evaluate)
|
||||
(def p (promise))
|
||||
;; ⇒ #'user/p
|
||||
p
|
||||
;; ⇒ #<core$promise$reify__6153@306a0a21: :pending>
|
||||
(realized? p)
|
||||
;; ⇒ false
|
||||
|
||||
;; delivering a promise makes it realized
|
||||
(deliver p {:result 42})
|
||||
;; ⇒ #<core$promise$reify__6153@306a0a21: {:result 42}>
|
||||
(realized? p)
|
||||
;; ⇒ true
|
||||
@p
|
||||
;; ⇒ {:result 42}
|
||||
</code></pre><p>Promises combine many of the benefits of callback-oriented asynchronous programming
|
||||
and the simpler blocking function calls model provided by dereferencing.</p><h2 id="watches-and-validators">Watches and Validators</h2><p><em>TBD</em></p><h2 id="using-intrinsic-locks-synchronized-in-clojure">Using Intrinsic Locks ("synchronized") in Clojure</h2><h3 id="explicit-locking">Explicit Locking</h3><p>Every object on the JVM has an <em>intrinsic lock</em> (also referred to as <em>monitor lock</em>
|
||||
or simply <em>monitor</em>). By convention, a thread that needs to modify a field of a
|
||||
mutable object has to acquire the object's intrinsic lock and then release it.
|
||||
As long as a thread owns an intrinsic lock, no other thread can acquire the same lock.</p><p>In Clojure, explicit synchronization like this is rarely necessary but may be
|
||||
needed for interoperability with Java code. When you need to execute a piece
|
||||
of code while holding an intrinsic lock of a mutable object, use
|
||||
the <code>clojure.core/locking</code> macro:</p><pre><code class="clojure">(let [l (java.util.ArrayList.)]
|
||||
(locking l
|
||||
(.add l 10))
|
||||
l)
|
||||
;; ⇒ #<ArrayList [10]>
|
||||
</code></pre><p>Note that for immutable Clojure data structures, explicit locking is effectively
|
||||
not necessary.</p><h3 id="synchronization-on-clojure-record-fields">Synchronization on Clojure Record Fields</h3><p><em>TBD</em></p><h2 id="reducers-clojure-15">Reducers (Clojure 1.5+)</h2><p><em>TBD</em></p><h2 id="javautilconcurrent">java.util.concurrent</h2><h3 id="overview-2">Overview</h3><p><code>java.util.concurrent</code> (sometimes abbreviated as <code>j.u.c.</code>) is a group of
|
||||
<em>concurrency utilities</em> in the JDK. Originally introduced in JDK 5 in 2004,
|
||||
they are developed and maintained by some of the experts in concurrency.
|
||||
<code>j.u.c.</code> is a mature library that has been heavily battle tested for
|
||||
almost a decade.</p><p>While Clojure provides a whole toolbelt of concurrency features of its own,
|
||||
in certain cases the best solution is to use an existing <code>j.u.c.</code> class
|
||||
or even build a new abstraction on top of <code>j.u.c.</code> building blocks.</p><p><code>j.u.c.</code> consists of multiple parts that cover common concurrent programming
|
||||
patterns and use cases: from thread pools (a.k.a. <em>executors</em>) to synchronization
|
||||
classes, to atomic variables, to concurrent collections, to the Fork/Join
|
||||
framework.</p><h3 id="executors-thread-pools">Executors (Thread Pools)</h3><h4 id="overview-3">Overview</h4><p>The Executor interface standardizes invocation, scheduling, execution, and control of asynchronous tasks.
|
||||
Those tasks can be executed in the calling thread, in newly created threads, or (mostly typically)
|
||||
in a thread pool. Thread pools also can have different implementations: for example,
|
||||
be fixed size or growing dynamically, using different error handling strategies and so on.</p><p>Executors are most often instantiated using static methods of the <code>java.util.concurrent.Executors</code> class. To submit an operation to the pool, use the <code>ExecutorService#submit</code> method.</p><pre><code class="clojure">(import '[java.util.concurrent Executors ExecutorService Callable])
|
||||
|
||||
(let [^ExecutorService pool (Executors/newFixedThreadPool 16)
|
||||
^Callable clbl (cast Callable (fn []
|
||||
(reduce + (range 0 10000))))]
|
||||
(.submit pool clbl))
|
||||
;; ⇒ #<FutureTask java.util.concurrent.FutureTask@19ca276f>
|
||||
</code></pre><p>In the example above, we create a new fixed size thread pool with 16 threads
|
||||
and submit a Clojure function for execution. Clojure functions <a href="../interop/index.html#clojure_functions_implement_runnable_and_callable">implement Runnable and Callable</a>
|
||||
interfaces and can be submitted for execution, however, because <code>ExecutorService#submit</code>
|
||||
is an overloaded method, to avoid reflection warnings, we cast the function
|
||||
to <code>java.util.concurrent.Callable</code>.</p><h4 id="javautilconcurrentfuture">java.util.concurrent.Future</h4><p><code>Executor#submit</code> will return an instance of <code>java.util.concurrent.Future</code>. It is much like Clojure
|
||||
futures but cannot be dereferenced. To get the result, use the <code>j.u.c.Future#get</code> method:</p><pre><code class="clojure">(import '[java.util.concurrent Executors ExecutorService Callable])
|
||||
|
||||
(let [^ExecutorService pool (Executors/newFixedThreadPool 16)
|
||||
^Callable clbl (cast Callable (fn []
|
||||
(reduce + (range 0 10000))))
|
||||
task (.submit pool clbl)]
|
||||
(.get task))
|
||||
;; ⇒ 49995000
|
||||
</code></pre><h4 id="scheduled-executors">Scheduled Executors</h4><p><em>TBD</em></p><h3 id="countdown-latches">Countdown Latches</h3><p><em>Countdown latch</em> is a thread synchronization data structure. More specifically, it handles
|
||||
on group of concurrent workflows: "block the current thread until N other threads are
|
||||
done with their work". For example, "make a POST request to N URLs and continue when all N operations
|
||||
succeeded or failed".</p><p>Countdown latches are instances of <code>java.util.concurrent.CountDownLatch</code> and instantiated with
|
||||
a positive integer:</p><pre><code class="clojure">(import java.util.concurrent.CountDownLatch)
|
||||
|
||||
(CountDownLatch. n)
|
||||
</code></pre><p>When the <code>CountDownLatch#await</code> method is executed, the calling thread blocks until the counter
|
||||
gets to 0. Invoking the <code>CountDownLatch#countDown</code> method decreases the counter by 1. Count down
|
||||
operations, of course, are supposed to be performed in other threads.</p><p>An example to demonstrate:</p><pre><code class="clojure">(let [cnt (atom [])
|
||||
n 5
|
||||
latch (java.util.concurrent.CountDownLatch. n)]
|
||||
(doseq [i (range 0 n)]
|
||||
(.start (Thread. (fn []
|
||||
(swap! cnt conj i)
|
||||
(.countDown latch)))))
|
||||
(.await latch)
|
||||
@cnt)
|
||||
;; note the ordering: starting N threads in parallel leads to
|
||||
;; non-deterministic thread interleaving
|
||||
;; ⇒ [0 1 2 4 3]
|
||||
</code></pre><p>In the example above, we start multiple threads and block the current thread until all other
|
||||
threads are done. In this example, those other threads simply add an integer to a vector
|
||||
stored in an atom. More realistic scenarios will contact external services, the file system,
|
||||
perform some computation and so on.</p><p>Because when threads are executed concurrently (or in parallel), the order of their execution is not
|
||||
guaranteed, we see 4 being added to the vector before 3 in the result.</p><p>Countdown latches are commonly used with initial value of 1 to "block and wait until this operation in
|
||||
a different thread is done".</p><h3 id="concurrent-collections">Concurrent Collections</h3><p>Most of the Java collections are mutable and were not designed for concurrency. <code>java.util.concurrent</code> includes a number of collections that
|
||||
are thread safe and can be used for passing data structures between threads.</p><h3 id="atomic-variables">Atomic Variables</h3><p>The <a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html">java.util.concurrent.atomic</a> package provides
|
||||
a number of data structures that support lock-free thread-safe programming on a single variable (identity). They support
|
||||
conditional atomic update operation (<em>compared-and-swap</em> aka <em>CAS</em>).</p><p>Some of the more popular atomic types in the <code>j.u.c.atomic</code> package are <a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html">AtomicBoolean</a>,
|
||||
<a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicLong.html">AtomicLong</a> and <a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicReference.html">AtomicReference</a>.</p><p>Atomic references are pretty well covered in Clojure with atoms but ocassionally may be used by
|
||||
other libraries. An example to demonstrate how to use an atomic long for a thread-safe counter:</p><pre><code class="clojure">(let [l (AtomicLong.)]
|
||||
(dotimes [i 50]
|
||||
(.start (Thread. (fn []
|
||||
(.incrementAndGet l)))))
|
||||
(.get l))
|
||||
;; ⇒ 49
|
||||
</code></pre><h3 id="forkjoin-framework">Fork/Join Framework</h3><p><em>TBD</em></p><h2 id="other-approaches-to-concurrency">Other Approaches to Concurrency</h2><p>There are also other approaches to concurrency that neither Clojure nor Java cover. The growing
|
||||
adoption of <em>message passing</em> concurrency (the <a href="http://en.wikipedia.org/wiki/Actor_model">Actor model</a> and <a href="http://en.wikipedia.org/wiki/Communicating_Sequential_Processes">CSP</a>)
|
||||
lead to the creation of several JVM-based frameworks for message passing. Some of the most popular ones
|
||||
include:</p><ul><li><a href="http://akka.io">Akka</a></li><li><a href="http://code.google.com/p/jetlang/">Jetlang</a></li><li><a href="http://lmax-exchange.github.com/disruptor/">LMAX Disruptor</a></li></ul><p>Akka's Java API can be used from Clojure either directly or via a library called <a href="https://github.com/gaverhae/okku">Okku</a>.</p><p>In LMAX Disruptor, event instances passed around are assumed to be mutable, so the framework is of limited use with Clojure.</p><h2 id="runtime-parallelism">Runtime Parallelism</h2><p>Clojure was designed to be a hosted language. Its primary target, the
|
||||
JVM, provides runtime parallelism support. JVM threads map 1:1 to
|
||||
kernel threads. Those will be executed in parallel given that enough
|
||||
cores are available for OS scheduler to use.</p><p>In Clojure, many concurrency features are built on top of JVM threads
|
||||
and thus benefit from runtime parallelism if the program is running on
|
||||
a multi-core machine.</p><h2 id="books">Books</h2><p>Concurrency is a broad topic and it would be silly to think that we
|
||||
can cover it well in just one guide. To get a better understanding of
|
||||
the subject, one can refer to a few excellent books:</p><ul><li><a href="http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601">Java Concurrency in Practice</a> by Brian Goetz et al. is a true classic.</li><li><a href="http://pragprog.com/book/vspcon/programming-concurrency-on-the-jvm">Programming Concurrency on the JVM</a> demonstrates a range of concurrency features in several JVM languages.</li></ul><h2 id="wrapping-up">Wrapping Up</h2><p>One of Clojure design goals was to make concurrent programming easier.</p><p>The key design decision was making Clojure data structures immutable
|
||||
(persistent) and separating the concepts of <em>identity</em> (references)
|
||||
and <em>value</em>. Immutable values eliminate many concurrency hazards, and
|
||||
ultimately make it easier for developers to reason about their
|
||||
programs.</p><p>Atoms are arguably the most commonly used reference type when working
|
||||
with concurrency (vars are used much more often but not for their
|
||||
concurrency semantics). Software Transactional Memory is a more
|
||||
specialized feature and has certain limitations (e.g., I/O operations
|
||||
must not be performed inside transactions). Finally, agents, futures,
|
||||
and promises provide an array of tools for working with asynchronous
|
||||
operations.</p><p>Concurrency is a hard fundamental problem. There is no single "best"
|
||||
solution or approach to it. On the JVM, Clojure offers several
|
||||
concurrency-related features of its own but also provides easy access
|
||||
to the <code>java.util.concurrent</code> primitives and libraries such as
|
||||
<a href="http://akka.io/">Akka</a> or
|
||||
<a href="http://code.google.com/p/jetlang/">Jetlang</a>.</p><h2 id="contributors">Contributors</h2><p>Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a>, 2012 (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../polymorphism/index.html">« Polymorphism in Clojure: Protocols and Multimethods</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../glossary/index.html">Clojure Terminology Guide »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,745 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Overview of clojure.core, the standard Clojure library</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/core_overview/" />
|
||||
<meta property="og:title" content="Overview of clojure.core, the standard Clojure library" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/core_overview/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Overview of clojure.core, the standard Clojure library</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>Key functions of <code>clojure.core</code></li><li>Key macros of <code>clojure.core</code></li><li>Key vars of <code>clojure.core</code></li><li>Essential special forms</li></ul><p>This guide is <strong>by no means comprehensive</strong> and does not try to explain each function/macro/form in depth. It is an overview,
|
||||
the goal is to briefly explain the purpose of each item and provide links to other articles with more information.</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="binding">Binding</h2><p><a id="let_desc"></a></p><h3 id="let">let</h3><pre><code class="clojure">(let [bindings*] exprs*)
|
||||
</code></pre><p><code>let</code> takes a vector of symbol value pairs followed by a variable number of expressions.</p><p><code>let</code> allows binding of locals (roughly equivalent to variables in many other languages) and defines an explicit scope for those bindings.</p><p>The body of a <code>let</code> statement also provides an implicit <code>do</code> that allows for multiple statements in the body of <code>let</code>.</p><p>A basic example:</p><pre><code class="klipse-clojure nohighlight">(let [x 1 y 2]
|
||||
(println x y))
|
||||
</code></pre><p>Let can be nested, and the scope is lexically determined. This means that a binding's value is determined by the nearest binding form for that symbol.</p><p>This example basically demonstrates the lexical scoping of the let form.</p><pre><code class="klipse-clojure nohighlight">(let [x 1]
|
||||
(println x) ; prints 1
|
||||
(let [x 2]
|
||||
(println x))) ; prints 2
|
||||
</code></pre><p>Let bindings are immutable and can be destructured.</p><p>See: Sections about destructuring in
|
||||
<a href="../../tutorials/introduction/index.html#destructuring">Introduction</a> and
|
||||
<a href="../functions/index.html#destructuring-of-function-arguments">Functions</a>
|
||||
pages.</p><p><a id="def_desc"></a></p><h3 id="def">def</h3><pre><code class="clojure">(def symbol doc-string? init?)
|
||||
</code></pre><p><code>def</code> takes a symbol and an optional init value.</p><p>If an init value is supplied, the root binding of the var is assigned to that value. Redefining a var with an init value will re-assign the root binding.</p><p>A root binding is a value that is shared across all threads.</p><p>The <code>let</code> form is the preferred method of creating local bindings. It is strongly suggested to prefer it where possible, and never use <code>def</code> within another form.</p><pre><code class="clojure">;; TBD - reference to var documentation, basic example
|
||||
;; TBD - metadata
|
||||
</code></pre><p><a id="declare_desc"></a></p><h3 id="declare">declare</h3><pre><code class="clojure">([& names])
|
||||
</code></pre><p><code>declare</code> takes a variable number of symbols.</p><p><code>declare</code> provides a simple way of creating 'forward declarations'. <code>declare</code> defs the supplied symbols with no init values. This allows for referencing of a var before it has been supplied a value.</p><p>There are much better methods of value-based dispatch or code architecture in general, but this presents a simple situation forward declarations would be necessary.</p><pre><code class="klipse-clojure nohighlight">(declare func<10 func<20)
|
||||
|
||||
;; without declare you will receive an error similar to:
|
||||
;; "Unable to resolve symbol: func10 in this context"
|
||||
|
||||
(defn func<10 [x]
|
||||
(cond
|
||||
(< x 10) (func<10 (inc x))
|
||||
(< x 20) (func<20 x)
|
||||
:else "too far!"))
|
||||
|
||||
(defn func<20 [x]
|
||||
(cond
|
||||
(< x 10) (func<10 x)
|
||||
(< x 20) "More than 10, less than 20"
|
||||
:else "too far!"))
|
||||
</code></pre><p>No matter which order you put func<10 and func<20 in, there will be a reference to a var that does not yet exist when the compiler does the initial evaluation of top-level forms.</p><p><code>declare</code> defines the var with no binding so that the the var exists when it is referenced later in the code.</p><p><a id="defn_desc"></a></p><h3 id="defn">defn</h3><pre><code class="clojure">([name doc-string? attr-map? [params*] prepost-map? body] [name doc-string? attr-map? ([params*] prepost-map? body) + attr-map?])
|
||||
</code></pre><p><code>defn</code> takes a symbol, an optional doc string, an optional meta-data
|
||||
map, a vector of arguments and a variable number of expressions.</p><p><code>defn</code> is the primary way of defining functions. It allows for
|
||||
convenient definition of metadata about its argslist and documentation
|
||||
(docstrings). <code>defn</code> inherently allows for quick documentation of
|
||||
functions that can be retrieved with <code>doc</code>. This feature should be
|
||||
used almost universally.</p><p>Without <code>defn</code>, a var would be directly bound to a function definition
|
||||
and explicit metadata about the doc string and argslits would be added
|
||||
manually.</p><pre><code class="clojure">(def func (fn [x] x))
|
||||
|
||||
;; same as:
|
||||
(defn func [x]
|
||||
x)
|
||||
|
||||
;; with metadata added by defn
|
||||
(def ^{:doc "documentation!"} ^{:arglists '([x])} func (fn [x] x))
|
||||
|
||||
;;same as
|
||||
(defn func
|
||||
"documentation!"
|
||||
[x]
|
||||
x)
|
||||
</code></pre><pre><code class="clojure">;; TBD - link to doc and metadata
|
||||
</code></pre><h2 id="branching">Branching</h2><p><a id="if_desc"></a></p><h3 id="if">if</h3><pre><code class="clojure">(if test then else?)
|
||||
</code></pre><p><code>if</code> takes 2 expressions, and an optional third.</p><p><code>if</code> is the primary method of conditional execution and other conditionals are built upon <code>if</code>.</p><p>If the return value of the first expression is anything except nil or false, the second expression is evaluated and the result returned..</p><p>If a third expression is provided, when the first expression returns nil or false the third expression is evaluated and returned.</p><pre><code class="klipse-clojure nohighlight">(if 0 "second") ; 0 is a 'true' value. Only false or nil are 'false'
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(if nil "second" "third")
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(if (< 10 9) "second" "third") ; (< 9 10) returns false
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(if (seq '()) "second") ; seq returns nil for an empty sequence
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(if (nil? (= 1 2)) "second" "third") ; differentiate between nil and false if needed
|
||||
</code></pre><p><a id="when_desc"></a></p><h3 id="when">when</h3><pre><code class="clojure">([test & body])
|
||||
</code></pre><p><code>when</code> takes 2 expressions.</p><p><code>when</code> provides an implicit do form that is evaluated if an expression returns true, otherwise nil is returned. <code>when</code> does not provide an 'else'.</p><pre><code class="klipse-clojure nohighlight">(when (= 1 2) (print "hey") 10)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(when (< 10 11) (print "hey") 10)
|
||||
</code></pre><h3 id="for">for</h3><p>See: <a href="index.html#for_desc">for</a></p><h3 id="doseq">doseq</h3><p>See: <a href="index.html#doseq_desc">doseq</a></p><h2 id="looping">Looping</h2><p><a id="recur_desc"></a></p><h3 id="recur">recur</h3><pre><code class="clojure">(recur exprs*)
|
||||
</code></pre><p><code>recur</code> allows for self-recursion without consuming stack space proportional to the number of recursive calls made. Due to the lack of tail-call optimization on the JVM currently, this is the only method of recursion that does not consume excess stack space.</p><p><code>recur</code> takes a number of arguments identical to the point of recursion. <code>recur</code> will evaluate those arguments, rebind them at the point of recursion and resume execution at that point.</p><p>The point of recursion is the nearest <code>fn</code> or <code>loop</code> form determined lexically.</p><p><code>recur</code> must be in the tail position of the recursion point expression. The tail position is the point in the expression where a return value would otherwise be determined and.</p><p><code>recur</code> does not bind <code>&</code> in variadic functions and in these situations an empty seq must be passed by <code>recur</code>.</p><pre><code class="klipse-clojure nohighlight">(defn count-up
|
||||
[result x y]
|
||||
(if (= x y)
|
||||
result
|
||||
(recur (conj result x) (inc x) y)))
|
||||
|
||||
(count-up [] 0 10)
|
||||
</code></pre><p>Example: getting factorial of a positive integer:</p><pre><code class="klipse-clojure nohighlight">(defn factorial
|
||||
([n]
|
||||
(factorial n 1))
|
||||
([n acc]
|
||||
(if (zero? n)
|
||||
acc
|
||||
(recur (dec n) (* n acc)))))
|
||||
|
||||
(factorial 10)
|
||||
;; ⇒ 3628800
|
||||
</code></pre><p>TBD: more examples</p><p><a id="loop_desc"></a></p><h3 id="loop">loop</h3><pre><code class="clojure">(loop [bindings*] exprs*)
|
||||
</code></pre><p><code>loop</code> takes a vector of symbol value pairs followed by a variable number of expressions.</p><p><code>loop</code> establishes a recursion point for a <code>recur</code> expression inside its body. <code>loop</code> provides an implicit <code>let</code> for bindings.</p><p>The implicit <code>let</code> that <code>loop</code> provides binds each symbol to the init-expression. <code>recur</code> then binds new values when returning the execution point to <code>loop</code>.</p><pre><code class="klipse-clojure nohighlight">(defn count-up
|
||||
[start total]
|
||||
(loop [result []
|
||||
x start
|
||||
y total]
|
||||
(if (= x y)
|
||||
result
|
||||
(recur (conj result x) (inc x) y))))
|
||||
|
||||
(count-up 0 10)
|
||||
;; ⇒ [0 1 2 3 4 5 6 7 8 9]
|
||||
</code></pre><p>Example: getting factorial of a positive integer:</p><pre><code class="klipse-clojure nohighlight">(defn factorial
|
||||
[n]
|
||||
(loop [n n
|
||||
acc 1]
|
||||
(if (zero? n)
|
||||
acc
|
||||
(recur (dec n) (* acc n)))))
|
||||
|
||||
(factorial 10)
|
||||
;; ⇒ 3628800
|
||||
</code></pre><p>TBD: more examples</p><p><a id="trampoline_desc"></a></p><h3 id="trampoline">trampoline</h3><pre><code class="clojure">([f])
|
||||
([f & args])
|
||||
</code></pre><p><code>trampoline</code> takes a function and a variable number of arguments to pass to that function.</p><p><code>trampoline</code> allows for mutual recursion without consuming stack space proportional to the number of recursive calls made.</p><p>If the return value of that function is a function, <code>trampoline</code> calls that function with no arguments. If the return value is not a function, <code>trampoline</code> simply returns that value.</p><p>Since <code>trampoline</code> calls the returned functions with no arguments, you must supply an anonymous function that takes no arguments and calls the function you wish to recur to. This is usually done with anonymous function literals <code>#()</code></p><pre><code class="klipse-clojure nohighlight">(declare count-up1 count-up2) ;; see `declare` for why this is needed
|
||||
|
||||
(defn count-up1
|
||||
[result start total]
|
||||
(if (= start total)
|
||||
result
|
||||
#(count-up2 (conj result start) (inc start) total))) ;; returns an anonymous function
|
||||
|
||||
(defn count-up2 [result start total]
|
||||
(if (= start total)
|
||||
result
|
||||
#(count-up1 (conj result start) (inc start) total))) ;; returns an anonymous function
|
||||
|
||||
#_(trampoline count-up1 [] 0 10)
|
||||
;; ⇒ [0 1 2 3 4 5 6 7 8 9]
|
||||
</code></pre><p>TBD: a trivial example that would not be easily solved with self-recursion</p><p><a id="for_desc"></a></p><h3 id="for-1">for</h3><pre><code class="clojure">([seq-exprs body-expr])
|
||||
</code></pre><p><code>for</code> takes a vector of pairs of [binding collection].</p><p><code>for</code> allows for list comprehensions. <code>for</code> assigns each sequential value in the collection to the binding form and evaluates them rightmost first. The results are returned in a lazy sequence.</p><p><code>for</code> allows for explicit let, when and while through use of ":let []" ":when (expression)" ":while (expression)" in the binding vector.</p><pre><code class="klipse-clojure nohighlight">(for [x [1 2 3] y [4 5 6]]
|
||||
[x y])
|
||||
;; ⇒ ([1 4] [1 5] [1 6] [2 4] [2 5] [2 6] [3 4] [3 5] [3 6])
|
||||
</code></pre><p>:when only evaluates the body when a true value is returned by the expression provided</p><pre><code class="klipse-clojure nohighlight">(for [x [1 2 3] y [4 5 6]
|
||||
:when (and
|
||||
(even? x)
|
||||
(odd? y))]
|
||||
[x y])
|
||||
;; ⇒ ([2 5])
|
||||
</code></pre><p>:while evaluates the body until a non-true value is reached. Note that the rightmost collection is fully bound to y before a non-true value of (< x 2) is reached. This demonstrates the order of the comprehension.</p><pre><code class="klipse-clojure nohighlight">(for [x [1 2 3] y [4 5 6]
|
||||
:while (< x 2)]
|
||||
[x y])
|
||||
;; ⇒ ([1 4] [1 5] [1 6])
|
||||
</code></pre><p><a id="doseq_desc"></a></p><h3 id="doseq-1">doseq</h3><pre><code class="clojure">([seq-exprs & body])
|
||||
</code></pre><p><code>doseq</code> takes a vector of pairs of [binding collection].</p><p><code>doseq</code> is similar to <code>for</code> except it does not return a sequence of results. <code>doseq</code> is generally intended for execution of side-effects in the body, and thusly returns nil.</p><p><code>doseq</code> supports the same bindings as for - :let :when :while. For examples of these, see for.</p><pre><code class="klipse-clojure nohighlight">(doseq [x [1 2 3] y [4 5 6]]
|
||||
(println [x y]))
|
||||
|
||||
;; [1 4][1 5][1 6][2 4][2 5][2 6][3 4][3 5][3 6]
|
||||
;; ⇒ nil
|
||||
</code></pre><p><a id="iterate_desc"></a></p><h3 id="iterate">iterate</h3><pre><code class="clojure">([f x])
|
||||
</code></pre><p><code>iterate</code> takes a function and an argument to the function.</p><p>A lazy sequence is returned consisting of the argument then each subsequent entry is the function evaluated with the previous entry in the lazy sequence.</p><pre><code class="clojure">TBD: Examples
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><p><a id="reduce_desc"></a></p><h3 id="reduce">reduce</h3><pre><code class="clojure">([f coll])
|
||||
([f val coll])
|
||||
</code></pre><p><code>reduce</code> takes a function, an optional initial value and a collection.</p><p><code>reduce</code> takes the first item of the collection and either the second
|
||||
item of the collection or the provided initial value, then evaluates
|
||||
the function with those arguments. The function is then evaluated with
|
||||
that result and the next item in the collection. This is repeated
|
||||
until the collection is exhausted and the value of the final function
|
||||
call is returned.</p><pre><code class="clojure">TBD: examples
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><p><a id="reductions_desc"></a></p><h3 id="reductions">reductions</h3><pre><code class="clojure">([f coll])
|
||||
([f val coll])
|
||||
</code></pre><p><code>reductions</code> takes a function, an optional initial value and a collection.</p><p><code>reductions</code> returns a lazy sequence consisting of the first item in
|
||||
the collection, or the provided initial value followed by the result
|
||||
of the function evaluated with the previous result and the next item
|
||||
in the collection.</p><pre><code class="clojure">TBD: examples
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><p><a id="map_desc"></a></p><h3 id="map">map</h3><pre><code class="clojure">([f coll])
|
||||
([f c1 c2])
|
||||
([f c1 c2 c3])
|
||||
([f c1 c2 c3 & colls])
|
||||
</code></pre><p><code>map</code> takes a function and one or more collections. <code>map</code> passes an
|
||||
item from each collection, in order, to the function and returns a
|
||||
lazy sequence of the results.</p><p>The function provided to <code>map</code> must support an arity matching the
|
||||
number of collections passed. Due to this, when using more than one
|
||||
collection, map stops processing items when any collection runs out of
|
||||
items.</p><pre><code class="clojure">TBD: Examples
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><h2 id="collection-and-sequence-modification">Collection and Sequence Modification</h2><p><a id="conj_desc"></a></p><h3 id="conj">conj</h3><pre><code class="clojure">([coll x])
|
||||
([coll x & xs])
|
||||
</code></pre><p><code>conj</code> takes a collection and a variable number of arguments.</p><p><code>conj</code> is short for "conjoin". As the name implies, <code>conj</code> returns the
|
||||
collection with those arguments added.</p><p>Adding items to a collection occurs at different places depending on
|
||||
the concrete type of collection.</p><p>List addition occurs at the beginning of the list. This is because
|
||||
accessing the head of the list is a constant time operation, and
|
||||
accessing the tail requires traversal of the entire list.</p><pre><code class="clojure">(conj '(1 2) 3)
|
||||
;; ⇒ (3 1 2)
|
||||
</code></pre><p>Vectors have constant time access across the entire data
|
||||
structure. `'conj' thusly appends to the end of a vector.</p><pre><code class="klipse-clojure nohighlight">(conj [1 2] 3)
|
||||
;; ⇒ [1 2 3]
|
||||
</code></pre><p>Maps do not have guaranteed ordering, so the location that items are
|
||||
added is irrelevant. <code>conj</code> requires vectors of [key value] pairs to
|
||||
be added to the map.</p><pre><code class="klipse-clojure nohighlight">(conj {:a 1 :b 2 :c 3} [:d 4])
|
||||
;; ⇒ {:d 4, :a 1, :c 3, :b 2}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(conj {:cats 1 :dogs 2} [:ants 400] [:giraffes 13])
|
||||
;; ⇒ {:giraffes 13, :ants 400, :cats 1, :dogs 2}
|
||||
</code></pre><p>Sets also do not have guaranteed ordering. <code>conj</code> returns a set with
|
||||
the item added. As the concept of sets implies, added items will not
|
||||
duplicate equivalent items if they are present in the set.</p><pre><code class="klipse-clojure nohighlight">(conj #{1 4} 5)
|
||||
;; ⇒ #{1 4 5}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(conj #{:a :b :c} :b :c :d :e)
|
||||
;; ⇒ #{:a :c :b :d :e}
|
||||
</code></pre><p><a id="empty_desc"></a></p><h3 id="empty">empty</h3><pre><code class="clojure">([coll])
|
||||
</code></pre><p><code>empty</code> takes a collection</p><p><code>empty</code> returns an empty collection of the same type as the collection
|
||||
provided.</p><pre><code class="klipse-clojure nohighlight">(empty [1 2 3])
|
||||
;; ⇒ []
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(empty {:a 1 :b 2 :c 3})
|
||||
;; ⇒ {}
|
||||
</code></pre><p><a id="assoc_desc"></a></p><h3 id="assoc">assoc</h3><pre><code class="clojure">([map key val])
|
||||
([map key val & kvs])
|
||||
</code></pre><p><code>assoc</code> takes a key and a value and returns a collection of the same
|
||||
type as the supplied collection with the key mapped to the new value.</p><p><code>assoc</code> is similar to get in how it works with maps, records or
|
||||
vectors. When applied to a map or record, the same type is returned
|
||||
with the key/value pairs added or modified. When applied to a vector,
|
||||
a vector is returned with the key acting as an index and the index
|
||||
being replaced by the value.</p><p>Since maps and records can not contain multiple equivalent keys,
|
||||
supplying <code>assoc</code> with a key/value that exists in the one will cause
|
||||
<code>assoc</code> to return modify the key at that value in the result and not
|
||||
duplicate the key.</p><pre><code class="klipse-clojure nohighlight">(assoc {:a 1} :b 2)
|
||||
;; ⇒ {:b 2, :a 1}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(assoc {:a 1 :b 45 :c 3} :b 2)
|
||||
;; ⇒ {:a 1, :c 3, :b 2}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defrecord Hand [index middle ring pinky thumb])
|
||||
(assoc (Hand. 3 4 3.5 2 2) :index 3.75)
|
||||
;; ⇒ #user.Hand{:index 3.75, :middle 4, :ring 3.5, :pinky 2, :thumb 2}
|
||||
</code></pre><p>When using <code>assoc</code> with a vector, the key is
|
||||
the index and the value is the value to assign to that index in the
|
||||
returned vector. The key must be <= (count vector) or a
|
||||
"IndexOutOfBoundsException" will occur. <code>assoc</code> can not be used to add
|
||||
an item to a vector.</p><pre><code class="klipse-clojure nohighlight">(assoc [1 2 76] 2 3) ;= [1 2 3]
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; index 5 does not exist. valid indexes for this vector are: 0, 1, 2
|
||||
(assoc [1 2 3] 5 6)
|
||||
;; IndexOutOfBoundsException clojure.lang.PersistentVector.assocN (PersistentVector.java:136)
|
||||
</code></pre><p><a id="dissoc_desc"></a></p><h3 id="dissoc">dissoc</h3><pre><code class="clojure">([map])
|
||||
([map key])
|
||||
([map key & ks])
|
||||
</code></pre><p><code>dissoc</code> takes a map and a variable number of keys.</p><p><code>dissoc</code> returns a map with the supplied keys, and subsequently their
|
||||
values, removed. Unlike <code>assoc</code>, <code>dissoc</code> does not work on
|
||||
vectors. When a record is provided, <code>dissoc</code> returns a map. For
|
||||
similar functionality with vectors, see <code>subvec</code> and <code>concat</code>.</p><pre><code class="klipse-clojure nohighlight">(dissoc {:a 1 :b 2 :c 3} :b)
|
||||
;; ⇒ {:a 1, :c 3}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(dissoc {:a 1 :b 14 :c 390 :d 75 :e 2 :f 51} :b :c :e)
|
||||
;; ⇒ {:a 1, :f 51, :d 75}
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; note that a map is returned, not a record.
|
||||
(defrecord Hand [index middle ring pinky thumb])
|
||||
;; always be careful with the bandsaw!
|
||||
(dissoc (Hand. 3 4 3.5 2 2) :ring)
|
||||
;; ⇒ {:index 3, :middle 4, :pinky 2, :thumb 2}
|
||||
</code></pre><h2 id="information-about-a-collection-or-sequence">Information about a Collection or Sequence</h2><p><a id="count_desc"></a></p><h3 id="count">count</h3><pre><code class="clojure">([coll])
|
||||
</code></pre><p><code>count</code> takes a collection.</p><p>Returns a count of the number of items in a collection. An argument of nil returns 0.</p><pre><code class="klipse-clojure nohighlight">(count "Hello")
|
||||
;; ⇒ 5
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(count [1 2 3 4 5 6 7])
|
||||
;; ⇒ 7
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(count nil)
|
||||
;; ⇒ 0
|
||||
</code></pre><p>Note that count does not return in constant time for all
|
||||
collections. This can be determined with <code>counted?</code>. Keep in mind
|
||||
that lazy sequences must be realized to get a count of the items. This
|
||||
is often not intended and can cause a variety of otherwise cryptic
|
||||
errors.</p><pre><code class="klipse-clojure nohighlight">(counted? "Hello")
|
||||
;; ⇒ false
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; will be fully realized when using (count (range 10))
|
||||
(counted? (range 10))
|
||||
;; ⇒ false
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; Constant time return of (count)
|
||||
(counted? [1 2 3 4 5])
|
||||
;; ⇒ true
|
||||
</code></pre><p><a id="empty?_desc"></a></p><h3 id="empty-1">empty?</h3><pre><code class="clojure">([coll])
|
||||
</code></pre><p><code>empty</code> takes a collection.</p><p><code>empty?</code> returns true if the collection has no items, or false if it has 1 or more items.</p><pre><code class="klipse-clojure nohighlight">(empty? [])
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(empty? '(1 2 3))
|
||||
;; ⇒ false
|
||||
</code></pre><p>Do not confuse <code>empty?</code> with <code>empty</code>. This can be a source of great confusion:</p><pre><code class="klipse-clojure nohighlight">(if (empty [1 2 3]) ;; empty returns an empty seq, which is true! use empty? here.
|
||||
"It's empty"
|
||||
"It's not empty")
|
||||
;; ⇒ "It's empty"
|
||||
</code></pre><p><a id="not-empty_desc"></a></p><h3 id="not-empty">not-empty</h3><pre><code class="clojure">([coll])
|
||||
</code></pre><p><code>not-empty</code> takes a collection.</p><p><code>not-empty</code> returns nil if the collection has no items. If the collection contains items, the collection is returned.</p><pre><code class="klipse-clojure nohighlight">(not-empty '(:mice :elephants :children))
|
||||
;; ⇒ (:mice :elephants :children)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(not-empty '())
|
||||
;; ⇒ nil
|
||||
</code></pre><h2 id="items-in-a-collection-or-sequence">Items in a Collection or Sequence</h2><p><a id="first_desc"></a></p><h3 id="first">first</h3><pre><code class="clojure">([coll])
|
||||
</code></pre><p><code>first</code> takes a collection.</p><p><code>first</code> returns the first item in the collection. <code>first</code> returns nil
|
||||
if the argument is empty or is nil.</p><p>Note that for collections that do not guarantee order like some maps
|
||||
and sets, the behaviour of <code>first</code> should not be relied on.</p><pre><code class="klipse-clojure nohighlight">(first (range 10))
|
||||
;; ⇒ 0
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(first [:floor :piano :seagull])
|
||||
;; ⇒ :floor
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(first [])
|
||||
;; ⇒ nil
|
||||
</code></pre><p><a id="rest_desc"></a></p><h3 id="rest">rest</h3><pre><code class="clojure">([coll])
|
||||
</code></pre><p><code>rest</code> takes a collection.</p><p><code>rest</code> returns a seq of items starting with the second element in the
|
||||
collection. <code>rest</code> returns an empty seq if the collection only
|
||||
contains a single item.</p><p><code>rest</code> should also not be relied on when using maps and sets unless
|
||||
you are sure ordering is guaranteed.</p><pre><code class="klipse-clojure nohighlight">(rest [13 1 16 -4])
|
||||
;; ⇒ (1 16 -4)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(rest '(:french-fry))
|
||||
;; ⇒ '()
|
||||
</code></pre><p>The behaviour of <code>rest</code> should be contrasted with <code>next</code>. <code>next</code>
|
||||
returns nil if the collection only has a single item. This is
|
||||
important when considering "truthiness" of values since an empty seq
|
||||
is "true" but nil is not.</p><pre><code class="klipse-clojure nohighlight">(if (rest '("stuff"))
|
||||
(print "Does this print?")) ;; yes, it prints.
|
||||
</code></pre><pre><code class="clojure">;; NEVER FINISHES EXECUTION!
|
||||
;; "done" is never reached because (rest x) is always a "true" value
|
||||
(defn inf
|
||||
[x]
|
||||
(if (rest x)
|
||||
(inf (rest x))
|
||||
"done"))
|
||||
</code></pre><p><a id="get_desc"></a></p><h3 id="get">get</h3><pre><code class="clojure">([map key])
|
||||
([map key not-found])
|
||||
</code></pre><p><code>get</code> takes an associative collection, a sequence of keys and an optional default value.</p><p><code>get</code> returns the value for the specified key in a map or record,
|
||||
index of a vector or value in a set. If the key is not present, <code>get</code>
|
||||
returns nil or a supplied default value.</p><pre><code class="klipse-clojure nohighlight">;; val of a key in a map
|
||||
(get {:a 1 :b 2 :c 3} :b)
|
||||
;; ⇒ 2
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; index of a vector
|
||||
(get [10 15 20 25] 2)
|
||||
;; ⇒ 20
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; in a set, returns the value itself if present
|
||||
(get #{1 10 100 2 20 200} 1)
|
||||
;; ⇒ 1
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; returns nil if key is not present
|
||||
(get {:a 1 :b 2} :c)
|
||||
;; ⇒ nil
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; vector does not have an _index_ of 4. nil is returned
|
||||
(get [1 2 3 4] 4)
|
||||
;; ⇒ nil
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defrecord Hand [index middle ring pinky thumb])
|
||||
(get (Hand. 3 4 3.5 2 2) :index)
|
||||
;; ⇒ 3
|
||||
</code></pre><p><code>get</code> also supports a default return value supplied as the last argument.</p><pre><code class="klipse-clojure nohighlight">;; index 4 does not exist. return default value
|
||||
(get [1 2 3 4] 4 "Not Found")
|
||||
;; ⇒ "Not Found"
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; key :c does not exist, so return default value of 3
|
||||
(get {:a 1 :b 2} :c 3)
|
||||
;; ⇒ 3
|
||||
</code></pre><p><a id="contains?_desc"></a></p><h3 id="contains">contains?</h3><pre><code class="clojure">([coll key])
|
||||
</code></pre><p><code>contains?</code> takes a map and a key.</p><p><code>contains</code> returns true if the provided <em>key</em> is present in a
|
||||
collection. <code>contains</code> is similar to <code>get</code> in that vectors treat the
|
||||
key as an index. <code>contains</code> will always return false for lists.</p><pre><code class="klipse-clojure nohighlight">(contains? {:a 1 :b 2 :c 3} :c)
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; true if index 2 exists
|
||||
(contains? ["John" "Mary" "Paul"] 2)
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; false if index 5 does not exist
|
||||
(contains? ["John" "Mary" "Paul"] 5)
|
||||
;; ⇒ false
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; "Paul" does not exist as an index
|
||||
(contains? ["John" "Mary" "Paul"] "Paul")
|
||||
;; ⇒ false
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; lists are not supported. contains? won't traverse a collection for a result.
|
||||
(contains? '(1 2 3) 0)
|
||||
;; ⇒ java.lang.IllegalArgumentException: contains? not supported on type: clojure.lang.PersistentList
|
||||
</code></pre><p><a id="keys_desc"></a></p><h3 id="keys">keys</h3><pre><code class="clojure">([map])
|
||||
</code></pre><p><code>keys</code> takes a map or record.</p><p><code>keys</code> returns a sequence of the keys in a map or record.</p><pre><code class="klipse-clojure nohighlight">(keys {1 "one" 2 "two" 3 "three"})
|
||||
;; ⇒ (1 2 3)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defrecord Hand [index middle ring pinky thumb])
|
||||
(keys (Hand. 2 4 3 1 2))
|
||||
;; ⇒ (:index :middle :ring :pinky :thumb)
|
||||
</code></pre><p><a id="vals_desc"></a></p><h3 id="vals">vals</h3><pre><code class="clojure">([map])
|
||||
</code></pre><p><code>vals</code> takes a map or record.</p><p><code>vals</code> returns a sequence of vals in a map or record.</p><pre><code class="klipse-clojure nohighlight">(vals {:meows 20 :barks 2 :moos 5})
|
||||
;; ⇒ (5 2 20)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defrecord Hand [index middle ring pinky thumb])
|
||||
(vals (Hand. 1 2 3 4 5))
|
||||
;; ⇒ (1 2 3 4 5)
|
||||
</code></pre><p><a id="take_desc"></a></p><h3 id="take">take</h3><pre><code class="clojure">([n coll])
|
||||
</code></pre><p><code>take</code> takes a number and a collection.</p><p><code>take</code> returns a lazy sequence starting with the first value of the
|
||||
collection and n sequential items after that.</p><p>If the number of items in the collection is less than the provided
|
||||
number, the entire collection is returned lazily.</p><pre><code class="clojure">TBD: example
|
||||
</code></pre><p><a id="drop_desc"></a></p><h3 id="drop">drop</h3><pre><code class="clojure">([n coll])
|
||||
</code></pre><p><code>drop</code> takes a number and a collection.</p><p><code>drop</code> returns a lazy sequence starting at the nth item of the collection.</p><pre><code class="clojure">TBD: example
|
||||
</code></pre><p><a id="take-while_desc"></a></p><h3 id="take-while">take-while</h3><pre><code class="clojure">([pred coll])
|
||||
</code></pre><p><code>take-while</code> takes a function that accepts a single-argument and a
|
||||
collection.</p><p><code>take-while</code> returns a lazy sequence of sequential items until the
|
||||
function returns nil/false value for that item.</p><pre><code class="clojure">TBD: example
|
||||
</code></pre><p><a id="drop-while_desc"></a></p><h3 id="drop-while">drop-while</h3><pre><code class="clojure">([pred coll])
|
||||
</code></pre><p>'drop-while` takes a function that accepts a single-argument and a
|
||||
collection.</p><p><code>drop-while</code> returns a lazy sequence starting at the first item in the
|
||||
collection that the function returns nil/false.</p><p><a id="filter_desc"></a></p><h3 id="filter">filter</h3><pre><code class="clojure">([pred coll])
|
||||
</code></pre><p><code>filter</code> takes a function that accepts a single argument and a
|
||||
collection.</p><p><code>filters</code> returns a lazy sequence of items that return <code>true</code> for the
|
||||
provided predicate. Contrast to <code>remove</code>.</p><pre><code class="klipse-clojure nohighlight">(filter even? (range 10))
|
||||
;; ⇒ (0 2 4 6 8)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(filter #(if (< (count %) 5) %) ["Paul" "Celery" "Computer" "Rudd" "Tayne"])
|
||||
;; ⇒ ("Paul" "Rudd")
|
||||
</code></pre><p>When using sets with <code>filter</code>, remember that if nil or false is in the
|
||||
set and in the collection, then the predicate will return itself:
|
||||
<code>nil</code>.</p><p>In this example, when nil and false are tested with the predicate, the
|
||||
predicate returns nil. This is because if the item is present in the
|
||||
set it is returned. This will cause that item to /not/ be included in
|
||||
the returned lazy-sequence.</p><pre><code class="klipse-clojure nohighlight">(filter #{:nothing :something nil} [:nothing :something :things :someone nil false :pigeons])
|
||||
;; ⇒ (:nothing :something)
|
||||
</code></pre><p><a id="keep_desc"></a></p><h3 id="keep">keep</h3><p><code>(keep f coll)</code></p><p><code>keep</code> takes a function that accepts a single argument and a
|
||||
collection.</p><p><code>keep</code> returns a lazy sequence of non-nil results of the function
|
||||
applied to each item in the collection in sequence.</p><pre><code class="clojure">TBD: examples
|
||||
</code></pre><p><a id="remove_desc"></a></p><h3 id="remove">remove</h3><pre><code class="clojure">([pred coll])
|
||||
</code></pre><p><code>remove</code> takes a function that accepts a single argument and a
|
||||
collection.</p><p><code>remove</code> returns a lazy sequence of items that return <code>false</code> or <code>nil</code>
|
||||
for the provided predicate. Contrast to <code>filter</code>.</p><pre><code class="klipse-clojure nohighlight">(remove even? (range 10))
|
||||
;; ⇒ (1 3 5 7 9)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; relative complement. probably useless?
|
||||
(remove {:a 1 :b 2} [:h :k :z :b :s])
|
||||
;; ⇒ (:h :k :z :s)
|
||||
</code></pre><p>When using sets with <code>remove</code>, remember that if nil or false is in the
|
||||
set and in the collection, then the predicate will return itself:
|
||||
<code>nil</code>. This will cause that item to be included in the returned lazy
|
||||
sequence.</p><p>In this example, when nil and false are tested with the predicate, the
|
||||
predicate returns nil. This is because if the item is present in the
|
||||
set it is returned.</p><pre><code class="klipse-clojure nohighlight">(remove #{:nothing :something nil} [:nothing :something :things :someone nil false :pigeons])
|
||||
;; ⇒ (:things :someone nil false :pigeons)
|
||||
</code></pre><p><a id="some_desc"></a></p><h3 id="some">some</h3><pre><code class="clojure">([pred coll])
|
||||
</code></pre><p><code>some</code> takes a function that accepts a single argument and a collection.</p><p><code>some</code> will apply a predicate to each value in a collection until a
|
||||
non-false/nil result is returned then immediately return that result.</p><p>Since collections are "true" values, this makes it possible to return
|
||||
the first result itself rather than simply <code>true</code>.</p><pre><code class="klipse-clojure nohighlight">(some even? [1 2 3 4 5])
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; predicate returns the value rather than simply true
|
||||
(some #(if (even? %) %) [1 2 3 4 5])
|
||||
;; ⇒ 2
|
||||
</code></pre><p>Since maps can be used as functions, you can use a map as a
|
||||
predicate. This will return the value of the first key in the
|
||||
collection that is also in the map.</p><pre><code class="klipse-clojure nohighlight">(some {:a 1 :b 5} [:h :k :d :b])
|
||||
;; ⇒ 5
|
||||
</code></pre><p>Sets can also be used as functions and will return the first item in
|
||||
the collection that is present in the set.</p><pre><code class="klipse-clojure nohighlight">(some #{4} (range 20))
|
||||
;; ⇒ 4
|
||||
</code></pre><p><a id="every?_desc"></a></p><h3 id="every">every?</h3><pre><code class="clojure">([pred coll])
|
||||
</code></pre><p><code>every</code> takes a function that accepts a single argument and a collection.</p><p><code>every</code> returns true if the predicate returns true for every item in
|
||||
the collection, otherwise it returns false.</p><pre><code class="klipse-clojure nohighlight">(every? even? (range 0 10 2))
|
||||
;; ⇒ true
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; set can be used to see if collection only contains items in the set.
|
||||
(every? #{2 3 4} [2 3 4 2 3 4])
|
||||
;; ⇒ true
|
||||
</code></pre><h2 id="processing-collections-and-sequences">Processing Collections and Sequences</h2><p><a id="partition_desc"></a></p><h3 id="partition">partition</h3><pre><code class="clojure">([n coll])
|
||||
([n step coll])
|
||||
([n step pad coll])
|
||||
</code></pre><p><code>partition</code> takes a number, an optional step, an optional padding
|
||||
collection and a collection. If the padding collection is provided, a
|
||||
step must be provided.</p><p><code>partition</code> sequentially takes a provided number of items from the
|
||||
collection in sequence and puts them into lists. This lazy sequence of
|
||||
lists is returned.</p><p>If a step is provided, the lists in the returned lazy sequence start
|
||||
at offsets in the provided collection of that number items in the
|
||||
list.</p><p>If a padding collection is provided, the last item in the returned
|
||||
lazy sequence will be padded with the padding collection to achieve
|
||||
the desired partitioning size.</p><p>If there is no padding collection provided and there is not enough
|
||||
items to fill the last list in the returned lazy sequence, those items
|
||||
will be not used.</p><pre><code class="clojure">TBD: example
|
||||
</code></pre><p><a id="partition-all_desc"></a></p><h3 id="partition-all">partition-all</h3><pre><code class="clojure">([n coll])
|
||||
([n step coll])
|
||||
</code></pre><p><code>partition-all</code> takes a number, an optional step and a collection.</p><p><code>partition-all</code> sequentially takes a provided number of items from the
|
||||
collection in sequence and puts them into lists. This lazy sequence of
|
||||
lists is returned.</p><p>If a step is provided, the lists in the returned lazy sequence start
|
||||
at offsets in the provided collection of that number items in the
|
||||
list.</p><p>If there are not enough items to fill the last list in the returned
|
||||
lazy sequence, the remaining items will be used in the last list.</p><pre><code class="clojure">TBD: example
|
||||
</code></pre><h3 id="filter-1">filter</h3><p>See: <a href="https://clojure-doc.org/articles/language/core_overview/filter_desc">filter</a></p><h3 id="remove-1">remove</h3><p>See: <a href="https://clojure-doc.org/articles/language/core_overview/remove_desc">remove</a></p><h3 id="for-2">for</h3><p>See: <a href="https://clojure-doc.org/articles/language/core_overview/for_desc">for</a></p><h3 id="map-1">map</h3><p>See: <a href="https://clojure-doc.org/articles/language/core_overview/map_desc">map</a></p><h3 id="remove-2">remove</h3><p>See: <a href="https://clojure-doc.org/articles/language/core_overview/remove_desc">remove</a></p><h3 id="empty-2">empty?</h3><p>See: <a href="https://clojure-doc.org/articles/language/core_overview/empty?_desc">empty</a></p><h3 id="not-empty-1">not-empty</h3><p>See: <a href="https://clojure-doc.org/articles/language/core_overview/not-empty_desc">not-empty</a></p><h2 id="function-composition-and-application">Function Composition and Application</h2><p><a id="juxt_desc"></a></p><h3 id="juxt">juxt</h3><pre><code class="clojure">([])
|
||||
([f])
|
||||
([f g])
|
||||
([f g h])
|
||||
([f1 f2 f3 & fs])
|
||||
</code></pre><p><code>juxt</code> takes a variable number of functions.</p><p><code>juxt</code> returns a function that will return a vector consisting of the
|
||||
result of each of those functions to a provided argument.</p><pre><code class="clojure">TBD: examples
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><p><a id="comp_desc"></a></p><h3 id="comp">comp</h3><pre><code class="clojure">([])
|
||||
([f])
|
||||
([f g])
|
||||
([f g h])
|
||||
([f1 f2 f3 & fs])
|
||||
</code></pre><p><code>comp</code> takes a variable number of functions.</p><p><code>comp</code> returns a function that will return the result of applying the
|
||||
rightmost function to the provided argument, then the second rightmost
|
||||
function to the result of that etc.</p><pre><code class="clojure">TBD: examples
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><p><a id="fnil_desc"></a></p><h3 id="fnil">fnil</h3><pre><code class="clojure">([f x])
|
||||
([f x y])
|
||||
([f x y z])
|
||||
</code></pre><p><code>fnil</code> takes a function and one to three arguments.</p><p><code>fnil</code> returns a function that replaces any nil arguments with the
|
||||
provided values. <code>fnil</code> only supports supports patching 3 arguments,
|
||||
but will pass any arguments beyond that un-patched.</p><pre><code class="klipse-clojure nohighlight">(defn say-info [name location hobby]
|
||||
(println name "is from" location "and enjoys" hobby))
|
||||
|
||||
(def say-info-patched (fnil say-info "Someone" "an unknown location" "Clojure"))
|
||||
|
||||
(say-info-patched nil nil nil)
|
||||
;; ⇒ Someone is from an unknown location and enjoys Clojure
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(say-info-patched "Robert" nil "giraffe migrations")
|
||||
;; ⇒ Robert is from an unknown location and enjoys giraffe migrations
|
||||
</code></pre><p><a id="apply_desc"></a></p><h3 id="apply">apply</h3><pre><code class="clojure">([f args] [f x args] [f x y args] [f x y z args] [f a b c d & args])
|
||||
</code></pre><p><code>apply</code> takes a variable number of arguments and a collection.</p><p><code>apply</code> effectively unrolls the supplied args and a collection into a
|
||||
list of arguments to the supplied function.</p><pre><code class="klipse-clojure nohighlight">(str ["Hel" "lo"])
|
||||
;; ⇒ "[\"Hel\" \"lo\"]" ;; not what we want, str is operating on the vector
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(apply str ["Hel" "lo"]) ;; same as (str "Hel" "lo")
|
||||
;; ⇒ "Hello"
|
||||
</code></pre><p><code>apply</code> prepends any supplied arguments to the form as well.</p><pre><code class="klipse-clojure nohighlight">(map + [[1 2 3] [1 2 3]]) ;; This attempts to add 2 vectors with +
|
||||
;; ClassCastException java.lang.Class.cast (Class.java:2990)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(apply map + [[1 2 3] [1 2 3]]) ;; same as (map + [1 2 3] [1 2 3])
|
||||
;; ⇒ (2 4 6)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(apply + 1 2 3 [4 5 6]) ;; same as (+ 1 2 3 4 5 6)
|
||||
;; ⇒ 21
|
||||
</code></pre><p>Note that apply can not be used with macros.</p><p><a id="-_desc"></a></p><h3 id="-">-></h3><pre><code class="clojure">([x])
|
||||
([x form])
|
||||
([x form & more])
|
||||
</code></pre><p><code>-></code> takes a value and optionally one or more expressions.</p><p><code>-></code> takes the first argument and inserts it as the second item in the
|
||||
next form, or creates a list with the first argument as the second
|
||||
item. The return value of that expression is inserted as the second
|
||||
item in the next form, making a list if necessary. This continues
|
||||
until all expressions are evaluated and the final value is returned.</p><pre><code class="clojure">TBD: example
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><p><a id="-_desc"></a></p><h3 id="--1">->></h3><pre><code class="clojure">([x])
|
||||
([x form])
|
||||
([x form & more])
|
||||
</code></pre><p><code>->></code> takes a value and optionally one or more expressions.</p><p><code>->></code> takes the first argument and inserts it as the last item in the
|
||||
next form, or creates a list with the first argument as the last
|
||||
item. The return value of that expression is inserted as the last item
|
||||
in the next form, making a list if necessary. This continues until
|
||||
all expressions are evaluated and the final value is returned.</p><p>TBD: Simple image accompaniment.</p><h2 id="associative-collections">Associative Collections</h2><p><a id="get-in_desc"></a></p><h3 id="get-in">get-in</h3><pre><code class="clojure">([m ks] [m ks not-found])
|
||||
</code></pre><p><code>get-in</code> takes an associative collection, a sequence of keys and an optional default value.</p><p><code>get-in</code> takes the first value in the sequence of keys and retrieves
|
||||
the value, then applies each subsequent key to to the most recently
|
||||
returned value and returns the final result. If any key is not present
|
||||
when evaluated then either nil, or a provided default value is
|
||||
returned.</p><pre><code class="klipse-clojure nohighlight">(get-in {:profile {:personal {:age 28}}} [:profile :personal :age])
|
||||
;= 28
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><p><a id="update-in_desc"></a></p><h3 id="update-in">update-in</h3><pre><code class="clojure">([m [k & ks] f & args])
|
||||
</code></pre><p><code>update-in</code> takes an associative collection, a sequence of keys, a
|
||||
function and optional arguments to supply to that function.</p><p><code>update-in</code> takes the first value in the sequence of keys and
|
||||
retrieves the value, then applies each subsequent key to to the most
|
||||
recently returned value. The function and optional arguments are
|
||||
applied to the value and a new nested collection is returned with the
|
||||
key having the result of that function.</p><p><code>update-in</code> will create new hash-maps if a key in the sequence of keys
|
||||
does not exist. The returned collection will have a nested structure
|
||||
correlating to the provided sequence along with the result of the
|
||||
function and optional arguments as the value of the final key.</p><pre><code class="klipse-clojure nohighlight">(update-in {:profile {:personal {:age 28}}} [:profile :personal :age] inc)
|
||||
;= {:profile {:personal {:age 29}}}
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><p><a id="assoc-in_desc"></a></p><h3 id="assoc-in">assoc-in</h3><pre><code class="clojure">([m [k & ks] v])
|
||||
</code></pre><p><code>assoc-in</code> takes an associative collection, a sequence of keys and a value.</p><p><code>assoc-in</code> takes the first value in the sequence of keys and retrieves
|
||||
the value, then applies each subsequent key to to the most recently
|
||||
returned value. The final key is assigned the provided value and a new
|
||||
nested collection is returned.</p><p><code>update-in</code> will create new hash-maps if a key in the sequence of keys
|
||||
does not exist. The returned collection will have a nested structure
|
||||
correlating to the provided sequence along with the provided value as
|
||||
the value of the final key.</p><pre><code class="klipse-clojure nohighlight">(assoc-in {:profile {:personal {:age 28}}} [:profile :personal :location] "Vancouver, BC")
|
||||
;= {:profile {:personal {:location "Vancouver, BC", :age 28}}}
|
||||
</code></pre><p>TBD: Simple image accompaniment.</p><p><a id="select-keys_desc"></a></p><h3 id="select-keys">select-keys</h3><pre><code class="clojure">([map keyseq])
|
||||
</code></pre><p><code>select-keys</code> takes an associative collection and a sequence of keys.</p><p><code>select-keys</code> returns a map containing only the entries that have a
|
||||
key which is also present in the sequence of keys.</p><pre><code class="klipse-clojure nohighlight">(select-keys {:a 1 :b 2 :c 3} [:a :b])
|
||||
;= {:b 2, :a 1}
|
||||
</code></pre><h3 id="keys-1">keys</h3><p>See: <a href="index.html#keys_desc">keys</a></p><h3 id="vals-1">vals</h3><p>See: <a href="index.html#vals_desc">vals</a></p><h3 id="get-1">get</h3><p>See: <a href="index.html#get_desc">get</a></p><h3 id="assoc-1">assoc</h3><p>See: <a href="index.html#assoc_desc">assoc</a></p><h3 id="dissoc-1">dissoc</h3><p>See: <a href="index.html#dissoc_desc">dissoc</a></p><h2 id="namespace-functions">Namespace Functions</h2><p><a id="ns_desc"></a></p><h3 id="ns-require-use-import-refer">ns, require, use, import, refer</h3><p>Please see the <a href="../namespaces/index.html">Namespace guide</a></p><h2 id="reference-types">Reference Types</h2><p><a id="ref_desc"></a></p><h3 id="ref-atom-var-agent">ref, atom, var, agent</h3><p>Please see the <a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism Guide</a></p><p><a id="deref_desc"></a></p><h3 id="deref-swap-reset-dosync-alter-commute-binding">deref, swap!, reset!, dosync, alter, commute, binding</h3><p>Please see the <a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism Guide</a></p><h2 id="contributors">Contributors</h2><p>Robert Randolph <a href="mailto:audiolabs@gmail.com">audiolabs@gmail.com</a> (original author)
|
||||
Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a>
|
||||
Nguyễn Hà Dương <a href="mailto:cmpitg@gmail.com">cmpitg@gmail.com</a></p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../../tutorials/growing_a_dsl_with_clojure/index.html">« Growing a DSL with Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../namespaces/index.html">Clojure Namespaces and Vars »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
|
||||
<script>
|
||||
window.klipse_settings = {
|
||||
"selector" : ".klipse-clojure"
|
||||
};
|
||||
</script>
|
||||
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
|
||||
</body>
|
||||
</html>
|
398
clones/clojure-doc.org/articles/language/functions/index.html
Normal file
398
clones/clojure-doc.org/articles/language/functions/index.html
Normal file
|
@ -0,0 +1,398 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Functions in Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/functions/" />
|
||||
<meta property="og:title" content="Functions in Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/functions/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Functions in Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>How to define functions</li><li>How to invoke functions</li><li>Multi-arity functions</li><li>Variadic functions</li><li>Higher order functions</li><li>Other topics related to functions</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="overview">Overview</h2><p>Clojure is a functional programming language. Naturally, functions are very important part of Clojure.</p><h2 id="how-to-define-functions">How To Define Functions</h2><p>Functions are typically defined using the <a href="http://clojuredocs.org/clojure_core/clojure.core/defn">defn</a> macro:</p><pre><code class="clojure">(defn round
|
||||
[d precision]
|
||||
(let [factor (Math/pow 10 precision)]
|
||||
(/ (Math/floor (* d factor)) factor)))
|
||||
</code></pre><p>Functions can have doc strings (documentation strings) and it is a good idea to document functions that
|
||||
are part of the public API:</p><pre><code class="clojure">(defn round
|
||||
"Round down a double to the given precision (number of significant digits)"
|
||||
[d precision]
|
||||
(let [factor (Math/pow 10 precision)]
|
||||
(/ (Math/floor (* d factor)) factor)))
|
||||
</code></pre><p>In Clojure, function arguments may have optional type hints:</p><pre><code class="clojure">(defn round
|
||||
[^double d ^long precision]
|
||||
(let [factor (Math/pow 10 precision)]
|
||||
(/ (Math/floor (* d factor)) factor)))
|
||||
</code></pre><p>Type hints sometimes allow the compiler to avoid reflective method calls and/or produce significantly more efficient bytecode.
|
||||
However, as a rule of thumb, it is usually not necessary to use type hints. Start writing your code without them. The compiler
|
||||
is also free to ignore provided hints.</p><p>Functions can also define <em>preconditions</em> and <em>postconditions</em> that put restrictions on argument values and
|
||||
the value function returns:</p><pre><code class="clojure">(defn round
|
||||
"Round down a double to the given precision (number of significant digits)"
|
||||
[^double d ^long precision]
|
||||
{:pre [(not-nil? d) (not-nil? precision)]}
|
||||
(let [factor (Math/pow 10 precision)]
|
||||
(/ (Math/floor (* d factor)) factor)))
|
||||
</code></pre><p>In the example above, we use preconditions to check that both arguments are not nil. The <code>not-nil?</code> macro (or function) is not
|
||||
demonstrated in this example and assumed to be implemented elsewhere.</p><h2 id="anonymous-functions">Anonymous Functions</h2><p>Anonymous functions are defined using the <code>fn</code> special form:</p><pre><code class="clojure">(fn [x]
|
||||
(* 2 x))
|
||||
</code></pre><p>Anonymous functions can be assigned to locals, passed between functions (higher order functions are covered later in this document)
|
||||
and returned from functions:</p><pre><code class="klipse-clojure nohighlight">(let [f (fn [x]
|
||||
(* 2 x))]
|
||||
(map f (range 0 10)))
|
||||
</code></pre><p>There is also a reader macro for anonymous functions:</p><pre><code class="klipse-clojure nohighlight">(let [f #(* 2 %)]
|
||||
(map f (range 0 10)))
|
||||
</code></pre><p>The <code>%</code> in the example above means "the first argument". To refer to more than one argument, use <code>%1</code>, <code>%2</code> and so on:</p><pre><code class="klipse-clojure nohighlight">;; an anonymous function that takes 3 arguments and adds them together
|
||||
(let [f #(+ %1 %2 %3)]
|
||||
(f 1 2 3))
|
||||
</code></pre><p>Please <strong>use this reader macro sparingly</strong>; excessive use may lead to unreadable code.</p><h2 id="how-to-invoke-functions">How To Invoke Functions</h2><p>Functions are invoked by placing a function to the leading position (<em>the calling position</em>) of a list:</p><pre style="visibility:hidden; height:0;"><code class="klipse-clojure nohighlight">
|
||||
(import '(goog.string format))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(format "Hello, %s" "world")
|
||||
</code></pre><p>This works also if you have a function stored in a local, a var or passed as an argument:</p><pre><code class="klipse-clojure nohighlight">(let [f format]
|
||||
(f "Hello, %s" "world"))
|
||||
</code></pre><p>Alternatively, you can call a function using <a href="http://clojuredocs.org/clojure_core/clojure.core/apply">clojure.core/apply</a></p><pre><code class="klipse-clojure nohighlight">(apply format "Hello, %s" ["world"])
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(apply format "Hello, %s %s" ["Clojure" "world"])
|
||||
</code></pre><p><code>clojure.core/apply</code> is usually only necessary when calling variadic functions or having the list of arguments passed in
|
||||
as a collection.</p><h2 id="multi-arity-functions">Multi-arity Functions</h2><p>Functions in Clojure can have multiple <em>arities</em>, or sets of arguments:</p><pre><code class="clojure">(defn tax-amount
|
||||
([amount]
|
||||
(tax-amount amount 35))
|
||||
([amount rate]
|
||||
(Math/round (double (* amount (/ rate 100))))))
|
||||
</code></pre><p>In the example above, the version of the function that takes only one argument (so called <em>one-arity</em> or <em>1-arity</em> function)
|
||||
calls another version (<em>2-arity</em>) with a default parameter. This is a common use case for multiple arities: to have default
|
||||
argument values. Clojure is a hosted language and JVM (and JavaScript VMs, for that matter) does not support default argument
|
||||
values, however, it does support <em>method overloading</em> and Clojure takes advantage of this.</p><p>Arities in Clojure can only differ by the number of arguments, not types. This is because Clojure is strongly dynamically typed language and type information about
|
||||
parameters may or may not be available to the compiler.</p><p>A larger example:</p><pre><code class="clojure">(defn range
|
||||
([]
|
||||
(range 0 Double/POSITIVE_INFINITY 1))
|
||||
([end]
|
||||
(range 0 end 1))
|
||||
([start end]
|
||||
(range start end 1))
|
||||
([start end step]
|
||||
(comment Omitted for clarity)))
|
||||
</code></pre><h2 id="destructuring-of-function-arguments">Destructuring of Function Arguments</h2><p>Sometimes function arguments are data structures: vectors, sequences, maps. To access parts of such
|
||||
data structure, you may do something like this:</p><pre><code class="clojure">(defn currency-of
|
||||
[m]
|
||||
(let [currency (get m :currency)]
|
||||
currency))
|
||||
</code></pre><p>For vector arguments:</p><pre><code class="clojure">(defn currency-of
|
||||
[pair]
|
||||
(let [amount (first pair)
|
||||
currency (second pair)]
|
||||
currency))
|
||||
</code></pre><p>However, this is boilerplate code that has little to do with what the function really does. Clojure
|
||||
lets developer <strong>destructure</strong> parts of arguments, for both maps and sequences.</p><h3 id="positional-destructuring">Positional Destructuring</h3><p>Destructuring over vectors (<strong>positional destructuring</strong>) works like this: you replace the argument
|
||||
with a vector that has "placeholders" (symbols) in positions you want to bind. For example, if the
|
||||
argument is known to be a pair and you need second argument, it would look like this:</p><pre><code class="clojure">(defn currency-of
|
||||
[[amount currency]]
|
||||
currency)
|
||||
</code></pre><p>In the example above the first element in the pair is bound to <code>amount</code> and the second one is bound to
|
||||
<code>currency</code>. So far so good. However, notice that we do not use the <code>amount</code> local. In that case, we can
|
||||
ignore it by replacing it with an underscore:</p><pre><code class="clojure">(defn currency-of
|
||||
[[_ currency]]
|
||||
currency)
|
||||
</code></pre><p>Destructuring can nest (destructure deeper than one level):</p><pre><code class="clojure">(defn first-first
|
||||
[[[i _] _]]
|
||||
i)
|
||||
</code></pre><p>While this article does not cover <code>let</code> and locals, it is worth demonstrating that positional destructuring works
|
||||
exactly the same way for let bindings:</p><pre><code class="klipse-clojure nohighlight">(let [pair [10 :gbp]
|
||||
[_ currency] pair]
|
||||
currency)
|
||||
</code></pre><h3 id="map-destructuring">Map Destructuring</h3><p>Destructuring over maps and records (<strong>map destructuring</strong>) works slightly differently:</p><pre><code class="clojure">(defn currency-of
|
||||
[{currency :currency}]
|
||||
currency)
|
||||
</code></pre><p>In this case example, we want to bind the value for key <code>:currency</code> to <code>currency</code>. Keys don't have to be
|
||||
keywords:</p><pre><code class="clojure">(defn currency-of
|
||||
[{currency "currency"}]
|
||||
currency)
|
||||
</code></pre><pre><code class="clojure">(defn currency-of
|
||||
[{currency 'currency}]
|
||||
currency)
|
||||
</code></pre><p>When destructuring multiple keys at once, it is more convenient to use a slightly different syntax:</p><pre><code class="clojure">(defn currency-of
|
||||
[{:keys [currency amount]}]
|
||||
currency)
|
||||
</code></pre><p>The example above assumes that map keys will be keywords and we are interested in two values: <code>currency</code>
|
||||
and <code>amount</code>. The same can be done for strings:</p><pre><code class="clojure">(defn currency-of
|
||||
[{:strs [currency amount]}]
|
||||
currency)
|
||||
</code></pre><p>and symbols:</p><pre><code class="clojure">(defn currency-of
|
||||
[{:syms [currency amount]}]
|
||||
currency)
|
||||
</code></pre><p>In practice, keywords are very commonly used for map keys so destructuring with <code>{:keys [...]}</code> is very common
|
||||
as well.</p><p>Map destructuring also lets us specify default values for keys that may be missing:</p><pre><code class="clojure">(defn currency-of
|
||||
[{:keys [currency amount] :or {currency :gbp}}]
|
||||
currency)
|
||||
</code></pre><p>This is very commonly used for implementing functions that take "extra options" (faking named arguments support).</p><p>Just like with positional destructuring, map destructuring works exactly the same way for let bindings:</p><pre><code class="klipse-clojure nohighlight">(let [money {:currency :gbp :amount 10}
|
||||
{currency :currency} money]
|
||||
currency)
|
||||
</code></pre><h2 id="variadic-functions">Variadic Functions</h2><p>Variadic functions are functions that take varying number of arguments (some arguments are optional). Two examples
|
||||
of such function in <code>clojure.core</code> are <code>clojure.core/str</code> and <code>clojure.core/format</code>:</p><pre><code class="klipse-clojure nohighlight">(str "a" "b")
|
||||
; ⇒ "ab"
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(str "a" "b" "c")
|
||||
; ⇒ "abc"
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(format "Hello, %s" "world")
|
||||
; ⇒ "Hello, world"
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(format "Hello, %s %s" "Clojure" "world")
|
||||
; ⇒ "Hello, Clojure world"
|
||||
</code></pre><p>To define a variadic function, prefix optional arguments with an ampersand (<code>&</code>):</p><pre><code class="clojure">(defn log
|
||||
[message & args]
|
||||
(comment ...))
|
||||
</code></pre><p>In the example above, one argument is required and the rest is optional. Variadic functions
|
||||
are invoked as usual:</p><pre><code class="klipse-clojure nohighlight">(defn log
|
||||
[message & args]
|
||||
(println "args: " args))
|
||||
|
||||
(log "message from " "192.0.0.76")
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(log "message from " "192.0.0.76" "service:xyz")
|
||||
</code></pre><p>As you can see, optional arguments (<code>args</code>) are packed into a list.</p><h3 id="extra-arguments-aka-named-parameters">Extra Arguments (aka Named Parameters)</h3><p>Named parameters are achieved through the use of destructuring a variadic function.</p><p>Approaching named parameters from the standpoint of destructuring a variadic function allows for more clearly readable function invocations. This is an example of named parameters:</p><pre><code class="klipse-clojure nohighlight">(defn job-info
|
||||
[& {:keys [name job income] :or {job "unemployed" income "$0.00"}}]
|
||||
(if name
|
||||
[name job income]
|
||||
(println "No name specified")))
|
||||
</code></pre><p>Using the function looks like this:</p><pre><code class="klipse-clojure nohighlight">(job-info :name "Robert" :job "Engineer")
|
||||
;; ["Robert" "Engineer" "$0.00"]
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(job-info :job "Engineer")
|
||||
;; No name specified
|
||||
</code></pre><p>Without the use of a variadic argument list, you would have to call the function with a single map argument such as <code>{:name "Robert" :job "Engineer}</code>.</p><p>Keyword default values are assigned by use of the <code>:or</code> keyword followed by a map of keywords to their default value.
|
||||
Keywords not present and not given a default will be nil.</p><h2 id="higher-order-functions">Higher Order Functions</h2><p>Higher-order functions (<em>HOFs</em>) are functions that take other functions as arguments. HOFs
|
||||
are an important functional programming technique and are quite commonly used in Clojure. One example
|
||||
of an HOF is a function that takes a function and a collection and returns a collection of elements
|
||||
that satisfy a condition (a predicate). In Clojure, this function is called <code>clojure.core/filter</code>:</p><pre><code class="klipse-clojure nohighlight">(filter even? (range 0 10)) ; ⇒ (0 2 4 6 8)
|
||||
</code></pre><p>In the example above, <code>clojure.core/filter</code> takes <code>clojure.core/even?</code> as an argument.</p><p><code>clojure.core</code> has dozens of other higher-order functions. The most commonly used ones are covered in <a href="../core_overview/index.html">clojure.core Overview</a>.</p><h2 id="private-functions">Private Functions</h2><p>Functions in Clojure can be private to their namespace.</p><p>They are covered in more detail in the <a href="../namespaces/index.html">Namespaces</a> guide.</p><h2 id="keywords-as-functions">Keywords as Functions</h2><p>In Clojure, keywords can be used as functions. They take a map or record and look themselves up in it:</p><pre><code class="klipse-clojure nohighlight">(:age {:age 27 :name "Michael"})
|
||||
; ⇒ 27
|
||||
</code></pre><p>This is commonly used with higher order functions:</p><pre><code class="klipse-clojure nohighlight">(map :age [{:age 45 :name "Joe"}
|
||||
{:age 42 :name "Jill"}
|
||||
{:age 17 :name "Matt"}])
|
||||
;; ⇒ (45 42 17)
|
||||
</code></pre><p>and the <code>-></code> macro:</p><pre><code class="klipse-clojure nohighlight">(-> [{:age 45 :name "Joe"} {:age 42 :name "Jill"}]
|
||||
first
|
||||
:name)
|
||||
;; ⇒ "Joe"
|
||||
</code></pre><h2 id="maps-as-functions">Maps as Functions</h2><p>Clojure maps are also functions that take keys and look up values for them:</p><pre><code class="klipse-clojure nohighlight">({:age 42 :name "Joe"} :name)
|
||||
; ⇒ "Joe"
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">({:age 42 :name "Joe"} :age)
|
||||
; ⇒ 42
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">({:age 42 :name "Joe"} :unknown)
|
||||
; ⇒ nil
|
||||
</code></pre><p>Note that this is <strong>not true</strong> for Clojure records, which are almost identical to maps in other
|
||||
cases.</p><h2 id="sets-as-functions">Sets as Functions</h2><pre><code class="klipse-clojure nohighlight">(#{1 2 3} 1)
|
||||
; ⇒ 1
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(#{1 2 3} 10)
|
||||
; ⇒ nil
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(#{:us :au :ru :uk} :uk)
|
||||
; ⇒ :uk
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(#{:us :au :ru :uk} :cn)
|
||||
; ⇒ nil
|
||||
</code></pre><p>This is often used to check if a value is in a set:</p><pre><code class="clojure">(when (countries :in)
|
||||
(comment ...))
|
||||
|
||||
(if (countries :in)
|
||||
(comment Implement positive case)
|
||||
(comment Implement negative case))
|
||||
</code></pre><p>because everything but <code>false</code> and <code>nil</code> evaluates to <code>true</code> in Clojure.</p><h2 id="clojure-functions-as-comparators">Clojure Functions As Comparators</h2><p>Clojure functions implement the <a href="http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html">java.util.Comparator</a>
|
||||
interface and can be used as comparators.</p><h2 id="wrapping-up">Wrapping Up</h2><p>Functions are at the heart of Clojure. They are defined using the <code>defn</code> macro, can have multiple arities,
|
||||
be variadic and support parameter destructuring. Function arguments and return value can optionally be
|
||||
type hinted.</p><p>Functions are first class values and can be passed to other functions (called Higher Order Functions or HOFs).
|
||||
This is fundamental to functional programming techniques.</p><p>Several core data types behave like functions. When used reasonably, this can lead to more concise, readable
|
||||
code.</p><h2 id="contributors">Contributors</h2><p>Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a>, 2012 (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../collections_and_sequences/index.html">« Collections and Sequences in Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../laziness/index.html">Laziness in Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
|
||||
<script>
|
||||
window.klipse_settings = {
|
||||
"selector" : ".klipse-clojure"
|
||||
};
|
||||
</script>
|
||||
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
|
||||
</body>
|
||||
</html>
|
344
clones/clojure-doc.org/articles/language/glossary/index.html
Normal file
344
clones/clojure-doc.org/articles/language/glossary/index.html
Normal file
|
@ -0,0 +1,344 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Clojure Terminology Guide</title>
|
||||
|
||||
|
||||
<meta name="description" content="A glossary of terminology specific to Clojure. Terms
|
||||
are listed in alphabetical order.Terms">
|
||||
|
||||
<meta property="og:description" content="A glossary of terminology specific to Clojure. Terms
|
||||
are listed in alphabetical order.Terms">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/glossary/" />
|
||||
<meta property="og:title" content="Clojure Terminology Guide" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/glossary/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Clojure Terminology Guide</h2>
|
||||
</div>
|
||||
|
||||
<p>A glossary of terminology specific to Clojure. Terms
|
||||
are listed in alphabetical order.</p><h2 id="terms">Terms</h2><h3 id="arity">arity</h3><p>The number of arguments a function takes is its arity. If it's
|
||||
written to take a variable number of args, it's referred to as
|
||||
<a href="index.html#variadic">variadic</a>.</p><p>Functions can have multiple arity (for example, a function might have
|
||||
2 different bodies: one for when 2 args are passed, and another when 3
|
||||
args are passed).</p><h3 id="binding-form">binding-form</h3><p>Could mean one of two things:</p><ol><li><p>the expression you're binding to in a
|
||||
<a href="index.html#let_binding">let-binding</a>. It might be a simple name, or it
|
||||
might be a data structure used for
|
||||
<a href="index.html#destructuring">destructuring</a>.</p></li><li><p>Clojure provides the <code>binding</code> macro, used for setting the
|
||||
thread-local value of a dynamic var. The whole expression (form)
|
||||
is sometimes referred to as the "binding <a href="index.html#form">form</a>".</p></li></ol><h3 id="classpath">classpath</h3><p>The search path used by the JVM to locate classes which are not
|
||||
part of the Java standard class library. May include jar files.</p><h3 id="comparator">comparator</h3><p>A function that takes two args and compares them. Returns -1, 0, or 1
|
||||
depending whether the first arg is less than, equal to or greater than
|
||||
the second. The stock comparator that Clojure.core comes with is
|
||||
<code>compare</code>.</p><h3 id="coordinates">coordinates</h3><p>The "group-id/artifact-id version-string" identifier used in your
|
||||
project.clj to indicate a particular dependency.</p><p>See also <a href="index.html#libspec">libspec</a>.</p><h3 id="destructuring">destructuring</h3><p>The handy trick used in a <a href="index.html#let_binding">let-binding</a> to "unpack" the
|
||||
values from a data structure into the locals you're going to use. See
|
||||
also <a href="index.html#binding-form">binding-form</a> and <a href="https://clojure-doc.org/articles/language/glossary/functions.html#destructuring_of_function_arguments">the destructuring section in
|
||||
the functions
|
||||
guide</a>.</p><h3 id="dereference">dereference</h3><p>To get the value of a reference type. You can use the <code>deref</code> function
|
||||
for this, or else some syntactic sugar: <code>@some-ref-type</code>.</p><h3 id="entry">entry</h3><p>A key/value pair in a map. Try <code>(type (first {:a 1 :b 2}))</code> and see
|
||||
that it returns <code>clojure.lang.MapEntry</code>.</p><h3 id="evaluator">evaluator</h3><p><em>todo</em></p><h3 id="form">form</h3><p>A valid s-expression. For example: <code>(+ 1 1)</code> and <code>(defn foo [x] (* x x))</code>.</p><h3 id="head-retention">head retention</h3><p><a href="index.html#lazy">Lazy</a> sequences are still <a href="index.html#persistence">persistent</a>. If you
|
||||
make <em>another</em> data structure using one, the original lazy sequence
|
||||
will be kept around and not garbage-collected. If the lazy sequence in
|
||||
infinite, and grows very large, it can cause performance problems or
|
||||
even an out-of-memory error. Accidentally keeping around a lazy
|
||||
sequence like this is referred to as "head retention".</p><h3 id="homoiconicity">homoiconicity</h3><p>Where the code and the data is represented by the same structure.
|
||||
This allows the code to be treated as data, and the data to be treated
|
||||
as code. This feature of Clojure, and other Lisps, allows for
|
||||
macros in the language, since they can operate on code as a data
|
||||
structure, and to return a transformation of that structure to
|
||||
be the representation of new code.</p><h3 id="idempotent">idempotent</h3><p>An operation that when given the same inputs will produce the same
|
||||
result when called one or more times. An idempotent function may
|
||||
produce a side effect, such a updating a ref or an atom, but will
|
||||
only produce the side effect once. An idempotent function is
|
||||
different than a pure function, in that a pure function will
|
||||
produce no side effects.</p><h3 id="identity">identity</h3><p>A logical entity in your program that may change over time --- it may
|
||||
take on different states at different times, but it still means the
|
||||
same logical entity. Clojure uses <a href="index.html#reference_types">reference types</a>
|
||||
to represent identities. This is not to be confused with the <code>identity</code> function that just returns the argument given to it.</p><h3 id="implicit-do">implicit do</h3><p>The bodies of some expressions act like <code>do</code> in that you can include
|
||||
multiple expressions in them, and the expressions will be evaluated in
|
||||
the order they appear, with the resulting value of the body being the
|
||||
last expression evaluated. Forms that do this include: <code>when</code>,
|
||||
<code>when-let</code>, <code>fn</code>, <code>defn</code>, <code>let</code>, <code>loop</code>, and <code>try</code>.</p><h3 id="intern">intern</h3><p>A method of storing values or immutable data structures as a single
|
||||
copy of the item, allowing for more space-efficiency, and possibly
|
||||
time-efficiency, with the trade off of requiring more time being
|
||||
required when interning the item. When the string "clojure" is interned,
|
||||
all instances of the string "clojure" will reference the exact same
|
||||
instance, instead of having multiple string objects with the same value
|
||||
of "clojure".</p><h3 id="keyword">keyword</h3><p>A Clojure scalar data type whose literal syntax looks <code>:like</code> <code>:this</code>.
|
||||
They are like numbers and strings in that they evaluate to themselves,
|
||||
and are most often seen being used as keys in <a href="index.html#map">hash-maps</a>.</p><p>See also <a href="index.html#namespaced_keyword">namespaced keyword</a></p><p>The term is also used when talking about functions that take "keyword
|
||||
arguments", for example, something like: <code>(my-func :speed 42 :mass 2)</code>
|
||||
(as opposed to <code>(my-func {:speed 42 :mass 2})</code>).</p><h3 id="lazy">lazy</h3><p>Clojure can (and often does) create sequences for you that aren't
|
||||
fully computed. Upon casual inspection they <em>look</em> just like a regular
|
||||
list, but particular values in them are only computed the moment you
|
||||
ask for them --- not sooner.</p><p>This has the added benefit that you can easily create infinite
|
||||
sequences that don't consume infinite memory.</p><p>Many of the built-in Clojure functions return lazy sequences.</p><p>See also <a href="index.html#realize">realize</a>.</p><h3 id="let-binding">let-binding</h3><p>AKA, "binding vector", or just "bindings": in a <code>let</code> (and expressions
|
||||
that work like let, for example, <code>defn</code>, <code>loop</code>, <code>loop</code>, & <code>fn</code>), the
|
||||
vector that comes first where you specify lexical bindings.</p><p>See also <a href="index.html#binding_form">binding form</a></p><h3 id="libspec">libspec</h3><p><em>todo</em></p><h3 id="macro">macro</h3><p>A special type of function which is transforms a S-Expression read in
|
||||
and applies a transformation to the S-Expression resulting in a new
|
||||
form. This process is called macro-expansion, and is done as part
|
||||
of the Clojure reader.</p><h3 id="map">map</h3><p>Either refers to the built in <code>map</code> function, or else means "a
|
||||
hash-map object".</p><h3 id="memoization">memoization</h3><p>The ability to cache a result of a function call by given arguments,
|
||||
and return the result without having to do the calculation again.
|
||||
Memoization is a time-space trade off in that more memory is used
|
||||
to store the results of a function call to be able to return the
|
||||
value instead of having to keep spending time doing the calculation
|
||||
involved in the function.</p><h3 id="metadata">metadata</h3><p>An extra map that you can attach to a collection value (or a symbol),
|
||||
which contains data about the data you're attaching it to. Use <code>meta</code>
|
||||
to see the metadata of a given value.</p><h3 id="namespaced-keyword">namespaced keyword</h3><p>When you put two colons in front of a keyword's name --- for example
|
||||
::foo --- it is a so-called "namespaced keyword", and is expanded by
|
||||
the reader to become :current-namespace/foo.</p><h3 id="nullipotent">nullipotent</h3><p>An operation with no side effects. The result of calling the function
|
||||
one or more times is the same as if it was never called. Queries are
|
||||
typically good examples of functions that are nullipotent, as they
|
||||
do not modify the state of the object or structure they are queried
|
||||
against.</p><h3 id="persistence">persistence</h3><p>See the <a href="https://clojure-doc.org/articles/language/tutorials/introduction.html#values_immutability_and_persistence">relevant section of the
|
||||
introduction</a>.</p><h3 id="predicate">predicate</h3><p>A function taking one or more args and returning a boolean (<code>true</code> or
|
||||
<code>false</code>). Its name typically ends with a question mark. Some examples:
|
||||
<code>nil?</code>, <code>zero?</code>, <code>string?</code>.</p><h3 id="pure-function">pure function</h3><p>A function that given the same inputs will always produce the same
|
||||
result. A pure function also does not have any observable side effects
|
||||
and cannot depend on any outside state, other than that which was given
|
||||
as arguments to the function. A pure function's result also cannot change
|
||||
during the execution of the program or between executions of the program,
|
||||
as the dependency on outside state can lead to changes in the result of
|
||||
the function. Pure functions are also
|
||||
<a href="index.html#referential-transparency">referentially transparent.</a></p><h3 id="reader">reader</h3><p><em>todo</em></p><h3 id="reader-macro">reader macro</h3><p>Syntax that the Clojure reader recognizes as special syntactic sugar,
|
||||
for example, <code>#""</code>, <code>#{}</code>, quoting, etc.</p><h3 id="realize">realize</h3><p>When the next value in a <a href="index.html#lazy">lazy</a> sequence is accessed for the
|
||||
first time, and is computed so as to made available, it is said to
|
||||
have been "realized". This term is also used to refer to the status of <a href="https://clojure-doc.org/articles/language/glossary/concurrency_and_parallelism.html#promises">promises</a>, <a href="https://clojure-doc.org/articles/language/glossary/concurrency_and_parallelism.html#futures">futures</a>, and <a href="https://clojure-doc.org/articles/language/glossary/concurrency_and_parallelism.html#delays">delays</a>. That is, if a promise (for example) is realized then that means its value has been delivered and is accessible via <a href="index.html#dereference">dereferencing</a>.</p><h3 id="reference-types">reference types</h3><p>Vars, atoms, refs, and agents are all reference types. They are
|
||||
mutable in the sense that you can change to what value they refer, and
|
||||
Clojure provides thread-safe mechanisms for doing so.</p><h3 id="referential-transparency">referential transparency</h3><p>An expression that will always return the same result for the values
|
||||
given, and can be substituted for the resulting value, without
|
||||
effecting the program. The advantage of referential transparent
|
||||
expressions is that they can be memoized, and be the subject of
|
||||
various compilier optimizations.</p><h3 id="reify">reify</h3><p><em>todo</em></p><h3 id="repl">REPL</h3><p>Short for: "Read, Eval, Print, Loop". The REPL reads in text through
|
||||
the <a href="index.html#reader">reader</a> transforming it into a Clojure data structure,
|
||||
<a href="index.html#evaluator">evaluates</a> the data structure as code, prints the result
|
||||
of the evaluation, and loops back waiting to read the next input string.</p><h3 id="rest-args">rest args</h3><p>The extra args passed to a <a href="index.html#variadic">variadic</a> function, for example
|
||||
if <code>my-func</code> were defined like <code>(defn my-func [a b & more] ...)</code>, then
|
||||
called like <code>(my-func 1 2 3 4 5)</code>, then 3, 4, & 5 are the "rest args".</p><h3 id="s-expression">s-expression</h3><p>Short for Symbolic Expression. A S-Expression is a data structure able
|
||||
to represent both simple datastructes such as literals, or complex data
|
||||
structures such as nested expressions. Due to their versatile nature,
|
||||
S-Expressions are able to represent both data in Clojure, as well as
|
||||
the Clojure code itself, allowing Clojure to be a
|
||||
<a href="index.html#homoiconicity">homoiconic</a> language.</p><h3 id="state">state</h3><p>The <a href="index.html#value">value</a> that a given <a href="index.html#identity">identity</a> may have at a
|
||||
given time. When you change the state of an identity, you're changing
|
||||
to which value it refers. Clojure uses values to represent states.</p><h3 id="stm-software-transactional-memory">STM (Software Transactional Memory)</h3><p>Software Transactional Memory (STM) is a concurrency control method to
|
||||
coordinate and control access to shared storage as an alternative to
|
||||
lock-based synchronization. Clojure's STM uses multiversion concurrency
|
||||
control (MVCC) as an alternative to lock-based transactions, as well as
|
||||
ensuring changes are made atomically, consistently, and in
|
||||
isolation. It does this by taking a snapshot of the ref, making the
|
||||
changes in isolation to the snapshot, and apply the result. If the STM
|
||||
detects that another transaction has made an update to the ref, the
|
||||
current transaction will be forced to retry.</p><h3 id="symbol">symbol</h3><p>An identifier that refers to vars or local values.</p><h3 id="tagged-literals">tagged literals</h3><p>(Formerly called "reader literals".)</p><p>Some literals begin with a hash mark "#" (so-called "dispatch
|
||||
macros"); for example, <code>#{}</code> for sets and <code>#""</code> for regex
|
||||
literals. Starting with Clojure 1.4, you can create your own
|
||||
#-prefixed literal which causes the reader to parse the form
|
||||
following it using a function or macro of your own
|
||||
choosing/devising. It's in this way that you can <em>tag</em> a literal to be
|
||||
handled specially by the reader.</p><p>For more info, see <a href="http://clojure.org/reader">the "Tagged Literals" section of the reader
|
||||
doc</a>.</p><h3 id="threading-macros">threading macros</h3><p>The thread-first (<code>-></code>) and thread-last (<code>->></code>) macros. "Threading"
|
||||
refers to how they pass values to each subsequent argument in the
|
||||
macro, not concurrency.</p><h3 id="thrush">thrush</h3><p>A combinator. Not the same thing as the <a href="index.html#threading-macros">thread-first
|
||||
macro</a>. More info at
|
||||
<a href="http://blog.fogus.me/2010/09/28/thrush-in-clojure-redux/">http://blog.fogus.me/2010/09/28/thrush-in-clojure-redux/</a> if you're
|
||||
curious.</p><h3 id="transaction">transaction</h3><p><em>todo</em></p><h3 id="type-erasure">type erasure</h3><p>Java-related: Java generics allow you to specify a type for a
|
||||
collection. This way you don't have to cast every object you pull out
|
||||
of an ArrayList like in the old days. This is a courtesy of the java
|
||||
compiler. The java runtime doesn't know about generics --- the
|
||||
compiler does all the checking for you, then the type information is
|
||||
discarded at runtime. In Clojure, this discarding is referred to as
|
||||
type erasure.</p><h3 id="value">value</h3><p>An immutable object, such as the number 1, the character <code>\a</code>, the
|
||||
string "hello", or the vector <code>[1 2 3]</code>. In Clojure, all scalars and
|
||||
built-in core data structures are values.</p><h3 id="variadic">variadic</h3><p>A function that can take a variable number of arguments.
|
||||
See also <a href="index.html#rest_args">rest args</a>.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../concurrency_and_parallelism/index.html">« Concurrency and Parallelism in Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
617
clones/clojure-doc.org/articles/language/interop/index.html
Normal file
617
clones/clojure-doc.org/articles/language/interop/index.html
Normal file
|
@ -0,0 +1,617 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Clojure interoperability with Java</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/interop/" />
|
||||
<meta property="og:title" content="Clojure interoperability with Java" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/interop/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Clojure interoperability with Java</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>How to instantiate Java classes</li><li>How to invoke Java methods</li><li>How to extend Java classes with proxy</li><li>How to implement Java interfaces with reify</li><li>How to generate Java classes with gen-class</li><li>Other topics related to interop</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="overview">Overview</h2><p>Clojure was designed to be a hosted language that directly interoperates with its host platform (JVM, CLR and so on).
|
||||
Clojure code is compiled to JVM bytecode. For method calls on Java objects, Clojure compiler
|
||||
will try to emit the same bytecode <code>javac</code> would produce.</p><p>It is possible to implement interfaces, extend and generate Java classes in Clojure.</p><p>Clojure also provides convenient functions and macros that make consuming of Java libraries
|
||||
easier and often more concise than it would be in Java code.</p><h2 id="imports">Imports</h2><p>Java classes can be referenced either using their fully-qualified names (FQNs) such as
|
||||
<code>java.util.Date</code> or be <em>imported</em> in the current Clojure namespace using <code>clojure.core/import</code> and
|
||||
referenced by short names:</p><pre><code class="clojure">java.util.Date ; ⇒ java.util.Date
|
||||
</code></pre><pre><code class="clojure">(import java.util.Date)
|
||||
|
||||
Date ; ⇒ java.util.Date
|
||||
</code></pre><p><code>ns</code> macro supports imports, too:</p><pre><code class="clojure">(ns myservice.main
|
||||
(:import java.util.Date))
|
||||
</code></pre><p>More about the <code>ns</code> macro can be found in the article on <a href="../namespaces/index.html">Clojure namespaces</a>.</p><p>Dynamic (at runtime) imports are usually only used in the REPL and cases when there are multiple implementations of a particular
|
||||
protocol/service/feature and it is not possible to tell which one should be used until run time.</p><h3 id="automatic-imports-for-javalang">Automatic Imports For java.lang.*</h3><p>Classes from the <code>java.lang</code> package are automatically imported. For example, you can use <code>String</code> or <code>Math</code>
|
||||
without explicitly importing them:</p><pre><code class="clojure">(defn http-uri?
|
||||
[^String uri]
|
||||
(.startsWith (.toLowerCase uri) "http"))
|
||||
|
||||
(Math/round 0.7886)
|
||||
</code></pre><h3 id="inner-nested-classes">Inner (Nested) Classes</h3><p>In Java, classes can be nested inside other classes. They are called <em>inner classes</em> and by convention,
|
||||
separated from their outer class by a dollar sign (<code>$</code>):</p><pre><code class="clojure">(import java.util.Map$Entry)
|
||||
|
||||
Map$Entry ; ⇒ java.util.Map$Entry
|
||||
|
||||
;; this example assumes RabbitMQ Java client is on classpath
|
||||
(import com.rabbitmq.client.AMQP$BasicProperties)
|
||||
|
||||
AMQP$BasicProperties ; ⇒ com.rabbitmq.client.AMQP$BasicProperties
|
||||
</code></pre><p>Note that if you need to use both a class and one or more of its inner classes, they all need to be imported separately.
|
||||
As far as JVM is concerned, they are all separate classes, there is no "imports hierarchy".</p><h2 id="how-to-instantiate-java-classes">How to Instantiate Java Classes</h2><p>Java classes are instantiated using the <code>new</code> special form:</p><pre><code class="clojure">(new java.util.Date) ; ⇒ #inst "2012-10-09T21:23:57.278-00:00"
|
||||
</code></pre><p>However, the Clojure reader provides a bit of syntactic sugar and you are much more likely
|
||||
to see this:</p><pre><code class="clojure">(java.util.Date.) ; ⇒ #inst "2012-10-09T21:24:43.878-00:00"
|
||||
</code></pre><p>It is possible to use fully qualified names (e.g. <code>java.util.Date</code>) or short names with imports:</p><pre><code class="clojure">(import java.util.Date)
|
||||
|
||||
(Date.) ; ⇒ #inst "2012-10-09T21:24:27.229-00:00"
|
||||
</code></pre><p>An example with constructor arguments:</p><pre><code class="clojure">(java.net.URI. "http://clojure.org") ; ⇒ #<URI http://clojure.org>
|
||||
</code></pre><h2 id="how-to-invoke-java-methods">How to Invoke Java Methods</h2><h3 id="instance-methods">Instance Methods</h3><p>Instance methods are invoked using the <code>.</code> special form:</p><pre><code class="clojure">(let [d (java.util.Date.)]
|
||||
(. d getTime)) ; ⇒ 1349819873183
|
||||
</code></pre><p>Just like with object instantiation, it is much more common to see an alternative version:</p><pre><code class="clojure">(let [d (java.util.Date.)]
|
||||
(.getTime d)) ; ⇒ 1349819873183
|
||||
</code></pre><h3 id="static-methods">Static Methods</h3><p>Static methods can be invoked with the same <code>.</code> special form:</p><pre><code class="clojure">(. Math floor 5.677) ; ⇒ 5.0
|
||||
</code></pre><p>or (typically) to sugared version, <code>ClassName/methodName</code>:</p><pre><code class="clojure">(Math/floor 5.677) ; ⇒ 5.0
|
||||
|
||||
(Boolean/valueOf "false") ; ⇒ false
|
||||
(Boolean/valueOf "true") ; ⇒ true
|
||||
</code></pre><h3 id="chained-calls-with-the-double-dot-form">Chained Calls With The Double Dot Form</h3><p>It is possible to chain method calls using the <code>..</code> special form:</p><pre><code class="clojure">(.. (java.util.Date.) getTime toString) ; ⇒ "1349821993809"
|
||||
</code></pre><h3 id="multiple-calls-on-the-same-object">Multiple Calls On the Same Object</h3><p>If you need to call a bunch of methods on a mutable object, you
|
||||
can use the <code>doto</code> macro:</p><pre><code class="clojure">(doto (java.util.Stack.)
|
||||
(.push 42)
|
||||
(.push 13)
|
||||
(.push 7)) ; ⇒ #<Stack [42, 13, 7]>
|
||||
|
||||
(let [pt (Point. 0 0)]
|
||||
(doto pt
|
||||
(.move 10 0))) ; ⇒ #<Point java.awt.Point[x=10, y=0]
|
||||
|
||||
(let [pt (Point. 0 0)]
|
||||
(doto pt
|
||||
(.move 10 0)
|
||||
(.translate 0 10))) ; ⇒ #<Point java.awt.point[x=10,y=10]
|
||||
</code></pre><p>The <code>doto</code> macro returns its first argument as a result.</p><h2 id="how-to-access-java-fields">How to Access Java Fields</h2><p>Public mutable fields are not common in Java libraries but sometimes you need to access them.
|
||||
It's done with the same dot special form:</p><pre><code class="clojure">(import java.awt.Point)
|
||||
|
||||
(let [pt (Point. 0 10)]
|
||||
(. pt x)) ; ⇒ 0
|
||||
|
||||
(let [pt (Point. 0 10)]
|
||||
(. pt y)) ; ⇒ 10
|
||||
</code></pre><p>and just like with instance methods, it is much more common to see the following version:</p><pre><code class="clojure">(import java.awt.Point)
|
||||
|
||||
(let [pt (Point. 0 10)]
|
||||
(.x pt)) ; ⇒ 0
|
||||
|
||||
(let [pt (Point. 0 10)]
|
||||
(.y pt)) ; ⇒ 10
|
||||
</code></pre><h2 id="how-to-set-java-fields">How to Set Java Fields</h2><p>To set a public mutable field, use <code>clojure.core/set!</code> that takes a field in the dot notation
|
||||
demonstrated earlier and a new value:</p><pre><code class="clojure">(import java.awt.Point)
|
||||
|
||||
(let [pt (Point. 0 10)]
|
||||
(set! (.y pt) 100)
|
||||
(.y pt)) ; ⇒ 100
|
||||
</code></pre><p>Fortunately, mutable public fields are rare to meet in the JVM ecosystem so you won't need
|
||||
to do this often.</p><h2 id="how-to-work-with-enums">How To Work With Enums</h2><p><a href="http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html">Enums (enumeration) type</a> values are accessed
|
||||
the same way as fields, except on enum classes and not objects:</p><pre><code class="clojure">java.util.concurrent.TimeUnit/MILLISECONDS ; ⇒ #< MILLISECONDS>
|
||||
</code></pre><h2 id="determining-classes-of-java-objects">Determining Classes of Java Objects</h2><p>To get class of a particular value, pass it to <code>clojure.core/class</code>:</p><pre><code class="clojure">(class 1) ; ⇒ java.lang.Long
|
||||
(class 1.0) ; ⇒ java.lang.Double
|
||||
(class "docs") ; ⇒ java.lang.String
|
||||
(class (java.net.URI. "https://github.com")) ; ⇒ java.net.URI
|
||||
</code></pre><p>As this example demonstrates, Clojure strings are JVM strings, integer literals are compiled
|
||||
as longs and floating point literals are compiled as doubles.</p><p>You can also use <code>clojure.core/type</code> to return either the class of the
|
||||
Java object, or the <code>:type</code> metadata if it exists:</p><pre><code class="clojure">(def foo (with-meta [1 2 3] {:type :bar}))
|
||||
(type foo)
|
||||
;; ⇒ :bar
|
||||
(type [1 2 3])
|
||||
;; ⇒ clojure.lang.PersistentVector
|
||||
</code></pre><h2 id="how-to-get-a-java-class-reference-by-name">How To Get a Java Class Reference By Name</h2><p>To obtain a class reference by its string name (fully qualified), use <code>Class/forName</code> via Java interop:</p><pre><code class="clojure">(Class/forName "java.util.Date") ; ⇒ java.util.Date
|
||||
</code></pre><h3 id="array-types-primitives">Array Types, Primitives</h3><p>JVM has what is called <strong>primitive types</strong> (numerics, chars, booleans) that are not "real" objects.
|
||||
In addition, array types have pretty obscure internal names. If you need to obtain a reference to
|
||||
an array of longs, for example, pass <code>"[[J"</code> to <code>Class/forName</code>. Below is the full table:</p><table class="table-striped table-bordered table"><thead><tr><th>Internal JVM class name</th><th>Array of ? (type)</th></tr></thead><tbody><tr><td><pre>"[[S"</pre></td><td>short</td></tr><tr><td><pre>"[[I"</pre></td><td>integer</td></tr><tr><td><pre>"[[J"</pre></td><td>long</td></tr><tr><td><pre>"[[F"</pre></td><td>float</td></tr><tr><td><pre>"[[D"</pre></td><td>double</td></tr><tr><td><pre>"[[B"</pre></td><td>byte</td></tr><tr><td><pre>"[[C"</pre></td><td>char</td></tr><tr><td><pre>"[[Z"</pre></td><td>boolean</td></tr></tbody></table><p>If this does not make much sense, don't worry. Just remember to come
|
||||
back to this guide when you need to extend a protocol for an array of
|
||||
primitives.</p><h2 id="implementing-java-interfaces-with-reify">Implementing Java Interfaces With reify</h2><p>It is possible to implement Java interfaces in Clojure. It is
|
||||
typically needed to interact with Java libraries that take arguments
|
||||
implementing a particular interface.</p><p>Interfaces are implemented using the <code>reify</code> special form.</p><p>Given the following Java interface:</p><pre><code class="java">public
|
||||
interface FilenameFilter {
|
||||
/**
|
||||
* Tests if a specified file should be included in a file list.
|
||||
*
|
||||
* @param dir the directory in which the file was found.
|
||||
* @param name the name of the file.
|
||||
* @return <code>true</code> if and only if the name should be
|
||||
* included in the file list; <code>false</code> otherwise.
|
||||
*/
|
||||
boolean accept(File dir, String name);
|
||||
}
|
||||
</code></pre><p>here is how to implement it in Clojure:</p><pre><code class="clojure">;; a FileFilter implementation that accepts everything
|
||||
(reify java.io.FilenameFilter
|
||||
(accept [this dir name]
|
||||
true))
|
||||
</code></pre><p><code>reify</code> takes an interface (fully-qualified name or short name) and one or more
|
||||
method implementations that mimic function definitions without the <code>defn</code> and with
|
||||
<em>this</em> (as in Java, JavaScript or <em>self</em> in Ruby, Python) reference being the first argument:</p><pre><code class="clojure">(accept [this dir name]
|
||||
true)
|
||||
</code></pre><p>With <code>reify</code>, generally there is no need to add type hints on arguments: Clojure
|
||||
compiler typically will detect the best matching method (by name and number of arguments).</p><p><code>reify</code> returns a <em>Java class instance</em>. Clojure compiler will generate a class that implements
|
||||
the interface and instantiate it. To demonstrate that reified objects indeed implement
|
||||
the interface:</p><pre><code class="clojure">(let [ff (reify java.io.FilenameFilter
|
||||
(accept [this dir name]
|
||||
true))]
|
||||
(instance? java.io.FileFilter ff)) ; ⇒ true
|
||||
</code></pre><p><code>reify</code> can be used to implement multiple interfaces at once:</p><pre><code class="clojure">(let [ff (reify java.io.FilenameFilter
|
||||
(accept [this dir name]
|
||||
true)
|
||||
|
||||
java.io.FileFilter
|
||||
(accept [this dir]
|
||||
true))]
|
||||
(instance? java.io.FileFilter ff)) ; ⇒ true
|
||||
</code></pre><h3 id="reify-parameter-destructuring-and-varargs">reify, Parameter Destructuring and Varargs</h3><p><code>reify</code> does not support destructuring or variadic number of arguments in method signatures.
|
||||
For example, the following will not work and won't even compile in Clojure 1.5:</p><pre><code class="clojure">(reify com.megacorp.api.AnInterface
|
||||
(aMethod [a [b c]]
|
||||
(comment ...))
|
||||
(anotherMethod [a & rest]
|
||||
(comment ...)))
|
||||
</code></pre><h3 id="example-1">Example 1</h3><p>The following example demonstrates how instances created with <code>reify</code> are passed around
|
||||
as regular Java objects:</p><pre><code class="clojure">(import java.io.File)
|
||||
|
||||
;; a file filter implementation that keeps only .clj files
|
||||
(let [ff (reify java.io.FilenameFilter
|
||||
(accept [this dir name]
|
||||
(.endsWith name ".clj")))
|
||||
dir (File. "/Users/antares/Development/ClojureWerkz/neocons.git/")]
|
||||
(into [] (.listFiles dir ff)))
|
||||
;; ⇒ [#<File /Users/antares/Development/ClojureWerkz/neocons.git/project.clj>]
|
||||
</code></pre><p><code>reify</code> forms a closure: it will capture locals in its scope. This can be used to make implemented
|
||||
methods delegate to Clojure functions. The same example, rewritten with delegation:</p><pre><code class="clojure">user> (import java.io.File)
|
||||
|
||||
;; a file filter implementation that keeps only .clj files
|
||||
(let [f (fn [^File dir ^String name]
|
||||
(.endsWith name ".clj"))
|
||||
ff (reify java.io.FilenameFilter
|
||||
(accept [this dir name]
|
||||
(f dir name)))
|
||||
dir (File. "/Users/antares/Development/ClojureWerkz/neocons.git/")]
|
||||
(into [] (.listFiles dir ff)))
|
||||
;; ⇒ [#<File /Users/antares/Development/ClojureWerkz/neocons.git/project.clj>]
|
||||
</code></pre><p>Note that unlike in the "inline" implementation, Clojure compiler cannot infer types of
|
||||
<code>dir</code> and <code>name</code> parameters in the function that does the filtering, so we added type hints
|
||||
to avoid reflective calls. When methods are implemented "inline", types can be inferred from
|
||||
method signatures in the interface.</p><h2 id="extending-java-classes-with-proxy">Extending Java Classes With proxy</h2><p><code>proxy</code> is one of two ways to generate instances of anonymous classes in Clojure.
|
||||
<code>proxy</code> takes two vectors: one listing its superclass and (optional) interfaces, another constructor signatures, as well as
|
||||
method implementations. Method implementations are basically identical to <code>reify</code> except that the <code>this</code> argument is
|
||||
not necessary.</p><p>A very minimalistic example, we instantiate an anonymous class that extends <code>java.lang.Object</code>, implements no
|
||||
interfaces, has no explictly defined constructors and overrides <code>#toString</code>:</p><pre><code class="clojure">(proxy [Object] []
|
||||
(toString []
|
||||
"I am an instance of an anonymous class generated via proxy"))
|
||||
;; ⇒ #<Object$0 I am an instance of an anonymous class generated via proxy>
|
||||
</code></pre><p>Clojure compiler will generate an anonymous class for this <code>proxy</code> and at runtime, the cost of
|
||||
a <code>proxy</code> call is the cost of instantiating this class (the class is not generated anew on every single call).</p><p>A slightly more complex example where the generated class also implements <code>java.lang.Runnable</code> (runnable objects
|
||||
are commonly used with threads and <code>java.util.concurrent</code> classes) which defines one method, <code>#run</code>:</p><pre><code class="clojure">;; extends java.lang.Object, implements java.lang.Runnable
|
||||
(let [runnable (proxy [Object Runnable] []
|
||||
(toString []
|
||||
"I am an instance of an anonymous class generated via proxy")
|
||||
(run []
|
||||
(println "Run, proxy, run")))]
|
||||
(.run runnable)) ; ⇒ nil
|
||||
;; outputs "Run, proxy, run"
|
||||
</code></pre><p><code>proxy</code> forms a closure: it will capture locals in its scope. This is very often used to create an instance
|
||||
that delegates to a Clojure function:</p><pre><code class="clojure">(let [f (fn [] (println "Executed from a function"))
|
||||
obj (proxy [Object Runnable] []
|
||||
(run []
|
||||
(f)))]
|
||||
(.run obj)) ; ⇒ nil
|
||||
;; outputs "Executed from a function"
|
||||
</code></pre><p>TBD: more realistic examples | <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></p><h2 id="clojure-functions-implement-runnable-and-callable">Clojure Functions Implement Runnable and Callable</h2><p>Note that Clojure functions implement <code>java.lang.Runnable</code> and
|
||||
<code>java.util.concurrent.Callable</code> directly so you can pass functions to
|
||||
methods found in various classes from the <code>java.util.concurrent</code> package.</p><p>For example, to run a function in a new thread:</p><pre><code class="clojure">(let [t (Thread. (fn []
|
||||
(println "I am running in a separate thread")))]
|
||||
(.start t))
|
||||
</code></pre><p>Or submit a function for execution to a thread pool (in JDK terms: an execution service):</p><pre><code class="clojure">(import '[java.util.concurrent Executors ExecutorService Callable])
|
||||
|
||||
(let [^ExecutorService pool (Executors/newFixedThreadPool 16)
|
||||
^Callable clbl (cast Callable (fn []
|
||||
(reduce + (range 0 10000))))
|
||||
task (.submit pool clbl)]
|
||||
(.get task))
|
||||
;; ⇒ 49995000
|
||||
</code></pre><p>Note that without the cast, Clojure compiler would not be able to determine
|
||||
which exact version of the method we intend to invoke, because <code>java.util.concurrent.ExecutionService/submit</code>
|
||||
has two versions, one for <code>Runnable</code> and one for <code>Callable</code>. They work very much the same but return
|
||||
slightly different results (<code>Callable</code> produces a value while <code>Runnable</code> always returns nil when
|
||||
executed).</p><p>The exception we would get without the cast is</p><pre><code>CompilerException java.lang.IllegalArgumentException: More than one matching method found: submit, compiling:(NO_SOURCE_PATH:2)
|
||||
</code></pre><h2 id="gen-class-and-how-to-implement-java-classes-in-clojure">gen-class and How to Implement Java Classes in Clojure</h2><h3 id="overview-1">Overview</h3><p><code>gen-class</code> is a Clojure feature for implementing Java classes in Clojure. It is relatively
|
||||
rarely used compared to <code>proxy</code> and <code>reify</code> but is needed to implement executable classes
|
||||
(that <code>java</code> runner and IDEs can as program entry points).</p><p>Unlike <code>proxy</code> and <code>reify</code>, <code>gen-class</code> defines named classes. They can be passed to Java
|
||||
APIs that expect class references. Classes defined with <code>gen-class</code> can extend
|
||||
base classes, implement any number of Java interfaces, define any number of constructors
|
||||
and define both instance and static methods.</p><h3 id="aot">AOT</h3><p><code>gen-class</code> requires <em>ahead-of-time</em> (AOT) compilation. It means that
|
||||
before using the classes defined with <code>gen-class</code>, the Clojure
|
||||
compiler needs to produce <code>.class</code> files from <code>gen-class</code> definitions.</p><h3 id="class-definition-with-clojurecoregen-class">Class Definition With clojure.core/gen-class</h3><p><code>clojure.core/gen-class</code> is a macro that uses a DSL for defining class
|
||||
methods, base class, implemented interfaces and so on.</p><p>It takes a number of options:</p><ul><li><code>:name</code> (a symbol): defines generated class name</li><li><code>:extends</code> (a symbol): name of the base class</li><li><code>:implements</code> (a collection): interfaces the class implements</li><li><code>:constructors</code> (a map): constructor signatures</li><li><code>:methods</code> (a collection): lists methods that will be implemented</li><li><code>:init</code> (symbol): defines a function that will be invoked with constructor arguments</li><li><code>:post-init</code> (symbol): defines a function that will be called with a constructed instance as its first argument</li><li><code>:state</code> (symbol): if supplied, a public final instance field with the given name will be created. Only makes sense when
|
||||
used with <code>:init</code>. State field value should be an atom or other ref type to allow state mutation.</li><li><code>:prefix</code> (string, default: <code>"-"</code>): methods will call functions named as <code>(str prefix method-name)</code>, e.g. <code>-getName</code> for <code>getName</code>.</li><li><code>:main</code> (boolean): if <code>true</code>, a public static main method will be generated for the class. It will delegate
|
||||
to a function named main with the prefix (<code>(str prefix "main")</code>), <code>-main</code> by default</li><li><code>:exposes</code>: TBD</li><li><code>:exposes-methods</code>: TBD</li><li><code>:factory</code>: TBD</li><li><code>:load-impl-ns</code>: TBD</li><li><code>:impl-ns</code>: TBD</li></ul><h4 id="the-name-option">The :name Option</h4><p>TBD</p><h4 id="the-extends-option">The :extends Option</h4><p>TBD</p><h4 id="the-implements-option">The :implements Option</h4><p>TBD</p><h4 id="the-constructors-option">The :constructors Option</h4><p>TBD</p><h4 id="the-methods-option">The :methods Option</h4><p>TBD</p><h4 id="the-init-option">The :init Option</h4><p>TBD</p><h4 id="the-post-init-option">The :post-init Option</h4><p>TBD</p><h4 id="the-state-option">The :state Option</h4><p>TBD</p><h4 id="the-prefix-option">The :prefix Option</h4><p>TBD</p><h4 id="the-main-option">The :main Option</h4><p>TBD</p><h4 id="the-exposes-option">The :exposes Option</h4><p>TBD</p><h4 id="the-exposes-methods-option">The :exposes-methods Option</h4><p>TBD</p><h4 id="the-factory-option">The :factory Option</h4><p>TBD</p><h4 id="the-load-impl-ns-option">The :load-impl-ns Option</h4><p>TBD</p><h4 id="the-impl-ns-option">The :impl-ns Option</h4><p>TBD</p><h3 id="gen-class-in-the-ns-macro">gen-class In The ns Macro</h3><p><code>gen-class</code> can be used with existing namespaces by adding <code>(:gen-class)</code> to the
|
||||
<code>ns</code> macro. Here is a "hello, world" example command line app that uses <code>gen-class</code>
|
||||
to generate a class that JVM launcher (<code>java</code>) can run:</p><pre><code class="clojure">(ns genclassy.core
|
||||
(:gen-class))
|
||||
|
||||
(defn -main
|
||||
[& args]
|
||||
(println "Hello, World!"))
|
||||
</code></pre><p>This will use the name of the namespace for class name and use the namespace for method
|
||||
implementation (see the <code>:impl-ns</code> option above).</p><h3 id="examples">Examples</h3><p>A medium size example taken from an open source library:</p><pre><code class="clojure">(ns clojurewerkz.quartzite.listeners.amqp.PublishingSchedulerListener
|
||||
(:gen-class :implements [org.quartz.SchedulerListener]
|
||||
:init init
|
||||
:state state
|
||||
:constructors {[com.rabbitmq.client.Channel String String] []})
|
||||
(:require [langohr.basic :as lhb]
|
||||
[clojure.data.json :as json])
|
||||
(:use [clojurewerkz.quartzite.conversion])
|
||||
(:import [org.quartz SchedulerListener SchedulerException Trigger TriggerKey JobDetail JobKey]
|
||||
[com.rabbitmq.client Channel]
|
||||
[java.util Date]
|
||||
[clojurewerkz.quartzite.listeners.amqp PublishingSchedulerListener]))
|
||||
|
||||
|
||||
|
||||
(defn publish
|
||||
[^PublishingSchedulerListener this payload ^String type]
|
||||
(let [{ :keys [channel exchange routing-key] } @(.state this)
|
||||
payload (json/json-str payload)]
|
||||
(lhb/publish channel exchange routing-key payload :type type)))
|
||||
|
||||
|
||||
(defn -init
|
||||
[^Channel ch ^String exchange ^String routing-key]
|
||||
[[] (atom { :channel ch :exchange exchange :routing-key routing-key })])
|
||||
|
||||
|
||||
(defmacro payloadless-publisher
|
||||
[method-name message-type]
|
||||
`(defn ~method-name
|
||||
[this#]
|
||||
(publish this# (json/json-str {}) ~message-type)))
|
||||
|
||||
(payloadless-publisher -schedulerStarted "quartz.scheduler.started")
|
||||
(payloadless-publisher -schedulerInStandbyMode "quartz.scheduler.standby")
|
||||
(payloadless-publisher -schedulingDataCleared "quartz.scheduler.cleared")
|
||||
(payloadless-publisher -schedulerShuttingDown "quartz.scheduler.shutdown")
|
||||
|
||||
|
||||
(defn -schedulerError
|
||||
[this ^String msg ^SchedulerException cause]
|
||||
(publish this (json/json-str { :message msg :cause (str cause) }) "quartz.scheduler.error"))
|
||||
|
||||
|
||||
(defn -jobScheduled
|
||||
[this ^Trigger trigger]
|
||||
(publish this (json/json-str { :group (-> trigger .getKey .getGroup) :key (-> trigger .getKey .getName) :description (.getDescription trigger) }) "quartz.scheduler.job-scheduled"))
|
||||
|
||||
(defn -jobUnscheduled
|
||||
[this ^TriggerKey key]
|
||||
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-unscheduled"))
|
||||
|
||||
(defn -triggerFinalized
|
||||
[this ^Trigger trigger]
|
||||
(publish this (json/json-str { :group (-> trigger .getKey .getGroup) :key (-> trigger .getKey .getName) :description (.getDescription trigger) }) "quartz.scheduler.trigger-finalized"))
|
||||
|
||||
(defn -triggerPaused
|
||||
[this ^TriggerKey key]
|
||||
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.trigger-paused"))
|
||||
|
||||
(defn -triggersPaused
|
||||
[this ^String trigger-group]
|
||||
(publish this (json/json-str { :group trigger-group }) "quartz.scheduler.triggers-paused"))
|
||||
|
||||
(defn -triggerResumed
|
||||
[this ^TriggerKey key]
|
||||
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.trigger-resumed"))
|
||||
|
||||
(defn -triggersResumed
|
||||
[this ^String trigger-group]
|
||||
(publish this (json/json-str { :group trigger-group }) "quartz.scheduler.triggers-resumed"))
|
||||
|
||||
|
||||
|
||||
(defn -jobAdded
|
||||
[this ^JobDetail detail]
|
||||
(publish this (json/json-str { :job-detail (from-job-data (.getJobDataMap detail)) :description (.getDescription detail) }) "quartz.scheduler.job-added"))
|
||||
|
||||
(defn -jobDeleted
|
||||
[this ^JobKey key]
|
||||
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-deleted"))
|
||||
|
||||
(defn -jobPaused
|
||||
[this ^JobKey key]
|
||||
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-paused"))
|
||||
|
||||
(defn -jobsPaused
|
||||
[this ^String job-group]
|
||||
(publish this (json/json-str { :group job-group }) "quartz.scheduler.jobs-paused"))
|
||||
|
||||
(defn -jobResumed
|
||||
[this ^JobKey key]
|
||||
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-resumed"))
|
||||
|
||||
(defn -jobsResumed
|
||||
[this ^String job-group]
|
||||
(publish this (json/json-str { :group job-group }) "quartz.scheduler.jobs-resumed"))
|
||||
</code></pre><h3 id="inspecting-class-signatures">Inspecting Class Signatures</h3><p>When using <code>gen-class</code> for interoperability purposes, sometimes it is necessary to inspect the API
|
||||
of the class generated by <code>gen-class</code>.</p><p>It can be inspected
|
||||
using <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html">javap</a>. Given the
|
||||
following Clojure namespace:</p><pre><code class="clojure">(ns genclassy.core
|
||||
(:gen-class))
|
||||
|
||||
(defn -main
|
||||
[& args]
|
||||
(println "Hello, World!"))
|
||||
</code></pre><p>We can inspect the produced class like so:</p><pre><code># from target/classes, default .class files location used by Leiningen
|
||||
javap genclassy.core
|
||||
</code></pre><p>will output</p><pre><code class="java">public class genclassy.core {
|
||||
public static {};
|
||||
public genclassy.core();
|
||||
public java.lang.Object clone();
|
||||
public int hashCode();
|
||||
public java.lang.String toString();
|
||||
public boolean equals(java.lang.Object);
|
||||
public static void main(java.lang.String[]);
|
||||
}
|
||||
</code></pre><h2 id="how-to-extend-protocols-to-java-classes">How To Extend Protocols to Java Classes</h2><p>Clojure protocols can be extended to any java class (including
|
||||
Clojure's internal types) very easily using <code>extend</code>:</p><p>Using the example of a json library, we can define our goal as getting
|
||||
to the point where the following works:</p><pre><code class="clojure">(json-encode (java.util.UUID/randomUUID))
|
||||
</code></pre><p>First, let's start with the protocol for json encoding an object:</p><pre><code class="clojure">(defprotocol JSONable
|
||||
(json-encode [obj]))
|
||||
</code></pre><p>So, everything that is "JSONable" implements a <code>json-encode</code> method.</p><p>Next, let's define a dummy method to do the "encoding" (in this
|
||||
example, it just prints to standard out instead, it doesn't actually
|
||||
do any json encoding):</p><pre><code class="clojure">(defn encode-fn
|
||||
[x]
|
||||
(prn x))
|
||||
</code></pre><p>Now, define a method that will encode java objects by calling <code>bean</code>
|
||||
on them, then making each value of the bean map a string:</p><pre><code class="clojure">(defn encode-java-thing
|
||||
[obj]
|
||||
(encode-fn
|
||||
(into {}
|
||||
(map (fn [m]
|
||||
[(key m) (str (val m))])
|
||||
(bean obj)))))
|
||||
</code></pre><p>Let's try it on an example object, a UUID:</p><pre><code class="clojure">(encode-java-thing (java.util.UUID/randomUUID))
|
||||
;; ⇒ {:mostSignificantBits "-6060053801408705927",
|
||||
;; :leastSignificantBits "-7978739947533933755",
|
||||
;; :class "class java.util.UUID"}
|
||||
</code></pre><p>The next step is to extend the protocol to the java type, telling
|
||||
clojure which java type to extend, the protocol to implement and the
|
||||
method to use for the <code>json-encode</code> method:</p><pre><code class="clojure">(extend java.util.UUID
|
||||
JSONable
|
||||
{:json-encode encode-java-thing})
|
||||
</code></pre><p>Alternatively, you could use the <code>extend-type</code> macro, which actually
|
||||
expands into calls to <code>extend</code>:</p><pre><code class="clojure">(extend-type java.util.UUID
|
||||
JSONable
|
||||
(json-encode [obj] (encode-java-thing obj)))
|
||||
</code></pre><p>Now we can use <code>json-encode</code> for the object we've extended:</p><pre><code class="clojure">(json-encode (java.util.UUID/randomUUID))
|
||||
;; ⇒ {:mostSignificantBits "3097485598740136901",
|
||||
;; :leastSignificantBits "-9000234678473924364",
|
||||
;; :class "class java.util.UUID"}
|
||||
</code></pre><p>You could also write the function inline in the extend block, for
|
||||
example, extending <code>nil</code> to return a warning string:</p><pre><code class="clojure">(extend nil
|
||||
JSONable
|
||||
{:json-encode (fn [x] "x is nil!")})
|
||||
|
||||
(json-encode nil)
|
||||
;; ⇒ "x is nil!"
|
||||
</code></pre><p>The <code>encode-java-thing</code> method can also be reused for other Java types
|
||||
we may want to encode:</p><pre><code class="clojure">(extend java.net.URL
|
||||
JSONable
|
||||
{:json-encode encode-java-thing})
|
||||
|
||||
(json-encode (java.net.URL. "http://aoeu.com"))
|
||||
;; ⇒ {:path "",
|
||||
;; :protocol "http",
|
||||
;; :authority "aoeu.com",
|
||||
;; :host "aoeu.com",
|
||||
;; :ref "",
|
||||
;; :content "sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@4ecac02f",
|
||||
;; :class "class java.net.URL",
|
||||
;; :defaultPort "80",
|
||||
;; :port "-1",
|
||||
;; :query "",
|
||||
;; :file "",
|
||||
;; :userInfo ""}
|
||||
</code></pre><h2 id="using-intrinsic-locks-synchronized-in-clojure">Using Intrinsic Locks ("synchronized") in Clojure</h2><p>Every object on the JVM has an <em>intrinsic lock</em> (also referred to as <em>monitor lock</em>
|
||||
or simply <em>monitor</em>). While very rarely necessary, Clojure provides support for
|
||||
operations that acquire intrinsic lock of a mutable Java object.</p><p>This is covered in the <a href="../concurrency_and_parallelism/index.html#using_intrinsic_locks_synchronized_in_clojure">Concurrency and Parallelism guide</a>.</p><h2 id="wrapping-up">Wrapping Up</h2><p>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></p><h2 id="contributors">Contributors</h2><p>Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a> (original author)
|
||||
Lee Hinman <a href="mailto:lee@writequit.org">lee@writequit.org</a>
|
||||
gsnewmark <a href="mailto:gsnewmark@meta.ua">gsnewmark@meta.ua</a></p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../laziness/index.html">« Laziness in Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../macros/index.html">Clojure Macros and Metaprogramming »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
264
clones/clojure-doc.org/articles/language/laziness/index.html
Normal file
264
clones/clojure-doc.org/articles/language/laziness/index.html
Normal file
|
@ -0,0 +1,264 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Laziness in Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/laziness/" />
|
||||
<meta property="og:title" content="Laziness in Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/laziness/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Laziness in Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>What are lazy sequences</li><li>Pitfalls with lazy sequences</li><li>How to create functions that produce lazy sequences</li><li>How to force evaluation</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="overview">Overview</h2><p>Clojure is not a <a href="http://en.wikipedia.org/wiki/Lazy_evaluation">lazy language</a>.</p><p>However, Clojure supports <em>lazily evaluated sequences</em>. This means that sequence elements are not
|
||||
available ahead of time and produced as the result of a computation. The computation
|
||||
is performed as needed. Evaluation of lazy sequences is known as <em>realization</em>.</p><p>Lazy sequences can be infinite (e.g. the sequence of Fibonacci numbers, a sequence of
|
||||
dates with a particular interval between them, and so on). If a lazy sequence is finite,
|
||||
when its computation is completed, it becomes <em>fully realized</em>.</p><p>When it is necessary to fully realize a lazy sequence, Clojure provides a way to
|
||||
<em>force evaluation</em> (force realization).</p><h2 id="benefits-of-lazy-sequences">Benefits of Lazy Sequences</h2><p>Lazy sequences have two main benefits:</p><ul><li>They can be infinite</li><li>Full realization of interim results can be avoided</li></ul><h2 id="producing-lazy-sequences">Producing Lazy Sequences</h2><p>Lazy sequences are produced by functions. Such functions either use the <code>clojure.core/lazy-seq</code> macro
|
||||
or other functions that produce lazy sequences.</p><p><code>clojure.core/lazy-seq</code> accepts one or more forms that produce a sequence of <code>nil</code> (when the sequence
|
||||
is fully realized) and returns a seqable data structure that invokes the body the first time
|
||||
the value is needed and then caches the result.</p><p>For example, the following function produces a lazy sequence of random UUIDs strings:</p><pre><code class="klipse-clojure nohighlight">(defn uuid-seq
|
||||
[]
|
||||
(lazy-seq
|
||||
(cons (str (random-uuid))
|
||||
(uuid-seq))))
|
||||
</code></pre><blockquote><p>Note: the <code>random-uuid</code> function is available in ClojureScript and was introduced into Clojure
|
||||
in version 1.11 Alpha 3. Prior to that, you needed to use Java interop:</p></blockquote><pre><code class="clojure">(defn uuid-seq
|
||||
[]
|
||||
(lazy-seq
|
||||
(cons (str (java.util.UUID/randomUUID))
|
||||
(uuid-seq))))
|
||||
</code></pre><p>Another example:</p><pre><code class="klipse-clojure nohighlight">(defn fib-seq
|
||||
"Returns a lazy sequence of Fibonacci numbers"
|
||||
([]
|
||||
(fib-seq 0 1))
|
||||
([a b]
|
||||
(lazy-seq
|
||||
(cons b (fib-seq b (+ a b))))))
|
||||
</code></pre><p>Both examples use <code>clojure.core/cons</code> which prepends an element to a sequence. The sequence
|
||||
can in turn be lazy, which both of the examples rely on.</p><p>Even though both of these sequences are infinite, taking first N elements from each does
|
||||
return successfully:</p><pre><code class="klipse-clojure nohighlight">(take 3 (uuid-seq))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(take 10 (fib-seq))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(take 20 (fib-seq))
|
||||
</code></pre><h2 id="realizing-lazy-sequences-forcing-evaluation">Realizing Lazy Sequences (Forcing Evaluation)</h2><p>Lazy sequences can be forcefully realized with <code>clojure.core/dorun</code> and
|
||||
<code>clojure.core/doall</code>. The difference between the two is that <code>dorun</code>
|
||||
throws away all results and is supposed to be used for side effects,
|
||||
while <code>doall</code> returns computed values:</p><pre><code class="klipse-clojure nohighlight">(dorun (map inc [1 2 3 4]))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(doall (map inc [1 2 3 4]))
|
||||
</code></pre><h2 id="commonly-used-functions-that-produce-lazy-sequences">Commonly Used Functions That Produce Lazy Sequences</h2><p>Multiple frequently used <code>clojure.core</code> functions return lazy sequences,
|
||||
most notably:</p><ul><li><code>map</code></li><li><code>filter</code></li><li><code>remove</code></li><li><code>range</code></li><li><code>take</code></li><li><code>take-while</code></li><li><code>drop</code></li><li><code>drop-while</code></li></ul><p>The following example uses several of these functions to return 10 first
|
||||
even numbers in the range of [0, n):</p><pre><code class="klipse-clojure nohighlight">(take 10 (filter even? (range 0 100)))
|
||||
</code></pre><p>Several functions in <code>clojure.core</code> are designed to produce lazy
|
||||
sequences:</p><ul><li><code>repeat</code></li><li><code>iterate</code></li><li><code>cycle</code></li></ul><p>For example:</p><pre><code class="klipse-clojure nohighlight">(take 3 (repeat "ha"))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(take 5 (repeat "ha"))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(take 3 (cycle [1 2 3 4 5]))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(take 10 (cycle [1 2 3 4 5]))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(take 3 (iterate (partial + 1) 1))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(take 5 (iterate (partial + 1) 1))
|
||||
</code></pre><h2 id="lazy-sequences-chunking">Lazy Sequences Chunking</h2><p>There are two fundamental strategies for implementing lazy sequences:</p><ul><li>Realize elements one-by-one</li><li>Realize elements in groups (chunks, batches)</li></ul><p>In Clojure 1.1+, lazy sequences are <em>chunked</em> (realized in chunks).</p><p>For example, in the following code</p><pre><code class="klipse-clojure nohighlight">(take 10 (range 1 1000000000000))
|
||||
</code></pre><p>one-by-one realization would realize one element 10 times. With chunked sequences,
|
||||
elements are realized ahead of time in chunks (32 elements at a time).</p><p>This reduces the number of realizations and, for many common workloads, improves
|
||||
efficiency of lazy sequences.</p><h2 id="contributors">Contributors</h2><p>Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a>, 2013 (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../functions/index.html">« Functions in Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../interop/index.html">Clojure interoperability with Java »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
|
||||
<script>
|
||||
window.klipse_settings = {
|
||||
"selector" : ".klipse-clojure"
|
||||
};
|
||||
</script>
|
||||
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
|
||||
</body>
|
||||
</html>
|
382
clones/clojure-doc.org/articles/language/macros/index.html
Normal file
382
clones/clojure-doc.org/articles/language/macros/index.html
Normal file
|
@ -0,0 +1,382 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Clojure Macros and Metaprogramming</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/macros/" />
|
||||
<meta property="og:title" content="Clojure Macros and Metaprogramming" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/macros/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Clojure Macros and Metaprogramming</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>Clojure macros</li><li>the Clojure compilation process</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="before-you-read-this-guide">Before You Read This Guide</h2><p>This is one of the most hardcore guides of the entire Clojure documentation
|
||||
project. It describes concepts that are relatively unique to the Lisp family of languages
|
||||
that Clojure belongs to. Understanding them may take some time for folks without
|
||||
a metaprogramming background. Don't let this learning curve
|
||||
discourage you.</p><p>If some parts are not clear, please ask for clarification <a href="https://groups.google.com/forum/?fromgroups#!forum/clojure">on the
|
||||
mailing
|
||||
list</a> or
|
||||
<a href="https://github.com/clojure-doc/clojure-doc.github.io/issues">file an issue</a> on GitHub.
|
||||
We will work hard on making this guide easy to follow with edits and
|
||||
images to illustrate the concepts.</p><h2 id="overview">Overview</h2><p>Clojure is a dialect of Lisp and while it departs with some features of "traditional" Lisps,
|
||||
the fundamentals are there. One very powerful feature that comes with it is <em>macros</em>,
|
||||
a way to do metaprogramming using the language itself. This is pretty different from
|
||||
other languages known for good metaprogramming capabilities (e.g. Ruby) in that
|
||||
in Clojure, metaprogramming does not mean string generation. Instead, it means
|
||||
constructing a tree [of S-expressions, or lists]. This enables very powerful
|
||||
DSLs (domain-specific languages).</p><h2 id="compile-time-and-run-time">Compile Time and Run Time</h2><p>Clojure is a compiled language. The compiler reads source files or strings,
|
||||
produces data structures (aka the AST) and performs <em>macroexpansion</em>. Macros are evaluated at
|
||||
<em>compile time</em> and produce modified data structures that are compiled to the JVM
|
||||
bytecode. That bytecode is executed at <em>run time</em>.</p><p>Clojure code is compiled when it is loaded with <code>clojure.core/load</code> or <code>clojure.core/require</code>
|
||||
or can be ahead of time (AOT compilation) using tools such as <a href="http://leiningen.org">Leiningen</a>
|
||||
or the <a href="../../ecosystem/maven/index.html">Clojure Maven plugin</a>.</p><h2 id="clojure-reader">Clojure Reader</h2><p>Reader is another name for parser. Unlike many other languages, reader in Clojure
|
||||
can be extended in the language itself. It is also exposed to the language
|
||||
with <code>clojure.core/read</code> and <code>clojure.core/read-string</code> functions that
|
||||
return data structures:</p><pre style="visibility:hidden; height:0;"><code class="klipse-clojure nohighlight">
|
||||
(require '[cljs.reader :refer [read-string]])
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(read-string "(if true :truth :false)")
|
||||
;= (if true :truth :false)
|
||||
</code></pre><p>Here we got back a list that is not evaluated.</p><p>The Reader produces data structures (in part that's why "code is data" in homoiconic
|
||||
languages) that are then evaluated:</p><ul><li>Literals (e.g., strings, integers, vectors) evaluate to themselves</li><li>Lists evaluate to invocations (calls) of functions and so on</li><li>Symbols are resolved to a var value</li></ul><p>Expressions that can be evaluated (invoked) are known as <em>forms</em>. Forms consist of:</p><ul><li>Functions</li><li>Macros</li><li>Special forms</li></ul><h3 id="special-forms">Special Forms</h3><p>The reader parses some forms in special ways that are not consistent
|
||||
with the rest of Clojure's syntax.</p><p>Such forms are called <em>special forms</em>. They consist of</p><ul><li>. (the dot special form)</li><li>new</li><li>set!</li><li>def</li><li>var</li><li>fn* (<code>fn</code> without destructuring)</li><li>if</li><li>case* (internal implementation of <code>case</code>)</li><li>do</li><li>let* (<code>let</code> without destructuring)</li><li>letfn* (<code>letfn</code> without destructuring)</li><li>clojure.core/import* (<code>import</code>)</li><li>quote</li><li>loop* (<code>loop</code> without destructuring)</li><li>recur</li><li>throw, try, catch, finally</li><li>deftype* (internals of <code>deftype</code>)</li><li>reify* (internals of <code>reify</code>)</li><li>monitor-enter, monitor-exit</li></ul><p>Some special forms are used directly in user code (like <code>do</code> and <code>if</code>), while others
|
||||
are only used to build more user friendly interfaces (like using <code>deftype</code> over the special form <code>deftype*</code>).</p><h2 id="first-taste-of-macros">First Taste of Macros</h2><p>Some programming languages include an <code>unless</code> expression (or statement) that is
|
||||
the opposite of <code>if</code>. Clojure is not one of them but it can be added by using
|
||||
a macro:</p><pre style="visibility:hidden; height:0;"><code class="klipse-clojure nohighlight">
|
||||
(require '[chivorcam.core :refer [defmacro defmacfn]])
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defmacro unless
|
||||
"Similar to if but negates the condition"
|
||||
[condition & forms]
|
||||
`(if (not ~condition)
|
||||
~@forms))
|
||||
</code></pre><p>Macros are defined using the <code>clojure.core/defmacro</code> function that takes
|
||||
macro name as a symbol, an optional documentation string, a vector
|
||||
of arguments and the macro body.</p><p>This macro can be used like similarly to the <code>if</code> form:</p><pre><code class="klipse-clojure nohighlight">(unless (= 1 2)
|
||||
"one does not equal two"
|
||||
"one equals two. How come?")
|
||||
</code></pre><p>Just like the <code>if</code> special form, this macro produces an expression that
|
||||
returns a value:</p><pre><code class="clojure">(unless (= 1 2)
|
||||
"one does not equal two"
|
||||
"one equals two. How come?")
|
||||
</code></pre><p>in fact, this is because the macro piggybacks on the <code>if</code> form.
|
||||
To see what the macro expands to, we can use <code>clojure.core/macroexpand-1</code>:</p><pre><code class="klipse-clojure nohighlight">(macroexpand-1 '(unless (= 1 2) true false))
|
||||
;= (if (clojure.core/not (= 1 2)) true false)
|
||||
</code></pre><p>This simplistic macro and the way we expanded it with <code>macroexpand-1</code>
|
||||
demonstrates three features of the Clojure reader that are used when
|
||||
writing macros:</p><ul><li>Quote (')</li><li>Syntax quote (`)</li><li>Unquote (~)</li><li>Unquote splicing (~@)</li></ul><h2 id="quote">Quote</h2><p>Quote supresses evaluation of the form that follows it. In other words,
|
||||
instead of being treated as an invocation, it will be treated as a list.</p><p>Compare:</p><pre><code class="klipse-clojure nohighlight">;; this form is evaluated by calling the clojure.core/+ function
|
||||
(+ 1 2 3)
|
||||
;= 6
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; quote supresses evaluation so the + is treated as a regular
|
||||
;; list element
|
||||
'(+ 1 2 3)
|
||||
;= (+ 1 2 3)
|
||||
</code></pre><p>The syntax quote supresses evaluation of the form that follows it and
|
||||
all nested forms. It is similar to templating languages where parts
|
||||
of the template are "fixed" and parts are "inserted" (evaluated).
|
||||
The syntax quote makes the form that follows it "a template".</p><h2 id="unquote">Unquote</h2><p>Unquote then is how parts of the template are forced to be evaluated
|
||||
(act similarly to variables in templates in templating languages).</p><p>Let's take another look at the same <code>unless</code> macro:</p><pre><code class="klipse-clojure nohighlight">(defmacro unless
|
||||
[condition & forms]
|
||||
`(if (not ~condition)
|
||||
~@forms))
|
||||
</code></pre><p>and how we invoke it:</p><pre><code class="klipse-clojure nohighlight">(unless (= 1 2)
|
||||
"one does not equal two"
|
||||
"one equals two. How come?")
|
||||
</code></pre><p>When the macro is expanded, the condition local in this example has the value
|
||||
of <code>(= 1 2)</code> (a list). We want <code>unless</code> to perform boolean evaluation on it,
|
||||
and that's what unquote (<code>~</code>) does as can be seen from macroexpansion:</p><pre><code class="klipse-clojure nohighlight">(macroexpand-1 '(unless (= 1 2) true false))
|
||||
;= (if (clojure.core/not (= 1 2)) true false)
|
||||
</code></pre><p>Compare this with what the macro expands to when the unquote is removed:</p><pre><code class="klipse-clojure nohighlight">;; incorrect, missing unquote!
|
||||
(defmacro unless
|
||||
[condition & forms]
|
||||
`(if (not condition)
|
||||
~@forms))
|
||||
|
||||
(macroexpand-1 '(unless (= 1 2) true false))
|
||||
;= (if (clojure.core/not user/condition) true false)
|
||||
</code></pre><h3 id="implementation-details">Implementation Details</h3><p>The unquote operator is replaced by the reader with a call to a core
|
||||
Clojure function, <code>clojure.core/unquote</code>.</p><h2 id="unquote-splicing">Unquote-splicing</h2><p>Some macros take multiple forms. This is common in DSLs, for example.
|
||||
Each of those forms is often need to be quoted and concatenated.</p><p>The unquote-splicing operator (<code>~@</code>) is a convenient way to do it:</p><pre><code class="klipse-clojure nohighlight">(defmacro unsplice
|
||||
[& coll]
|
||||
`(do ~@coll))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(macroexpand-1 '(unsplice (def a 1) (def b 2)))
|
||||
;= (do (def a 1) (def b 2))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(unsplice (def a 1) (def b 2))
|
||||
;= #'user/b
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">a
|
||||
;= 1
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">b
|
||||
;= 2
|
||||
</code></pre><h3 id="implementation-details-1">Implementation Details</h3><p>The unquote-splicing operator is replaced by the reader with a call to a core
|
||||
Clojure function, <code>clojure.core/unquote-splicing</code>.</p><h2 id="macro-hygiene-and-gensym">Macro Hygiene and gensym</h2><p>When writing a macro, there is a possibility that the macro will interact with
|
||||
vars or locals outside of it in unexpected ways, for example, by <a href="http://en.wikipedia.org/wiki/Variable_shadowing">shadowing</a> them.
|
||||
Such macros are known as <em>unhygienic macros</em>.</p><p>Clojure does not implement a full solution to hygienic macros but
|
||||
provides solutions to the biggest pitfalls of unhygienic macros by enforcing several restrictions:</p><ul><li>Symbols within a syntax quoted form are namespace-qualified</li><li>Unique symbol name generation (aka <em>gensyms</em>)</li></ul><h3 id="namespace-qualification-within-syntax-quote">Namespace Qualification Within Syntax Quote</h3><p>To demonstrate this behavior of syntax quote, consider the following example
|
||||
that replaces values "yes" and "no" with true and false, respectively, at compile
|
||||
time:</p><pre><code class="klipse-clojure nohighlight">(defmacro yes-no->boolean
|
||||
[val]
|
||||
`(let [b (= ~val "yes")]
|
||||
b))
|
||||
;= #'user/yes-no->boolean
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(macroexpand-1 '(yes-no->boolean "yes"))
|
||||
;= (clojure.core/let [user/b (clojure.core/= "yes" "yes")] user/b)
|
||||
</code></pre><p>Macroexpansion demonstrates that the Clojure compiler makes the <code>b</code> symbol namespace-qualified
|
||||
(<code>user</code> is the default namespace in the Clojure REPL). This helps avoid var and local
|
||||
shadowing.</p><p>Note: Special forms are not necessarily qualified. See section 'Special Forms in Detail'.</p><h3 id="generated-symbols-gensyms">Generated Symbols (gensyms)</h3><p>Automatic namespace generation is fine in some cases, but not every time. Sometimes
|
||||
a symbol name that is unique in the macro scope is necessary.</p><p>Unique symbols names can be generated with the <code>clojure.core/gensym</code> function that
|
||||
take an optional base string:</p><pre><code class="klipse-clojure nohighlight">(gensym)
|
||||
;= G__54
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(gensym "base")
|
||||
;= base57
|
||||
</code></pre><p>There is a shortcut: if a symbol ends in <code>#</code> within a syntax quote form, it will be
|
||||
expanded by the compiler into a gensym (aka. an auto-gensym):</p><pre><code class="clojure">(defmacro yes-no->boolean
|
||||
[val]
|
||||
`(let [b# (= ~val "yes")]
|
||||
b#))
|
||||
;= #'user/yes-no->boolean
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(macroexpand-1 '(yes-no->boolean "yes"))
|
||||
;= (clojure.core/let [b__148__auto__ (clojure.core/= "yes" "yes")] b__148__auto__)
|
||||
</code></pre><p>The name that replaced <code>b#</code> was generated by the compiler to make unwanted variable
|
||||
capture very unlikely in practice, and impossible if all bindings are named with auto-gensym.</p><p>Theoretically, Clojure's approach to generating uncaptured gensyms (incrementing a global counter) can be circumvented
|
||||
via a mischievous macro or very bad luck.</p><p>Tip:
|
||||
Avoid code with <code>__</code> in local binding names. This ensures
|
||||
auto-gensyms are <em>never</em> captured in unwanted ways.</p><h2 id="macroexpansions">Macroexpansions</h2><p>During macro development, it is important to be able to test the macro
|
||||
and see what data structures the macro expands to. This can be done
|
||||
with two functions in the core Clojure library:</p><ul><li><code>clojure.core/macroexpand-1</code></li><li><code>clojure.core/macroexpand</code></li><li><code>clojure.walk/macroexpand-all</code></li></ul><p>The difference between the two is that <code>macroexpand-1</code> will expand the macro
|
||||
only once. If the result contains calls to other macros, those won't be expanded.
|
||||
<code>macroexpand</code>, however, will continue expanding all macros until the top level form
|
||||
is no longer a macro.</p><p>Both macroexpansion functions take quoted forms.</p><p>Macro expansion functions can be used to find out that <code>when</code> is a macro implemented on top of
|
||||
the <code>if</code> special form, for example:</p><pre><code class="klipse-clojure nohighlight">(macroexpand '(when true 1 42))
|
||||
</code></pre><h3 id="full-macroexpansion">Full Macroexpansion</h3><p>Neither <code>macroexpand-1</code> nor <code>macroexpand</code> expand nested
|
||||
forms. To fully expand macros including those in nested forms, there is <code>clojure.walk/macroexpand-all</code>,
|
||||
which, however, is not part of Clojure core and does not behave exactly the same way
|
||||
the compiler does.</p><h2 id="difference-between-quote-and-syntax-quote">Difference Between Quote and Syntax Quote</h2><p>The key difference between quote and syntax quote is that
|
||||
symbols within a syntax quoted form are automatically namespace-qualified.</p><h2 id="security-considerations">Security Considerations</h2><p><code>clojure.core/read-string</code> <em>can execute arbitrary code</em> and <em>must not</em> be used
|
||||
on inputs coming from untrusted sources. This behavior is controlled by the <code>clojure.core/*read-eval*</code>
|
||||
var. Starting with Clojure 1.5, the default value of <code>*read-eval*</code> is <code>false</code>.</p><p><code>*read-eval*</code> can be disabled via a property when starting the JVM:</p><pre><code>-Dclojure.read.eval=false
|
||||
</code></pre><p>When reading Clojure forms from untrusted sources, use <code>clojure.edn/read-string</code>, which is
|
||||
does not perform arbitrary code execution and is safer. <code>clojure.edn/read-string</code> implements
|
||||
the <a href="https://github.com/edn-format/edn">EDN format</a>, a subset of Clojure syntax for data
|
||||
structures. <code>clojure.edn</code> was introduced in Clojure 1.5.</p><h2 id="special-forms-in-detail">Special Forms in Detail</h2><p>Special forms are restrictive in their use and do not interact cleanly with several area of Clojure.</p><ul><li><p>Special forms must be a list with a special name as the first element.</p><p>A special name in a higher-order context is not a special form.</p><pre><code class="klipse-clojure nohighlight">do
|
||||
;; CompilerException java.lang.RuntimeException: Unable to resolve symbol: do in this context, compiling:(NO_SOURCE_PATH:0:0)
|
||||
</code></pre><p>Macros have a similar restriction, but notice: the macro's var is identified in the error while
|
||||
special names have no meaning at all outside the first element of a list.</p><pre><code class="klipse-clojure nohighlight">dosync
|
||||
;; CompilerException java.lang.RuntimeException: Can't take value of a macro: #'clojure.core/dosync, compiling:(NO_SOURCE_PATH:0:0)
|
||||
</code></pre></li><li><p>Special form names are not namespace-qualified.</p><p>Most special forms (all except <code>clojure.core/import*</code>) are not namespace
|
||||
qualified. The reader must circumvent syntax quote's policy of namespace-qualifying
|
||||
all symbols.</p><pre><code class="klipse-clojure nohighlight">`a
|
||||
;; user/a
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">`do
|
||||
;; do
|
||||
</code></pre><pre><code class="clojure">user=> `if
|
||||
if
|
||||
user=> `import*
|
||||
user/import*
|
||||
</code></pre></li><li><p>Special forms conflict with local scope.</p><p>Never use special names as local binding or global variable names.</p><pre><code class="clojure">(let [do 1] do)
|
||||
;;; nil
|
||||
</code></pre><p>Ouch!</p><p>This includes destructuring:</p><pre><code class="clojure">user=> (let [{:keys [do]} {:do 1}] do)
|
||||
nil
|
||||
</code></pre><p>Note: Be wary of maps with keyword keys with special names, they are more
|
||||
likely to be destructured this way.</p></li></ul><p>Keep these special cases in mind as you work through the tutorial.</p><h2 id="contributors">Contributors</h2><ul><li>Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a>, 2013 (original author)</li><li>Ambrose Bonnaire-Sergeant <a href="mailto:abonnairesergeant@gmail.com">abonnairesergeant@gmail.com</a>, 2013</li></ul>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../interop/index.html">« Clojure interoperability with Java</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
|
||||
<script>
|
||||
window.klipse_settings = {
|
||||
"selector" : ".klipse-clojure"
|
||||
};
|
||||
</script>
|
||||
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
|
||||
</body>
|
||||
</html>
|
375
clones/clojure-doc.org/articles/language/namespaces/index.html
Normal file
375
clones/clojure-doc.org/articles/language/namespaces/index.html
Normal file
|
@ -0,0 +1,375 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Clojure Namespaces and Vars</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/namespaces/" />
|
||||
<meta property="og:title" content="Clojure Namespaces and Vars" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/namespaces/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Clojure Namespaces and Vars</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>An overview of Clojure namespaces and vars</li><li>How to define namespaces</li><li>How to use functions in other namespaces</li><li><code>require</code>, <code>refer</code> and <code>use</code></li><li>Common compilation errors and typical problems that cause them</li><li>Namespaces and their relation to code compilation in Clojure</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="overview">Overview</h2><p>Clojure functions are organized into <em>namespaces</em>. Clojure namespaces
|
||||
are very similar to Java packages and Python modules. Namespaces are
|
||||
basically maps (dictionaries) that map names to <em>vars</em>. In many cases,
|
||||
those vars store functions in them.</p><h2 id="defining-a-namespace">Defining a Namespace</h2><p>Namespaces are usually defined using the <code>clojure.core/ns</code> macro. In its basic
|
||||
form, it takes a name as a symbol:</p><pre><code class="clojure">(ns superlib.core)
|
||||
</code></pre><p>Namespaces can have multiple segments, separated by a dot:</p><pre><code class="clojure">(ns megacorp.service.core)
|
||||
</code></pre><p>It is <strong>highly recommended</strong> to avoid using single segment namespaces
|
||||
(e.g. <code>superlib</code>) to avoid inconvenient conflicts other developers
|
||||
will have to work around. If a library or application belongs to an
|
||||
organization or a group of projects, the
|
||||
<code>[organization].[library|app].[group-of-functions]</code> pattern is
|
||||
recommended. For example:</p><pre><code class="clojure">(ns clojurewerkz.welle.kv)
|
||||
|
||||
(ns megacorp.search.indexer.core)
|
||||
</code></pre><p>In addition, the <code>ns</code> macro takes a number of optional forms:</p><ul><li><code>(:require ...)</code></li><li><code>(:import ...)</code></li><li><code>(:use ...)</code></li><li><code>(:refer-clojure ...)</code></li><li><code>(:gen-class ...)</code></li></ul><p>These are just slightly more concise variants of <code>clojure.core/import</code>, <code>clojure.core/require</code>, et cetera.</p><h3 id="the-require-helper-form">The :require Helper Form</h3><p>The <code>:require</code> helper form is for setting up access to other Clojure
|
||||
namespaces from your code. For example:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:require clojure.set))
|
||||
|
||||
;; Now it is possible to do:
|
||||
;; (clojure.set/difference #{1 2 3} #{3 4 5})
|
||||
</code></pre><p>This will make sure the <code>clojure.set</code> namespace is loaded, compiled, and available as <code>clojure.set</code>
|
||||
(using its fully qualified name). It is possible (and common) to make a namespace available
|
||||
under an alias:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:require [clojure.set :as cs]))
|
||||
|
||||
;; Now it is possible to do:
|
||||
;; (cs/difference #{1 2 3} #{3 4 5})
|
||||
</code></pre><p>One more example with two required namespaces:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:require [clojure.set :as cs]
|
||||
[clojure.walk :as walk]))
|
||||
</code></pre><h4 id="the-refer-option">The :refer Option</h4><p>To make functions in <code>clojure.set</code> available in the defined namespace via short names
|
||||
(i.e., their unqualified names, without the <code>clojure.set</code> or other prefix), you can tell Clojure compiler
|
||||
to <em>refer</em> to certain functions:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:require [clojure.set :refer [difference intersection]]))
|
||||
|
||||
;; Now it is possible to do:
|
||||
;; (difference #{1 2 3} #{3 4 5})
|
||||
</code></pre><p>The <code>:refer</code> feature of the <code>:require</code> form is new in Clojure 1.4.</p><p>It is possible to refer to all functions in a namespace (usually not necessary):</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:require [clojure.set :refer :all]))
|
||||
|
||||
;; Now it is possible to do:
|
||||
;; (difference #{1 2 3} #{3 4 5})
|
||||
</code></pre><h3 id="the-import-helper-form">The :import Helper Form</h3><p>The <code>:import</code> helper form is for setting up access to Java classes
|
||||
from your Clojure code. For example:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:import java.util.concurrent.Executors))
|
||||
</code></pre><p>This will make sure the <code>java.util.concurrent.Executors</code> class is imported and can be used by its short
|
||||
name, <code>Executors</code>. It is possible to import multiple classes:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:import java.util.concurrent.Executors
|
||||
java.util.concurrent.TimeUnit
|
||||
java.util.Date))
|
||||
</code></pre><p>If multiple imported classes are in the same namespace (like in the example above),
|
||||
it is possible to avoid some duplication by using an <em>import list</em>. The first element
|
||||
of an import list is the package and other elements are class names in that package:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:import [java.util.concurrent Executors TimeUnit]
|
||||
java.util.Date))
|
||||
</code></pre><p>Even though <em>import list</em> is called a list, it can be any Clojure collection (typically
|
||||
vectors are used).</p><h3 id="the-current-namespace">The Current Namespace</h3><p>Under the hood, Clojure keeps <strong>current namespace</strong> a special var, <a href="http://clojuredocs.org/clojure_core/clojure.core/*ns*">*ns*</a>.
|
||||
When vars are defined using the <a href="http://clojuredocs.org/clojure_core/clojure.core/def">def</a> special form, they are
|
||||
added to the current namespace.</p><h3 id="the-refer-clojure-helper-form">The :refer-clojure Helper Form</h3><p>Functions like <code>clojure.core/get</code> and macros like <code>clojure.core/defn</code> can be used without
|
||||
namespace qualification because they reside in the <code>clojure.core</code> namespace and Clojure
|
||||
compiler automatically <em>refers</em> all vars in it. Therefore, if your
|
||||
namespace defines a function with the same name (e.g. <code>find</code>), you will get a warning
|
||||
from the compiler, like this:</p><pre><code>WARNING: find already refers to: #'clojure.core/find in namespace: megacorp.profitd.scheduling, being replaced by: #'megacorp.profitd.scheduling/find
|
||||
</code></pre><p>This means that in the <code>megacorp.profitd.scheduling</code> namespace, <code>find</code> already refers to
|
||||
a value which happens to be <code>clojure.core/find</code>, but it is being replaced by a
|
||||
different value. Remember, Clojure is a very dynamic language and namespaces are
|
||||
basically maps, as far as the implementation goes. Most of the time, however,
|
||||
replacing vars like this is not intentional and Clojure compiler emits a warning.</p><p>To solve this problem, you can either rename your function, or else
|
||||
exclude certain <code>clojure.core</code> functions from being
|
||||
referred using the <code>(:refer-clojure ...)</code> form within the <code>ns</code>:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:refer-clojure :exclude [find]))
|
||||
|
||||
(defn find
|
||||
"Finds a needle in the haystack."
|
||||
[^String haystack]
|
||||
(comment ...))
|
||||
</code></pre><p>In this case, to use <code>clojure.core/find</code>, you will have to use its fully
|
||||
qualified name: <code>clojure.core/find</code>:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling
|
||||
(:refer-clojure :exclude [find]))
|
||||
|
||||
(defn find
|
||||
"Finds a needle in the haystack."
|
||||
[^String haystack]
|
||||
(clojure.core/find haystack :needle))
|
||||
</code></pre><h3 id="the-use-helper-form">The :use Helper Form</h3><p>In Clojure versions before 1.4, there was no <code>:refer</code> support for the
|
||||
<code>(:require ...)</code> form. Instead, a separate form was used: <code>(:use ...)</code>:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling-test
|
||||
(:use clojure.test))
|
||||
</code></pre><p>In the example above, <strong>all</strong> functions in <code>clojure.test</code> are made available
|
||||
in the current namespace. This practice (known as "naked use") works for <code>clojure.test</code> in
|
||||
test namespaces, but in general not a good idea. <code>(:use ...)</code> supports limiting
|
||||
functions that will be referred:</p><pre><code class="clojure">(ns megacorp.profitd.scheduling-test
|
||||
(:use clojure.test :only [deftest testing is]))
|
||||
</code></pre><p>which is a pre-1.4 alternative of</p><pre><code class="clojure">(ns megacorp.profitd.scheduling-test
|
||||
(:require clojure.test :refer [deftest testing is]))
|
||||
</code></pre><p>It is highly recommended to use <code>(:require ...)</code> (optionally with <code>... :refer [...]</code>) on Clojure 1.4
|
||||
and later releases. <code>(:use ...)</code> is a thing of the past and now that
|
||||
<code>(:require ...)</code> with <code>:refer</code> is capable of doing the same thing when you
|
||||
need it, it is a good idea to let <code>(:use ...)</code> go.</p><h3 id="the-gen-class-helper-form">The :gen-class Helper Form</h3><p><em>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></em></p><h3 id="documentation-and-metadata">Documentation and Metadata</h3><p>Namespaces can have documentation strings. You can add one with the optional
|
||||
<code>ns</code> macro parameter:</p><pre><code class="clojure">(ns superlib.core
|
||||
"Core functionality of Superlib.
|
||||
|
||||
Other parts of Superlib depend on functions and macros in this namespace."
|
||||
(:require [clojure.set :refer [union difference]]))
|
||||
</code></pre><p>or metadata:</p><pre><code class="clojure">(ns ^{:doc "Core functionality of Superlib.
|
||||
Other parts of Superlib depend on functions and macros in this namespace."
|
||||
:author "Joe Smith"}
|
||||
superlib.core
|
||||
(:require [clojure.set :refer [union difference]]))
|
||||
</code></pre><p>Metadata can contain any additional keys such as <code>:author</code> which may be of use to various tools
|
||||
(such as <a href="https://clojars.org/codox">Codox</a>, <a href="https://clojars.org/cadastre">Cadastre</a>, or <a href="https://clojars.org/lein-clojuredocs">lein-clojuredocs</a>).</p><h2 id="how-to-use-functions-from-other-namespaces-in-the-repl">How to Use Functions From Other Namespaces in the REPL</h2><p>The <code>ns</code> macro is how you usually require functions from other namespaces.
|
||||
However, it is not very convenient in the REPL. For that case, the <code>clojure.core/require</code> function
|
||||
can be used directly:</p><pre><code class="clojure">;; Will be available as clojure.set, e.g. clojure.set/difference.
|
||||
(require 'clojure.set)
|
||||
|
||||
;; Will be available as io, e.g. io/resource.
|
||||
(require '[clojure.java.io :as io])
|
||||
</code></pre><p>It takes a quoted <em><a href="https://clojure-doc.org/articles/language/namespaces/glossary.html#libspec">libspec</a></em>. The libspec is either a namespace name or
|
||||
a collection (typically a vector) of <code>[name :as alias]</code> or <code>[name :refer [fns]]</code>:</p><pre><code class="clojure">(require '[clojure.set :refer [difference]])
|
||||
|
||||
(difference #{1 2 3} #{3 4 5 6}) ; ⇒ #{1 2}
|
||||
</code></pre><p>The <code>:as</code> and <code>:refer</code> options can be used together:</p><pre><code class="clojure">(require '[clojure.set :as cs :refer [difference]])
|
||||
|
||||
(difference #{1 2 3} #{3 4 5 6}) ; ⇒ #{1 2}
|
||||
(cs/union #{1 2 3} #{3 4 5 6}) ; ⇒ #{1 2 3 4 5 6}
|
||||
</code></pre><p><code>clojure.core/use</code> does the same thing as <code>clojure.core/require</code> but with the
|
||||
<code>:refer</code> option (as discussed above). It is not generally recommended to use <code>use</code> with Clojure
|
||||
versions starting with 1.4. Use <code>clojure.core/require</code> with <code>:refer</code>
|
||||
instead.</p><h2 id="namespaces-and-class-generation">Namespaces and Class Generation</h2><p><em>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></em></p><h2 id="namespaces-and-code-compilation-in-clojure">Namespaces and Code Compilation in Clojure</h2><p>Clojure is a compiled language: code is compiled when it is loaded (usually with <code>clojure.core/require</code>).</p><p>A namespace can contain vars or be used purely to extend protocols, add multimethod implementations,
|
||||
or conditionally load other libraries (e.g. the most suitable JSON parser or key/value store implementation).
|
||||
In all cases, to trigger compilation, you need to require the namespace.</p><h2 id="private-vars">Private Vars</h2><p>Vars (and, in turn, functions defined with <code>defn</code>) can be private. There are two equivalent ways to
|
||||
specify that a function is private: either via metadata or by using the <code>defn-</code> macro:</p><pre><code class="clojure">(ns megacorp.superlib)
|
||||
|
||||
;;
|
||||
;; Implementation
|
||||
;;
|
||||
|
||||
(def ^{:private true}
|
||||
source-name "supersource")
|
||||
|
||||
(defn- data-stream
|
||||
[source]
|
||||
(comment ...))
|
||||
</code></pre><h2 id="constant-vars">Constant Vars</h2><p>Vars can be constant. This is done by setting the <code>:const</code> metadata key to <code>true</code>. This
|
||||
will cause Clojure compiler to compile it as a constant:</p><pre><code class="clojure">(ns megacorp.epicgame)
|
||||
|
||||
;;
|
||||
;; Implementation
|
||||
;;
|
||||
|
||||
(def ^{:const true}
|
||||
default-score 100)
|
||||
</code></pre><h2 id="how-to-look-up-and-invoke-a-function-by-name">How to Look up and Invoke a Function by Name</h2><p>It is possible to look up a function in particular namespace by-name with <code>clojure.core/ns-resolve</code>. This takes
|
||||
quoted names of the namespace and function. The returned value can be used just like any other
|
||||
function, for example, passed as an argument to a higher order function:</p><pre><code class="clojure">(ns-resolve 'clojure.set 'difference) ; ⇒ #'clojure.set/difference
|
||||
|
||||
(let [f (ns-resolve 'clojure.set 'difference)]
|
||||
(f #{1 2 3} #{3 4 5 6})) ; ⇒ #{1 2}
|
||||
</code></pre><h2 id="compiler-exceptions">Compiler Exceptions</h2><p>This section describes some common compilation errors.</p><h3 id="classnotfoundexception">ClassNotFoundException</h3><p>This exception means that JVM could not load a class. It is either misspelled or not on the
|
||||
<a href="https://clojure-doc.org/articles/language/namespaces/glossary.html#classpath">classpath</a>.
|
||||
Potentially your project has unsatisfied dependency (some dependencies may be optional).</p><p>Example:</p><pre><code class="clojure">user=> (import java.uyil.concurrent.TimeUnit)
|
||||
ClassNotFoundException java.uyil.concurrent.TimeUnit java.net.URLClassLoader$1.run (URLClassLoader.java:366)
|
||||
</code></pre><p>In the example above, <code>java.uyil.concurrent.TimeUnit</code> should have been <code>java.util.concurrent.TimeUnit</code>.</p><h3 id="compilerexception-javalangruntimeexception-no-such-var">CompilerException java.lang.RuntimeException: No such var</h3><p>This means that somewhere in the code a non-existent var is used. It may be a typo, an
|
||||
incorrect macro-generated var name or a similar issue. Example:</p><pre><code class="clojure">user=> (clojure.java.io/resouce "thought_leaders_quotes.csv")
|
||||
CompilerException java.lang.RuntimeException: No such var: clojure.java.io/resouce, compiling:(NO_SOURCE_PATH:1)
|
||||
</code></pre><p>In the example above, <code>clojure.java.io/resouce</code> should have been <code>clojure.java.io/resource</code>. <code>NO_SOURCE_PATH</code>
|
||||
means that compilation was triggered from the REPL and not a Clojure source file.</p><h2 id="temporarily-overriding-vars-in-namespaces">Temporarily Overriding Vars in Namespaces</h2><p><em>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></em></p><h2 id="getting-information-about-and-programmatically-manipulating-namespaces">Getting Information About and Programmatically Manipulating Namespaces</h2><p><em>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></em></p><h2 id="wrapping-up">Wrapping Up</h2><p>Namespaces are basically maps (dictionaries) that map names to
|
||||
vars. In many cases, those vars store functions in them.</p><p>This implementation lets Clojure have many of its highly dynamic
|
||||
features at a very reasonable runtime overhead cost. For example, vars
|
||||
in namespaces can be temporarily altered for unit testing purposes.</p><h2 id="contributors">Contributors</h2><p>Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a> (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../core_overview/index.html">« Overview of clojure.core, the standard Clojure library</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
385
clones/clojure-doc.org/articles/language/polymorphism/index.html
Normal file
385
clones/clojure-doc.org/articles/language/polymorphism/index.html
Normal file
|
@ -0,0 +1,385 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Polymorphism in Clojure: Protocols and Multimethods</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/polymorphism/" />
|
||||
<meta property="og:title" content="Polymorphism in Clojure: Protocols and Multimethods" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/polymorphism/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Polymorphism in Clojure: Protocols and Multimethods</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>What are polymorphic functions</li><li>Type-based polymorphism with protocols</li><li>Ad-hoc polymorphism with multimethods</li><li>How to create your own data types that behave like core Clojure data types</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="overview">Overview</h2><p>According to Wikipedia,</p><blockquote><p>In computer science, polymorphism is a programming language feature that allows values of different data types to be handled using a uniform interface.</p></blockquote><p>Polymorphism is not at all unique to object-oriented programming languages. Clojure has excellent support for
|
||||
polymorphism.</p><p>For example, when a function can be used on multiple data types or behave differently based on additional argument
|
||||
(often called <em>dispatch value</em>), that function is <em>polymorphic</em>. A simple example of such function is a function that
|
||||
serializes its input to JSON (or other format).</p><p>Ideally, developers would like to use the same function regardless of the input, and be able to extend
|
||||
it to new inputs, without having to change the original source. Inability to do so is known as the <a href="http://en.wikipedia.org/wiki/Expression_problem">Expression Problem</a>.</p><p>In Clojure, there are two approaches to polymorphism:</p><ul><li>Data type-oriented. More efficient (modern JVMs optimize this case very well), less flexible.</li><li>So called "ad-hoc polymorphism" where the exact function implementation is picked at runtime based on a special argument (<em>dispatch value</em>).</li></ul><p>The former is implemented using <em>protocols</em>, a feature first introduced in Clojure 1.2. The latter is available via
|
||||
<em>multimethods</em>, a feature that was around in Clojure since the early days.</p><h2 id="type-based-polymorphism-with-protocols">Type-based Polymorphism With Protocols</h2><p>It is common for polymorphic functions to <em>dispatch</em> (pick implementation) on the type of the first argument. For example,
|
||||
in Java or Ruby, when calling <code>#toString</code> or <code>#to_s</code> on an object, the exact implementation is located using that object's
|
||||
type.</p><p>Because this is a common case and because JVM can optimize this dispatch logic very well, Clojure 1.2 introduced a new
|
||||
feature called <em>protocols</em>. Protocols are simply groups of functions. Each of the functions can have different
|
||||
implementations for different data types.</p><p>Protocols are defined using the <code>clojure.core/defprotocol</code> special form. The example below defines a protocol for working with URLs and URIs.
|
||||
While URLs and URIs are not the same thing, some operations make sense for both:</p><pre><code class="clojure">(defprotocol URLLike
|
||||
"Unifies operations on URLs and URIs"
|
||||
(^String protocol-of [input] "Returns protocol of given input")
|
||||
(^String host-of [input] "Returns host of given input")
|
||||
(^String port-of [input] "Returns port of given input")
|
||||
(^String user-info-of [input] "Returns user information of given input")
|
||||
(^String path-of [input] "Returns path of given input")
|
||||
(^String query-of [input] "Returns query string of given input")
|
||||
(^String fragment-of [input] "Returns fragment of given input"))
|
||||
</code></pre><p><code>clojure.core/defprotocol</code> takes the name of the protocol and one or more lists of
|
||||
<strong>function name</strong>, <strong>argument list</strong>, <strong>documentation string</strong>:</p><pre><code class="clojure">(^String protocol-of [input] "Returns protocol of given input")
|
||||
(^String host-of [input] "Returns host of given input")
|
||||
</code></pre><p>The example above uses return type hints. This makes sense in the example but is not necessary. It could have been written
|
||||
it as</p><pre><code class="clojure">(defprotocol URLLike
|
||||
"Unifies operations on URLs and URIs"
|
||||
(protocol-of [input] "Returns protocol of given input")
|
||||
(host-of [input] "Returns hostname of given input")
|
||||
(port-of [input] "Returns port of given input")
|
||||
(user-info-of [input] "Returns user information (username:password) of given input")
|
||||
(path-of [input] "Returns path of given input")
|
||||
(query-of [input] "Returns query string of given input")
|
||||
(fragment-of [input] "Returns fragment of given input"))
|
||||
</code></pre><p>There are 3 ways URIs and URLs are commonly represented on the JVM:</p><ul><li><code>java.net.URI</code> instances</li><li><code>java.net.URL</code> instances</li><li>Strings</li></ul><p>When a new protocol imlementation is added for a type, it is called <strong>extending the protocol</strong>. The most common way to extend
|
||||
a protocol is via the <code>clojure.core/extend-protocol</code>:</p><pre><code class="clojure">(import java.net.URI)
|
||||
(import java.net.URL)
|
||||
|
||||
(extend-protocol URLLike
|
||||
URI
|
||||
(protocol-of [^URI input]
|
||||
(when-let [s (.getScheme input)]
|
||||
(.toLowerCase s)))
|
||||
(host-of [^URI input]
|
||||
(-> input .getHost .toLowerCase))
|
||||
(port-of [^URI input]
|
||||
(.getPort input))
|
||||
(user-info-of [^URI input]
|
||||
(.getUserInfo input))
|
||||
(path-of [^URI input]
|
||||
(.getPath input))
|
||||
(query-of [^URI input]
|
||||
(.getQuery input))
|
||||
(fragment-of [^URI input]
|
||||
(.getFragment input))
|
||||
|
||||
URL
|
||||
(protocol-of [^URL input]
|
||||
(protocol-of (.toURI input)))
|
||||
(host-of [^URL input]
|
||||
(host-of (.toURI input)))
|
||||
(port-of [^URL input]
|
||||
(.getPort input))
|
||||
(user-info-of [^URL input]
|
||||
(.getUserInfo input))
|
||||
(path-of [^URL input]
|
||||
(.getPath input))
|
||||
(query-of [^URL input]
|
||||
(.getQuery input))
|
||||
(fragment-of [^URL input]
|
||||
(.getRef input)))
|
||||
</code></pre><p>Protocol functions are used just like regular Clojure functions:</p><pre><code class="clojure">(protocol-of (URI. "https://clojure-doc.github.io")) ;= "http"
|
||||
(protocol-of (URL. "https://clojure-doc.github.io")) ;= "http"
|
||||
|
||||
(path-of (URL. "https://clojure-doc.github.io/articles/content.html")) ;= "/articles/content/"
|
||||
(path-of (URI. "https://clojure-doc.github.io/articles/content.html")) ;= "/articles/content/"
|
||||
</code></pre><h3 id="using-protocols-from-different-namespaces">Using Protocols From Different Namespaces</h3><p>Protocol functions are required and used the same way as regular protocol functions. Consider a
|
||||
namespace that looks like this</p><pre><code class="clojure">(ns superlib.url-like
|
||||
(:import [java.net URL URI]))
|
||||
|
||||
(defprotocol URLLike
|
||||
"Unifies operations on URLs and URIs"
|
||||
(^String protocol-of [input] "Returns protocol of given input")
|
||||
(^String host-of [input] "Returns host of given input")
|
||||
(^String port-of [input] "Returns port of given input")
|
||||
(^String user-info-of [input] "Returns user information of given input")
|
||||
(^String path-of [input] "Returns path of given input")
|
||||
(^String query-of [input] "Returns query string of given input")
|
||||
(^String fragment-of [input] "Returns fragment of given input"))
|
||||
|
||||
(extend-protocol URLLike
|
||||
URI
|
||||
(protocol-of [^URI input]
|
||||
(when-let [s (.getScheme input)]
|
||||
(.toLowerCase s)))
|
||||
(host-of [^URI input]
|
||||
(-> input .getHost .toLowerCase))
|
||||
(port-of [^URI input]
|
||||
(.getPort input))
|
||||
(user-info-of [^URI input]
|
||||
(.getUserInfo input))
|
||||
(path-of [^URI input]
|
||||
(.getPath input))
|
||||
(query-of [^URI input]
|
||||
(.getQuery input))
|
||||
(fragment-of [^URI input]
|
||||
(.getFragment input))
|
||||
|
||||
URL
|
||||
(protocol-of [^URL input]
|
||||
(protocol-of (.toURI input)))
|
||||
(host-of [^URL input]
|
||||
(host-of (.toURI input)))
|
||||
(port-of [^URL input]
|
||||
(.getPort input))
|
||||
(user-info-of [^URL input]
|
||||
(.getUserInfo input))
|
||||
(path-of [^URL input]
|
||||
(.getPath input))
|
||||
(query-of [^URL input]
|
||||
(.getQuery input))
|
||||
(fragment-of [^URL input]
|
||||
(.getRef input)))
|
||||
</code></pre><p>To use <code>superlib.url-like/path-of</code> and other functions, you require them as regular functions:</p><pre><code class="clojure">(ns myapp
|
||||
(:require [superlib.url-like] :refer [host-of scheme-of]))
|
||||
|
||||
(host-of (java.net.URI. "https://twitter.com/cnn/"))
|
||||
</code></pre><h3 id="extending-protocols-for-core-clojure-data-types">Extending Protocols For Core Clojure Data Types</h3><p>TBD</p><h3 id="protocols-and-custom-data-types">Protocols and Custom Data Types</h3><p>TBD: cover extend-type, extend</p><h3 id="partial-implementation-of-protocols">Partial Implementation of Protocols</h3><p>With protocols, it is possible to only implement certain functions for certain types.</p><h2 id="ad-hoc-polymorphism-with-multimethods">Ad-hoc Polymorphism with Multimethods</h2><h3 id="first-example-shapes">First Example: Shapes</h3><p>Lets start with a simple problem definition. We have 3 shapes: square, circle and triangle, and
|
||||
need to provide an polymorphic function that calculates the area of the given shape.</p><p>In total, we need 4 functions:</p><ul><li>A function that calculates area of a square</li><li>A function that calculates area of a circle</li><li>A function that calculates area of a triangle</li><li>A polymorphic function that acts as a "unified frontend" to the functions above</li></ul><p>we will start with the latter and define a <em>multimethod</em> (not related to methods on Java objects or object-oriented programming):</p><pre><code class="klipse-clojure nohighlight">(defmulti area (fn [shape & _]
|
||||
shape))
|
||||
</code></pre><p>Our multimethod has a name and a <em>dispatch function</em> that takes arguments passed to the multimethod and returns
|
||||
a value. The returned value will define what implementation of multimethod is used. In Java or Ruby, method implementation
|
||||
is picked by traversing the class hierarchy. With multimethods, the logic can be anything you need. That's why it is
|
||||
called <em>ad-hoc polymorphism</em>.</p><p>An alternative way of doing the same thing is to pass <code>clojure.core/first</code> instead of an anonymous function:</p><pre><code class="klipse-clojure nohighlight">(defmulti area first)
|
||||
</code></pre><p>Next lets implement our area multimethod for squares:</p><pre><code class="klipse-clojure nohighlight">(defmethod area :square
|
||||
[_ side]
|
||||
(* side side))
|
||||
</code></pre><p>Here <code>defmethod</code> defines a particular implementation of the multimethod <code>area</code>, the one that will be used if dispatch function
|
||||
returns <code>:square</code>. Lets try it out. Multimethods are invoked like regular Clojure functions:</p><pre><code class="klipse-clojure nohighlight">(area :square 4)
|
||||
;= 16
|
||||
</code></pre><p>In this case, we pass dispatch value as the first argument, our dispatch function returns it unmodified and
|
||||
that's how the exact implementation is looked up.</p><p>Implementation for circles looks very similar, we choose <code>:circle</code> as a reasonable dispatch value:</p><pre><code class="klipse-clojure nohighlight">(defmethod area :circle
|
||||
[_ radius]
|
||||
(* radius radius Math/PI))
|
||||
|
||||
(area :circle 3)
|
||||
;= 28.274333882308138
|
||||
</code></pre><p>For the record, <code>Math/PI</code> in this example refers to <code>java.lang.Math/PI</code>, a field that stores the value of Pi.</p><p>Finally, an implementation for triangles. Here you can see that exact implementations can take different number of
|
||||
arguments. To calculate the area of a triangle, we multiple base by height and divide it by 2:</p><pre><code class="klipse-clojure nohighlight">(defmethod area :triangle
|
||||
[_ b h]
|
||||
(* 1/2 b h))
|
||||
|
||||
(area :triangle 3 5)
|
||||
;= 15/2
|
||||
</code></pre><p>In this example we used <strong>Clojure ratio</strong> data type. We could have used doubles as well.</p><p>Putting it all together:</p><pre><code class="klipse-clojure nohighlight">(defmulti area (fn [shape & _]
|
||||
shape))
|
||||
|
||||
(defmethod area :square
|
||||
[_ side]
|
||||
(* side side))
|
||||
|
||||
(defmethod area :circle
|
||||
[_ radius]
|
||||
(* radius radius Math/PI))
|
||||
|
||||
(defmethod area :triangle
|
||||
[_ b h]
|
||||
(* 1/2 b h))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(area :square 4)
|
||||
;= 16
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(area :circle 3)
|
||||
;= 28.274333882308138
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(area :triangle 3 5)
|
||||
;= 15/2
|
||||
</code></pre><h3 id="second-example-tbd">Second Example: TBD</h3><p>TBD: an example that demonstrates deriving</p><h2 id="how-to-create-custom-data-type-that-core-functions-can-work-with">How To Create Custom Data Type That Core Functions Can Work With</h2><p>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></p><h2 id="wrapping-up">Wrapping Up</h2><p>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../macros/index.html">« Clojure Macros and Metaprogramming</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
|
||||
<script>
|
||||
window.klipse_settings = {
|
||||
"selector" : ".klipse-clojure"
|
||||
};
|
||||
</script>
|
||||
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,476 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Basic Web Development</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers building a simple web-application using common
|
||||
Clojure libraries. When you're done working through it, you'll have a
|
||||
little webapp that displays some (x, y) locations from a database,
|
||||
letting you add more locations as well.It's assumed that you're already somewhat familiar with Clojure. If
|
||||
not, see the Getting Started and
|
||||
Introduction guides.">
|
||||
|
||||
<meta property="og:description" content="This guide covers building a simple web-application using common
|
||||
Clojure libraries. When you're done working through it, you'll have a
|
||||
little webapp that displays some (x, y) locations from a database,
|
||||
letting you add more locations as well.It's assumed that you're already somewhat familiar with Clojure. If
|
||||
not, see the Getting Started and
|
||||
Introduction guides.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/tutorials/basic_web_development/" />
|
||||
<meta property="og:title" content="Basic Web Development" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/tutorials/basic_web_development/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Basic Web Development</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers building a simple web-application using common
|
||||
Clojure libraries. When you're done working through it, you'll have a
|
||||
little webapp that displays some (x, y) locations from a database,
|
||||
letting you add more locations as well.</p><p>It's assumed that you're already somewhat familiar with Clojure. If
|
||||
not, see the <a href="https://clojure-doc.org/articles/tutorials/basic_web_development/getting_started/">Getting Started</a> and
|
||||
<a href="https://clojure-doc.org/articles/tutorials/basic_web_development/introduction/">Introduction</a> guides.</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><p>This guide uses Clojure 1.5, as well as current versions of the
|
||||
component libraries noted below.</p><h2 id="conceptual-overview-of-components">Conceptual Overview of Components</h2><p>We'll use four major components (briefly described below) for our
|
||||
little webapp:</p><ul><li>Ring</li><li>Compojure</li><li>Hiccup</li><li>H2</li></ul><h3 id="ring">Ring</h3><p><a href="https://github.com/ring-clojure/ring">Ring</a> (<a href="https://clojars.org/ring">at
|
||||
clojars</a>) is a foundational Clojure web
|
||||
application library. It:</p><ul><li>sets things up such that an http request comes into your webapp
|
||||
as a regular Clojure hashmap, and likewise makes it so that you
|
||||
can return a response as a hashmap.</li><li>provides <a href="https://github.com/ring-clojure/ring/blob/master/SPEC">a
|
||||
spec</a>
|
||||
describing exactly what those request and response maps should
|
||||
look like.</li><li>brings along a web server
|
||||
(<a href="http://www.eclipse.org/jetty/">Jetty</a>) and connects your
|
||||
webapp to it.</li></ul><p>For this tutorial, we won't actually need to deal with these maps
|
||||
by-hand, as you'll soon see.</p><p>For more info, see:</p><ul><li><a href="https://github.com/ring-clojure/ring#readme">the Ring readme</a></li><li><a href="https://github.com/ring-clojure/ring/wiki">its wiki docs</a></li><li><a href="http://ring-clojure.github.com/ring/">its API docs</a></li></ul><h3 id="compojure">Compojure</h3><p>If we were using only Ring, we'd have to write one single function to
|
||||
take that incoming request map and then delegate to various functions
|
||||
depending upon which page was requested.
|
||||
<a href="https://github.com/weavejester/compojure">Compojure</a> (<a href="https://clojars.org/compojure">at
|
||||
clojars</a>) provides some handy features
|
||||
to take care of this for us such that we can associate url paths with
|
||||
corresponding functions, all in one place.</p><p>For more info, see:</p><ul><li><a href="https://github.com/weavejester/compojure#readme">the Compojure readme</a></li><li><a href="https://github.com/weavejester/compojure/wiki">its wiki docs</a></li><li><a href="http://weavejester.github.com/compojure/">its API docs</a></li></ul><h3 id="hiccup">Hiccup</h3><p><a href="https://github.com/weavejester/hiccup">Hiccup</a> (<a href="https://clojars.org/hiccup">at
|
||||
clojars</a>) provides a quick and easy way to
|
||||
generate html. It converts regular Clojure data structures right into
|
||||
html. For example,</p><pre><code class="clojure">[:p "Hello, " [:i "doctor"] " Jones."]
|
||||
</code></pre><p>becomes</p><pre><code class="html"><p>Hello, <i>doctor</i> Jones.</p>
|
||||
</code></pre><p>but it also does two extra handy bits of magic:</p><ul><li><p>it provides some CSS-like shortcuts for specifying id and class,
|
||||
and</p></li><li><p>it automatically unpacks seqs for you, for example:</p><pre><code class="clojure">[:p '("a" "b" "c")]
|
||||
;; expands to (and so, is the same as if you wrote)
|
||||
[:p "a" "b" "c"]
|
||||
</code></pre></li></ul><p>For more info, see:</p><ul><li><a href="https://github.com/weavejester/hiccup#readme">the Hiccup readme</a></li><li><a href="https://github.com/weavejester/hiccup/wiki">its wiki docs</a></li><li><a href="http://weavejester.github.com/hiccup/">its API docs</a></li></ul><h3 id="h2">H2</h3><p><a href="http://www.h2database.com/html/main.html">H2</a> is a small and fast Java SQL
|
||||
database that could be embedded in your application or run in server
|
||||
mode. Uses single file for storage, but also could be run as in-memory DB.</p><blockquote><p>Another similar Java-based embedded DB that could be used in your
|
||||
application is <a href="http://db.apache.org/derby/">Apache Derby</a>.</p></blockquote><h2 id="create-and-set-up-your-project">Create and set up your project</h2><p>Create your new webapp project like so:</p><pre><code class="bash">lein new compojure my-webapp
|
||||
cd my-webapp
|
||||
</code></pre><p>Add the following extra dependencies to your project.clj's
|
||||
<code>:dependencies</code> vector:</p><pre><code class="clojure">[hiccup "1.0.5"]
|
||||
[org.clojure/java.jdbc "0.6.0"]
|
||||
[com.h2database/h2 "1.4.193"]
|
||||
</code></pre><p>(You might also remove the <code>-SNAPSHOT</code> from the project's version
|
||||
string.)</p><h2 id="add-some-styling">Add some styling</h2><pre><code class="bash">mkdir -p resources/public/css
|
||||
touch resources/public/css/styles.css
|
||||
</code></pre><p>and put into that file something like:</p><pre><code class="css">body {
|
||||
background-color: Cornsilk;
|
||||
}
|
||||
|
||||
#header-links {
|
||||
background-color: BurlyWood;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: CornflowerBlue;
|
||||
}
|
||||
</code></pre><h2 id="set-up-your-database">Set up your database</h2><p>A file with DB would be automatically created when you connect to it for the
|
||||
first time, so all necessary DB preparations could be done programmatically
|
||||
using the REPL (with help of <code>clojure.java.jdbc</code>):</p><pre><code class="bash">lein repl
|
||||
</code></pre><p>Execute the following code to create a new my-webapp.h2.db database file in db
|
||||
subdirectory of your project, create a table we'll use for our webapp, and add
|
||||
one record to start us off with:</p><pre><code class="clojure">(require '[clojure.java.jdbc :as jdbc])
|
||||
(jdbc/with-db-connection [conn {:dbtype "h2" :dbname "./my-webapp"}]
|
||||
|
||||
(jdbc/db-do-commands conn
|
||||
(jdbc/create-table-ddl :locations
|
||||
[[:id "bigint primary key auto_increment"]
|
||||
[:x "integer"]
|
||||
[:y "integer"]]))
|
||||
|
||||
(jdbc/insert! conn :locations
|
||||
{:x 8 :y 9}))
|
||||
</code></pre><p>and hit <code>ctrl-d</code> to exit.</p><p>For more about how to use the database functions, see the
|
||||
<a href="../../ecosystem/java_jdbc/home.html">Using java.jdbc</a> on this site.</p><h2 id="set-up-your-routes">Set up your routes</h2><p>In the default <code>src/my_webapp/handler.clj</code> file you're provided, we
|
||||
specify our webapp's <em>routes</em> inside the <code>defroutes</code> macro. That is,
|
||||
we assign a function to handle each of the url paths we'd like to
|
||||
support, and then at the end provide a "not found" page for any other
|
||||
url paths.</p><p>Make your handler.clj file look like this:</p><pre><code class="clojure">(ns my-webapp.handler
|
||||
(:require [my-webapp.views :as views] ; add this require
|
||||
[compojure.core :refer :all]
|
||||
[compojure.route :as route]
|
||||
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]))
|
||||
|
||||
(defroutes app-routes ; replace the generated app-routes with this
|
||||
(GET "/"
|
||||
[]
|
||||
(views/home-page))
|
||||
(GET "/add-location"
|
||||
[]
|
||||
(views/add-location-page))
|
||||
(POST "/add-location"
|
||||
{params :params}
|
||||
(views/add-location-results-page params))
|
||||
(GET "/location/:loc-id"
|
||||
[loc-id]
|
||||
(views/location-page loc-id))
|
||||
(GET "/all-locations"
|
||||
[]
|
||||
(views/all-locations-page))
|
||||
(route/resources "/")
|
||||
(route/not-found "Not Found"))
|
||||
|
||||
(def app
|
||||
(wrap-defaults app-routes site-defaults))
|
||||
</code></pre><p>Each of those expressions in <code>defroutes</code> like <code>(GET ...)</code> or <code>(POST ...)</code> are
|
||||
so-called "routes". They each evaluate to a function that
|
||||
takes a ring request hashmap and returns a response hashmap. Your
|
||||
<code>views/foo</code> function's job is to return that response hashmap, but note
|
||||
that Compojure is kind enough to make a suitable response map out of
|
||||
any html you return.</p><p>So, all you actually need to do now is write your views functions to
|
||||
return some html.</p><p>Incidentally, note the special destructuring that Compojure does for
|
||||
you in each of those routes. It can pull out url query (and body)
|
||||
parameters, as well as pieces of the url path requested, and hand them
|
||||
to your views functions. Read more about that at <a href="https://github.com/weavejester/compojure/wiki/Destructuring-Syntax">Compojure
|
||||
destructuring</a>.</p><h2 id="create-your-views">Create your Views</h2><p>Create a <code>src/my_webapp/views.clj</code> file and make it look like:</p><pre><code class="clojure">(ns my-webapp.views
|
||||
(:require [my-webapp.db :as db]
|
||||
[clojure.string :as str]
|
||||
[hiccup.page :as page]
|
||||
[ring.util.anti-forgery :as util]))
|
||||
|
||||
(defn gen-page-head
|
||||
[title]
|
||||
[:head
|
||||
[:title (str "Locations: " title)]
|
||||
(page/include-css "/css/styles.css")])
|
||||
|
||||
(def header-links
|
||||
[:div#header-links
|
||||
"[ "
|
||||
[:a {:href "/"} "Home"]
|
||||
" | "
|
||||
[:a {:href "/add-location"} "Add a Location"]
|
||||
" | "
|
||||
[:a {:href "/all-locations"} "View All Locations"]
|
||||
" ]"])
|
||||
|
||||
(defn home-page
|
||||
[]
|
||||
(page/html5
|
||||
(gen-page-head "Home")
|
||||
header-links
|
||||
[:h1 "Home"]
|
||||
[:p "Webapp to store and display some 2D (x,y) locations."]))
|
||||
|
||||
(defn add-location-page
|
||||
[]
|
||||
(page/html5
|
||||
(gen-page-head "Add a Location")
|
||||
header-links
|
||||
[:h1 "Add a Location"]
|
||||
[:form {:action "/add-location" :method "POST"}
|
||||
(util/anti-forgery-field) ; prevents cross-site scripting attacks
|
||||
[:p "x value: " [:input {:type "text" :name "x"}]]
|
||||
[:p "y value: " [:input {:type "text" :name "y"}]]
|
||||
[:p [:input {:type "submit" :value "submit location"}]]]))
|
||||
|
||||
(defn add-location-results-page
|
||||
[{:keys [x y]}]
|
||||
(let [id (db/add-location-to-db x y)]
|
||||
(page/html5
|
||||
(gen-page-head "Added a Location")
|
||||
header-links
|
||||
[:h1 "Added a Location"]
|
||||
[:p "Added [" x ", " y "] (id: " id ") to the db. "
|
||||
[:a {:href (str "/location/" id)} "See for yourself"]
|
||||
"."])))
|
||||
|
||||
(defn location-page
|
||||
[loc-id]
|
||||
(let [{x :x y :y} (db/get-xy loc-id)]
|
||||
(page/html5
|
||||
(gen-page-head (str "Location " loc-id))
|
||||
header-links
|
||||
[:h1 "A Single Location"]
|
||||
[:p "id: " loc-id]
|
||||
[:p "x: " x]
|
||||
[:p "y: " y])))
|
||||
|
||||
(defn all-locations-page
|
||||
[]
|
||||
(let [all-locs (db/get-all-locations)]
|
||||
(page/html5
|
||||
(gen-page-head "All Locations in the db")
|
||||
header-links
|
||||
[:h1 "All Locations"]
|
||||
[:table
|
||||
[:tr [:th "id"] [:th "x"] [:th "y"]]
|
||||
(for [loc all-locs]
|
||||
[:tr [:td (:id loc)] [:td (:x loc)] [:td (:y loc)]])])))
|
||||
</code></pre><p>Here we've implemented each function used in <code>handler.clj</code>.</p><p>Again, note that each of the functions with names ending in "-page"
|
||||
(the ones being called in <code>handler.clj</code>) is returning just a plain
|
||||
string consisting of html markup. In handler.clj's <code>defroutes</code>,
|
||||
Compojure is helpfully taking care of placing that into a response
|
||||
hashmap for us.</p><p>Rather than clog up this file with database-related calls, we've put
|
||||
them all into their own <code>db.clj</code> file (described next).</p><h2 id="create-some-db-access-functions">Create some db access functions</h2><p>Create a <code>src/my_webapp/db.clj</code> file and make it look like:</p><pre><code class="clojure">(ns my-webapp.db
|
||||
(:require [clojure.java.jdbc :as jdbc]))
|
||||
|
||||
(def db-spec {:dbtype "h2" :dbname "./my-webapp"})
|
||||
|
||||
(defn add-location-to-db
|
||||
[x y]
|
||||
(let [results (jdbc/insert! db-spec :locations {:x x :y y})]
|
||||
(assert (= (count results) 1))
|
||||
(first (vals (first results)))))
|
||||
|
||||
(defn get-xy
|
||||
[loc-id]
|
||||
(let [results (jdbc/query db-spec
|
||||
["select x, y from locations where id = ?" loc-id])]
|
||||
(assert (= (count results) 1))
|
||||
(first results)))
|
||||
|
||||
(defn get-all-locations
|
||||
[]
|
||||
(jdbc/query db-spec "select id, x, y from locations"))
|
||||
</code></pre><p>Note that <code>jdbc/query</code> returns a seq of maps. Each map
|
||||
entry's key is a column name (as a Clojure keyword), and its value is
|
||||
the value for that column.</p><p>You'll also notice that we used a plain string in <code>get-all-locations</code>,
|
||||
rather than putting it in a vector. <code>java.jdbc</code> allows us to omit the vector
|
||||
wrapping when we have a simple SQL query with no parameters.</p><p>Of course, you can try out all these calls yourself in the REPL,
|
||||
if you like:</p><pre><code>~/temp/my-webapp$ lein repl
|
||||
...
|
||||
user=> (require 'my-webapp.db)
|
||||
nil
|
||||
user=> (ns my-webapp.db)
|
||||
nil
|
||||
my-webapp.db=> (jdbc/query db-spec
|
||||
#_=> "select x, y from locations where id = 1")
|
||||
({:y 9, :x 8})
|
||||
</code></pre><h2 id="run-your-webapp-during-development">Run your webapp during development</h2><p>You can run your webapp via lein:</p><pre><code>lein ring server
|
||||
</code></pre><p>It should start up and also open a browser window for you pointed at
|
||||
<a href="http://localhost:3000">http://localhost:3000</a>. You should be able to stop the webapp by
|
||||
hitting <code>ctrl-c</code>.</p><p>If you don't want it to automatically open a
|
||||
browser window, run it like so:</p><pre><code>lein ring server-headless
|
||||
</code></pre><h2 id="deploy-your-webapp">Deploy your webapp</h2><p>To make your webapp suitable for deployment, make the following
|
||||
changes:</p><h3 id="changes-in-projectclj">Changes in project.clj</h3><p>In your <code>project.clj</code> file:</p><ul><li><p>add to <code>:dependencies</code> (the version should generally match <code>compojure</code>s version):</p><pre><code class="clojure">[ring/ring-jetty-adapter "1.5.1"] ; e.g., for compojure version 1.5.1
|
||||
</code></pre></li><li><p>and also add <code>:main my-webapp.handler</code></p></li></ul><h3 id="changes-in-handlerclj">Changes in handler.clj</h3><p>In <code>src/my_webapp/handler.clj</code>:</p><ul><li>in your <code>ns</code> macro:
|
||||
<ul><li>add <code>[ring.adapter.jetty :as jetty]</code> to the <code>:require</code>, and</li><li>add <code>(:gen-class)</code> to the end</li></ul></li></ul><p>The <code>ns</code> form should now look like this:</p><pre><code class="clojure">(ns my-webapp.handler
|
||||
(:require [my-webapp.views :as views]
|
||||
[compojure.core :refer :all]
|
||||
[compojure.route :as route]
|
||||
[ring.adapter.jetty :as jetty] ; add this require
|
||||
[ring.middleware.defaults :refer [wrap-defaults site-defaults]])
|
||||
(:gen-class)) ; and add this gen-class
|
||||
</code></pre><ul><li><p>and at the bottom, add the following <code>-main</code> function:</p><pre><code class="clojure">(defn -main
|
||||
[& [port]]
|
||||
(let [port (Integer. (or port
|
||||
(System/getenv "PORT")
|
||||
5000))]
|
||||
(jetty/run-jetty #'app {:port port
|
||||
:join? false})))
|
||||
</code></pre></li></ul><h3 id="build-and-run-it">Build and Run it</h3><p>Now create an uberjar of your webapp:</p><pre><code>lein uberjar
|
||||
</code></pre><p>And now you can run it directly:</p><pre><code>java -jar target/my-webapp-0.1.0-standalone.jar 8080
|
||||
</code></pre><p>(or on whatever port number you wish). If you run the JAR file from another
|
||||
folder, remember to copy the <code>my-webapp.mv.db</code> file to that folder!</p><p><em>NOTE: if you did not remove "-SNAPSHOT" from the project's version string
|
||||
when you first edited <code>project.clj</code>, then the JAR file will have <code>-SNAPSHOT</code>
|
||||
in its name.</em></p><h2 id="see-also">See Also</h2><ul><li>To get a head start with a more "batteries-included" project
|
||||
template, see <a href="http://www.luminusweb.net/">Luminus</a>.</li></ul><h2 id="contributors">Contributors</h2><p>John Gabriele <a href="mailto:jmg3000@gmail.com">jmg3000@gmail.com</a> (original author)</p><p>Ivan Kryvoruchko <a href="mailto:gildraug@gmail.com">gildraug@gmail.com</a></p><p>Sean Corfield <a href="mailto:sean@corfield.org">sean@corfield.org</a></p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../eclipse/index.html">« Starting with Eclipse and Counterclockwise For Clojure Development</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../parsing_xml_with_zippers/index.html">Parsing XML in Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
250
clones/clojure-doc.org/articles/tutorials/eclipse/index.html
Normal file
250
clones/clojure-doc.org/articles/tutorials/eclipse/index.html
Normal file
|
@ -0,0 +1,250 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Starting with Eclipse and Counterclockwise For Clojure Development</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/tutorials/eclipse/" />
|
||||
<meta property="og:title" content="Starting with Eclipse and Counterclockwise For Clojure Development" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/tutorials/eclipse/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Starting with Eclipse and Counterclockwise For Clojure Development</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>Installing Eclipse</li><li>Installing Counterclockwise, the Clojure plugin for Eclipse</li><li>Creating a sample project</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
|
||||
(including images & stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.4.</p><h2 id="installing-eclipse">Installing Eclipse</h2><ol><li>Download the latest release of Eclipse from the <a href="http://www.eclipse.org/downloads/packages/eclipse-ide-java-developers/junor">official site</a>.</li><li>Install Counterclockwise plugin.
|
||||
<ol><li>navigate to the "Install new Software" tab under the help menu</li><li>paste in the CCW update URL: http://ccw.cgrand.net/updatesite in the "Work with:" text field</li><li>check the "Clojure Programming" checkbox and hit the "Next" button</li></ol></li><li>Follow the instructions on the screen and restart Eclipse for changes to take effect.</li></ol><p>Counterclockwise takes care of setting up Clojure and Leiningen for you. And once the plugin is installed, you will be
|
||||
able to create a new Clojure project or a new Leiningen project.</p><p>Clojure project will use Eclipse to manage dependencies, while the Leiningen project will pull dependencies from the
|
||||
<code>project.clj</code> in the root folder of the project.</p><p>At this point you should have Eclipse with CCW up and running. Navigate to File->new->project in Eclipse menu.
|
||||
Then select Leiningen->Leiningen project. Here you'll see the default Leiningen Template filled in.
|
||||
And only thing you have to do is provide a project name. Give the project a name and hit the finish button.</p><p>You should now see a new project in your Package Explorer view on the left. If you created a project called
|
||||
<code>myproject</code> then the project template will have a src folder which will contain the package folder named myproject.</p><p>Note that since Java cannot use dashes in names, all the dashes in package folders for namespaces get converted to underscores.
|
||||
The package will contain a core.clj file, and its contents should look like the following:</p><pre><code class="clojure">(ns myproject.core)
|
||||
|
||||
(defn foo
|
||||
"I don't do a whole lot."
|
||||
[x]
|
||||
(println x "Hello, World!"))
|
||||
</code></pre><p>Let's open it and then hit the run button. You should see a REPL pop up momentarily on the bottom of the IDE.
|
||||
If all went well, your project should be ready to work on (if it failed, see the <a href="index.html#troubleshooting">troubleshooting section</a>). The code that's in the file will have already been
|
||||
loaded up in the REPL when we hit run, and we should now be able to call our foo function.</p><p>To do that, let's write the code which calls foo below it:</p><pre><code class="clojure">(foo "Test: ")
|
||||
</code></pre><p>Then navigate the cursor inside the call body and hit CTRL+ENTER on Linux/Windows or CMD+ENTER on OS X.
|
||||
You should see "Hello, World!" printed in the REPL view on the bottom. We can now change the behavior of the
|
||||
<code>foo</code> function and after reloading it the new behavior will be available next time it's called.</p><p>It's also recommended to enable the "strict/paredit" mode under Preferences->Clojure->Editor section.
|
||||
This will allow the editor to keep track of balancing the parens for you.</p><p>Another useful feature of the editor is the ability to select code by expression.
|
||||
If you navigate inside a function and press ALT+SHIFT+UP (use CMD instead of ALT in OS X), then inner
|
||||
body of the expression will be selected, pressing it again, will select the expression, and then the outer body,
|
||||
and so on. Conversely pressing ALT+SHIFT+DOWN will narrow the selection. This allows you to quickly navigate nested
|
||||
structures, and select code by chunks of logic as opposed to simply selecting individual lines.</p><h2 id="managing-dependencies">Managing dependencies</h2><h3 id="eclipse-dependencies">Eclipse Dependencies</h3><ol><li>Right click on the project navigate to properties.</li><li>Under properties select "Java Build Path"</li><li>Under Libraries select "Add External JARs..."</li><li>Click OK</li></ol><p>The library will show up under "Referenced Libraries" in Package Explorer.</p><h3 id="leiningen">Leiningen</h3><p>You will also see a <code>project.</code>clj` file in the root of the project. This file should look like the following:</p><pre><code class="clojure">(defproject myproject "0.1.0-SNAPSHOT"
|
||||
:description "FIXME: write description"
|
||||
:url "http://example.com/FIXME"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:dependencies [[org.clojure/clojure "1.4.0"]])
|
||||
</code></pre><p>You can add new dependencies to your project by adding them to the dependencies vector.
|
||||
For example, if we wanted to add an HTTP client, head to <a href="http://clojuresphere.herokuapp.com/">ClojureSphere</a>
|
||||
and navigate to the clj-http page.</p><p>From there follow the link to Clojars and copy the following:</p><pre><code class="clojure">[clj-http "0.6.4"]
|
||||
</code></pre><p>now we'll simply paste it under dependencies in our <code>project.clj</code>:</p><pre><code class="clojure">:dependencies [[org.clojure/clojure "1.4.0"]
|
||||
[clj-http "0.6.4"]]
|
||||
</code></pre><p>In the package explorer view on the left expand "Leiningen dependencies"
|
||||
and see that the clj-http jar included there. You will now have to kill our current REPL
|
||||
if it is running. To do that navigate to the terminal view next to it and press the stop button.
|
||||
When we start a new instance of the REPL, the library will be available for use.</p><p>In the core file we can now require the library in the namespace definition:</p><pre><code class="clojure">(ns myproject.core
|
||||
(:require [clj-http.client :as client]))
|
||||
</code></pre><p>and test using the client by typing</p><pre><code class="clojure">(client/get "http://google.com")
|
||||
</code></pre><p>and running it as we did earlier.</p><h2 id="troubleshooting">Troubleshooting</h2><p>If when you attempt to run your code for the first time, you are asked
|
||||
to select a way to run your project, rather than it just running, you
|
||||
can try right clicking on the root project folder and then selecting
|
||||
Leiningen->Reset Project Configuration.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../vim_fireplace/index.html">« Clojure with Vim and fireplace.vim</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../basic_web_development/index.html">Basic Web Development »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
445
clones/clojure-doc.org/articles/tutorials/emacs/index.html
Normal file
445
clones/clojure-doc.org/articles/tutorials/emacs/index.html
Normal file
|
@ -0,0 +1,445 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Clojure with Emacs</title>
|
||||
|
||||
|
||||
<meta name="description" content="This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.What Version of Clojure Does This Guide Cover?">
|
||||
|
||||
<meta property="og:description" content="This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.What Version of Clojure Does This Guide Cover?">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/tutorials/emacs/" />
|
||||
<meta property="og:title" content="Clojure with Emacs" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/tutorials/emacs/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Clojure with Emacs</h2>
|
||||
</div>
|
||||
|
||||
<p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5+ and Emacs 24+ for MS Windows and Linux,
|
||||
and Emacs 26+ for macOS. Earlier Clojure and Emacs releases
|
||||
are not supported by these instructions.</p><h2 id="overview">Overview</h2><p>Emacs has traditionally been one of the best development environments
|
||||
for functional languages and Lisps in particular. This guide will
|
||||
explain how to get it installed, and give an example of a basic
|
||||
workflow to use while developing a simple library.</p><h2 id="installing-emacs">Installing Emacs</h2><h3 id="macos">macOS</h3><p>The easiest way to get going with Emacs on macOS is to
|
||||
use <a href="https://brew.sh">Homebrew</a>. Instructions for installing Homebrew
|
||||
are provided on the landing page, and requirements (such as Xcode) are
|
||||
listed on the <a href="https://docs.brew.sh/Installation">installation details</a>
|
||||
page.</p><p>Once brew is installed, you can install Emacs using:</p><pre><code class="bash">$ brew cask install emacs
|
||||
</code></pre><p>This will install Emacs into your <code>/Applications/Emacs.app</code> folder and
|
||||
provide a symlink to the application as <code>/usr/local/bin/emacs</code>.</p><p>After installing, Emacs can be launched the same as other Mac applications
|
||||
<em>or</em> from the terminal via:</p><pre><code class="bash">$ emacs
|
||||
</code></pre><h3 id="debianubuntu">Debian/Ubuntu</h3><p>Newer Debian-based systems (post-wheezy) ship Emacs 24 in apt:</p><pre><code class="bash">$ sudo apt install emacs24
|
||||
</code></pre><h3 id="archantergos">Arch/Antergos</h3><p>The latest version of Emacs(<em>i.e.</em> v25.1) is available in Arch's official repositories.</p><pre><code class="bash">$ sudo pacman -S emacs
|
||||
</code></pre><p>On older systems you can add unofficial package sources for <code>emacs-snapshot</code>,
|
||||
either for <a href="http://emacs.naquadah.org/">Debian</a> or
|
||||
<a href="https://launchpad.net/~cassou/+archive/emacs">Ubuntu</a>.</p><h3 id="ms-windows">MS Windows</h3><p>You can find Emacs for Windows in the <a href="http://ftp.gnu.org/pub/gnu/emacs/windows/">FSF FTP
|
||||
directory</a>.</p><p>Download the file named <code>emacs-24.1-bin-i386.zip</code> and unzip it in a new folder.
|
||||
Avoid folder with spaces in their names such as <code>C:\Documents and Settings</code>.
|
||||
Prefer folder names such as <code>C:\emacs-24.1</code>.</p><p><a href="http://support.microsoft.com/kb/310519#tocHeadRef">Create an environment variable</a>
|
||||
with name HOME and value equal to the location of your home folder; in Windows
|
||||
XP, it's <code>C:\Documents and Settings\YourUsername</code>, in Windows 7, it's
|
||||
<code>C:\Users\YourUsername</code>. With this variable set, you can use the tilde character
|
||||
(<code>~</code>) to type the name of a file under your home folder and Emacs will expand
|
||||
its full path.</p><p>The following section describes Emacs configuration using the folder <code>.emacs.d</code>.
|
||||
When using Emacs in Windows, you should create this folder under your home
|
||||
folder. In Windows XP, that will be the folder <code>C:\Documents and Settings\YourUsername\.emacs.d</code>; in Windows 7, that will be the folder
|
||||
<code>C:\Users\YourUsername\.emacs.d</code>.</p><h2 id="configuring-emacs">Configuring Emacs</h2><p>So Emacs is installed, but running it now would be a somewhat
|
||||
barebones experience and not particularly useful for Clojure
|
||||
development.</p><h3 id="manual-setup">Manual setup</h3><p>Emacs can be configured through a folder in your home folder called
|
||||
<a href="http://www.emacswiki.org/emacs/DotEmacsDotD">~/.emacs.d</a>, and
|
||||
configuration options are pretty much endless. To help you through
|
||||
this, Phil Hagelberg has created a small library enables a few
|
||||
non-intrusive helpful features called
|
||||
<a href="https://github.com/technomancy/better-defaults">better-defaults</a>
|
||||
which might be useful if you are not already an Emacs pro.</p><p>Most Emacs packages are kept at <a href="http://melpa.milkbox.net">MELPA</a>,
|
||||
the community package host. Add this code to your config in
|
||||
<code>~/.emacs.d/init.el</code> to tell Emacs to look there:</p><p>For the stable repository:</p><pre><code class="cl">(require 'package)
|
||||
(add-to-list 'package-archives
|
||||
'("melpa-stable" . "http://stable.melpa.org/packages/") t)
|
||||
(package-initialize)
|
||||
</code></pre><p>For the latest packages:</p><pre><code class="cl">(require 'package)
|
||||
(add-to-list 'package-archives
|
||||
'("melpa" . "http://melpa.org/packages/") t)
|
||||
(package-initialize)
|
||||
</code></pre><p>Run <code>M-x package-refresh-contents</code> to pull in the package listing.</p><p><code>M-x</code> means <code>meta-x</code>, and meta is mapped to the alt key on most keyboards,
|
||||
though Mac OS X usually maps it to the command key.</p><p>You'll need to install the following packages:</p><ul><li><a href="https://github.com/clojure-emacs/clojure-mode">clojure-mode</a> - a major mode for editing Clojure and ClojureScript code</li><li><a href="https://github.com/clojure-emacs/cider">CIDER</a> - a Clojure interactive development environment and REPL for Emacs</li><li><a href="https://github.com/bbatsov/projectile">projectile</a>(optional) - for navigating inside your projects swiftly</li></ul><p>Before continuing any further you should briefly consult their documentation.</p><p>You can either install each package one-by-one with <code>M-x package-install</code> or specify all your packages in Emacs Lisp as part of
|
||||
your configuration file. This is helpful if you take your dotfiles to
|
||||
a new machine; you don't have to remember everything you've installed
|
||||
by hand.</p><pre><code class="cl">(defvar my-packages '(better-defaults
|
||||
projectile
|
||||
clojure-mode
|
||||
cider))
|
||||
|
||||
(dolist (p my-packages)
|
||||
(unless (package-installed-p p)
|
||||
(package-install p)))
|
||||
</code></pre><p>Put the code above in <code>~/.emacs.d/init.el</code> and run it with <code>M-x eval-buffer</code>.</p><p>A lot of warnings will likely whizz by as it installs and compiles
|
||||
packages. Unless you have any actual <em>errors</em> this is all fine.</p><p>To look at the other packages available for installation you can
|
||||
invoke <code>M-x package-list-packages</code>. To manually install a package,
|
||||
move the point to line of the package with the keyboard and press 'i'
|
||||
for 'install'. After selecting all the packages you are interested in,
|
||||
press 'x' for 'eXecute' to install.</p><h3 id="preconfigured-setup">Preconfigured setup</h3><p>There are also some ready-made Emacs configurations that are optimized
|
||||
for Clojure development -
|
||||
<a href="https://github.com/bbatsov/prelude">Prelude</a>(developed by the
|
||||
maintainer of CIDER and clojure-mode) and
|
||||
<a href="https://github.com/overtone/emacs-live">Emacs Live</a>.</p><p>If you want a more powerful Emacs setup you should definitely check them out.</p><h3 id="basics">Basics</h3><p>The first thing you should do without question, is to go through the
|
||||
built-in Emacs tutorial. To do this press <code>C-h t</code> or hold down Control
|
||||
and press <code>h</code> and then press <code>t</code> by itself.</p><p>With that in mind, these are the basic keystrokes you're going to be
|
||||
using most often with the default binary of Emacs 24+:</p><pre><code>File/buffer/window commands
|
||||
C-x C-f Find file
|
||||
C-x C-s Save buffer
|
||||
C-x s Save file (like save-as)
|
||||
C-x b Switch buffer
|
||||
C-x k Kill buffer
|
||||
C-x 1 Delete other windows
|
||||
C-x 0 Delete current window
|
||||
C-x 2 Split window horizontally
|
||||
C-x 3 Split window vertically
|
||||
|
||||
Movement commands
|
||||
C-a Beginning of line
|
||||
C-e End of line
|
||||
C-n Next line (down)
|
||||
C-p Previous line (up)
|
||||
C-b Back (left)
|
||||
C-f Forward (right)
|
||||
M-f Forward a word
|
||||
M-b Back a word
|
||||
C-v Forward a page
|
||||
M-v Back a page
|
||||
|
||||
Edit commands
|
||||
C-d Kill character
|
||||
M-d Kill word
|
||||
M-delete Kill word backwards
|
||||
|
||||
Misc commands
|
||||
C-s Regex search forwards
|
||||
C-r Regex search backwards
|
||||
M-% Query replace
|
||||
</code></pre><p>I should also mention the help commands:</p><pre><code>C-h t Tutorial (goes over the basics)
|
||||
C-h b Describe all current key bindings
|
||||
C-h m Describe the current mode
|
||||
C-h a Apropos - search the help for a term
|
||||
C-h k Describe key
|
||||
</code></pre><p>I recommend going through the tutorial at least once as it will give
|
||||
you a good understanding of the navigation and movement commands.
|
||||
Another useful command you will use a lot is <code>M-x</code> which allows you to
|
||||
run any command. And there are a LOT. Apropos is very useful for
|
||||
searching for something <code>C-h a</code>.</p><p>So after doing the tutorial (you did do that, RIGHT? O_O) you can move
|
||||
around, open files, save files, etc., and are generally comfortable at
|
||||
the basics. There is an almost infinite amount of things to learn
|
||||
about Emacs, but those basics will get you a long way.</p><h2 id="creating-a-project">Creating a project</h2><p>Let's go through the process of creating a small sample clojure project
|
||||
and illustrate how Emacs helps makes us champions in the land of lisp.</p><p>The project we will be building is a trivially simple command line
|
||||
parser that will take the argument pairs given to it and turn them
|
||||
into a map of key-value pairs. The functionality is irrelevant and not
|
||||
particularly useful. It serves purely to illustrate the development
|
||||
flow.</p><p>If you don't have <a href="http://leiningen.org">Leiningen</a> yet, get it
|
||||
installed and then use it to create a new project:</p><pre><code class="bash">$ lein new command-line-args
|
||||
$ cd command-line-args
|
||||
</code></pre><p>Take a look at the project structure:</p><pre><code>+ doc
|
||||
- intro.md
|
||||
- project.clj
|
||||
- README.md
|
||||
+ src
|
||||
+ command_line_args
|
||||
- core.clj
|
||||
+ test
|
||||
+ command_line_args
|
||||
- core_test.clj
|
||||
</code></pre><p>Should be fairly self-explanatory, though Leiningen's built-in tutorial
|
||||
(available via <code>lein help tutorial</code>) provides a detailed explanation of
|
||||
the project structure.</p><p>Let's start up a live REPL session.</p><pre><code>M-x cider-jack-in
|
||||
</code></pre><p>This should open up a new window looking at our <code>*cider-repl*</code> buffer.</p><p>First thing to do is add a simple test (in fact the only test we will
|
||||
be adding because by default, we get it right first time). Open the
|
||||
<code>core_test.clj</code> file inside of the test folder. Replace the test that
|
||||
is there with the following:</p><pre><code class="clojure">(deftest pairs-of-values
|
||||
(let [args ["--server" "localhost"
|
||||
"--port" "8080"
|
||||
"--environment" "production"]]
|
||||
(is (= {:server "localhost"
|
||||
:port "8080"
|
||||
:environment "production"}
|
||||
(parse-args args)))))
|
||||
</code></pre><p>We are simply assigning a list of arguments as they would arrive from
|
||||
the command line to a local called args, and asserting that the
|
||||
return value from a function called <code>parse-args</code> is equal to those
|
||||
command line args turned into a simple map.</p><p>Compile the file with <code>C-c C-k</code>(<code>M-x cider-load-buffer</code>). We should get an error
|
||||
message at the bottom of the emacs window complaining that clojure can't find
|
||||
parse-args. Let's try to fix the exception by opening <code>core.clj</code> (<code>C-x C-f</code>/<code>M-x find-file</code>) and adding the following definition:</p><pre><code class="clojure">(defn parse-args [args]
|
||||
{})
|
||||
</code></pre><p>Compile this with <code>C-c C-k</code>, save it (<code>C-x C-s</code>/<code>M-x save-buffer</code>), switch back
|
||||
to the test buffer (<code>C-x b RET</code>/<code>M-x switch-to-buffer RET</code>) and try compiling
|
||||
again (<code>C-c C-k</code>). This time it will succeed, so try running the tests with
|
||||
<code>C-c C-t t</code>(<em>i.e</em> <code>M-x cider-test-run-test</code>) and you should get a test report
|
||||
buffer showing some failure information:</p><pre><code class="clojure">(not (= {:server "localhost",
|
||||
:port "8080",
|
||||
:environment "production"}
|
||||
{}))
|
||||
</code></pre><p>Anyway, our map was empty as expected. Let's fix that:</p><pre><code class="clojure">(defn parse-args [args]
|
||||
(apply hash-map args))
|
||||
</code></pre><p>Running our tests again we now get another error:</p><pre><code class="clojure">(not (= {:server "localhost",
|
||||
:port "8080",
|
||||
:environment "production"}
|
||||
{"--port" "8080",
|
||||
"--server" "localhost",
|
||||
"--environment" "production"}))
|
||||
</code></pre><p>Whoops, our keys are just strings with the dashes still in place. We
|
||||
need to strip those off and turn them into keywords:</p><pre><code class="clojure">(defn parse-args [args]
|
||||
(into {} (map (fn [[k v]] [(keyword (.replace k "--" "")) v])
|
||||
(partition 2 args))))
|
||||
</code></pre><p>And re-running the tests in the test buffer we are all happy. If we
|
||||
had multiple test files we can run them all from the CLI using:</p><pre><code class="bash">$ lein test
|
||||
</code></pre><p>Re-running all the tests from Leiningen can be a good sanity check
|
||||
before you wrap up work on a feature or branch since there are some
|
||||
cases where developing from a REPL can give misleading results. For
|
||||
instance, if you delete a function definition but still call it from
|
||||
other functions, you won't notice until your process is restarted.</p><p>So that is an extremely simple example of a workflow using Emacs with
|
||||
<code>clojure-mode</code> and <code>cider-test</code>.</p><h2 id="using-the-repl">Using the REPL</h2><p>One thing we haven't looked at is how useful having an open running
|
||||
REPL in Emacs can be for development. If you still have your project
|
||||
open, split the window (<code>C-x 2</code> (horizontally) or <code>C-x 3</code> (vertically)) in
|
||||
two so you have the <code>core.clj</code> and <code>*cider-repl*</code> buffers open.
|
||||
Let's say you are editing the core.clj and you want to play around with
|
||||
the functions as you define them. Looking at <code>parse-args</code> you have
|
||||
decided you want to pull out the anonymous function to be a named
|
||||
function <code>keywordize</code>.</p><p>First load and compile the buffer into the REPL process with <code>C-c C-k</code>. Change the namespace of the REPL buffer to the one of the file
|
||||
you're in with <code>C-c M-n</code>. Now switch to the REPL window with <code>C-x o</code>.</p><p>You now have access to the functions in this namespace that were
|
||||
defined when you compiled the file. Try it:</p><pre><code>command-line-args.core> (parse-args '("key" "value"))
|
||||
{:key "value"}
|
||||
</code></pre><p>Let's go ahead and create our new function in <code>core.clj</code>:</p><pre><code class="clojure">(defn keywordize [kvp]
|
||||
(let [[k v] kvp]
|
||||
[(keyword (.replace k "--" "")) v]))
|
||||
|
||||
(defn parse-args [args]
|
||||
(into {} (map keywordize (partition 2 args))))
|
||||
</code></pre><p>Now we have a couple of options, we could re-compile the whole file again
|
||||
(<code>C-c C-k</code>) or we could evaluate each function on its own by going to the end
|
||||
of the s-exp and using <code>C-x C-e</code>(<em>i.e.</em> <code>cider-eval-last-sexp</code>) which sends the
|
||||
s-exp to the running REPL. Now switching back to the core.clj namespace
|
||||
(<code>C-c M-n</code>) and switching back to the REPL buffer we can try out our
|
||||
<code>keywordize</code> function:</p><pre><code>command-line-args.core> (keywordize ["--oh" "hai"])
|
||||
[:oh "hai"]
|
||||
</code></pre><p>If your REPL is starting to get cluttered you can <code>M-x cider-repl-clear-buffer</code>
|
||||
to clear by first switching to the REPL buffer. The ability to continually
|
||||
change the code and play around with it is one of the things that makes Emacs
|
||||
and a lisp a great combination for development.</p><p>If you find yourself wanting to repeat a command you just typed at the
|
||||
REPL, you can use <code>M-p</code> scroll back through history and <code>M-n</code> to go
|
||||
forwards. Also, all of the Emacs editing commands are available in the
|
||||
REPL, which is great.</p><p>A handy clojure function to use in the REPL is <code>clojure.repl/doc</code> which
|
||||
gives you the docstring for a given function:</p><pre><code>command-line-args.core> (use 'clojure.repl)
|
||||
nil
|
||||
command-line-args.core> (doc println)
|
||||
-------------------------
|
||||
clojure.core/println
|
||||
([& more])
|
||||
Same as print followed by (newline)
|
||||
nil
|
||||
</code></pre><p>However there is a shortcut <code>C-c C-d d</code> when your cursor is over a
|
||||
function name. This will show the Clojure (or Javadoc) doc in a new window. If
|
||||
instead you want to jump to the source of the function you can use
|
||||
<code>M-.</code>, which is awesome. This works on your own functions as well as
|
||||
those which come from third-party libraries. Use <code>M-,</code> to pop the
|
||||
stack and return to where you were. For all the definitions in a
|
||||
single file you can use <code>M-x imenu</code> to list them and jump to one.</p><p>When you are finished with the REPL (or if for some reason it has
|
||||
gotten into a bad state), you can simply kill the <code>*cider-repl*</code>
|
||||
buffer by typing <code>M-x cider-quit</code> and re-run <code>cider-jack-in</code> to start another.</p><h2 id="appendix">Appendix</h2><p><a href="http://stable.melpa.org/#/getting-started">MELPA documentation</a></p><p>CIDER keyboard shortcuts can be found in <a href="https://github.com/clojure-emacs/cider#keyboard-shortcuts">CIDER documentation</a>.</p><h2 id="contributors">Contributors</h2><p><a href="http://blog.gaz-jones.com">Gareth Jones</a>, 2012 (original author)</p><p>Thanks to <a href="http://technomancy.us/">Phil Hagelberg</a>, <a href="http://cleancode.se/">Mikael
|
||||
Sundberg</a>, and <a href="http://jakemccrary.com/">Jake
|
||||
McCrary</a> for suggestions for improvements to
|
||||
the original blog posts from which this guide was created.</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../introduction/index.html">« Introduction to Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../vim_fireplace/index.html">Clojure with Vim and fireplace.vim »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,256 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Getting Started with Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/tutorials/getting_started/" />
|
||||
<meta property="og:title" content="Getting Started with Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/tutorials/getting_started/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Getting Started with Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>prerequisites (such as Leiningen) and installing</li><li>running the REPL</li><li>creating a project</li><li>interactive development</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="overview">Overview</h2><p>Clojure is a wonderfully simple language and you are going to love
|
||||
it.</p><p>To quickly get started, first make sure you've got Java installed.</p><p>Then install the <a href="http://leiningen.org/">Leiningen</a> project management
|
||||
tool.</p><blockquote><p>This author (jg) recommends always installing by downloading the
|
||||
script directly (as described in the instructions at leiningen.org),
|
||||
rather than using your OS's package manager. This will ensure that
|
||||
you get the latest lein version 2.</p></blockquote><p>Clojure programs are typically developed inside their own project
|
||||
directory, and Leiningen manages projects for you. Lein takes care of
|
||||
pulling in dependencies (including Clojure itself), running the REPL,
|
||||
running your program and its tests, packaging your program for
|
||||
distribution, and other administrative tasks. Run <code>lein help</code> to
|
||||
see the list of all the tasks in can perform.</p><blockquote><p>Again, there's no need to "install" Clojure, per se. Lein
|
||||
will take care of fetching it for you.</p></blockquote><h2 id="trying-out-the-repl">Trying out the REPL</h2><p>Although lein facilitates managing your projects, you can also run it
|
||||
on its own (outside of any particular project directory). Once you
|
||||
have the <code>lein</code> tool installed, run it from anywhere you like to get a
|
||||
repl:</p><pre><code>$ lein repl
|
||||
</code></pre><p>You should be greeted with a "<code>user=></code>" prompt. Try it out:</p><pre><code class="clojure">user=> (+ 1 1)
|
||||
;; ⇒ 2
|
||||
user=> (distinct [:a :b :a :c :a :d])
|
||||
;; ⇒ (:a :b :c :d)
|
||||
user=> (dotimes [i 3]
|
||||
#_=> (println (rand-nth ["Fabulous!" "Marvelous!" "Inconceivable!"])
|
||||
#_=> i))
|
||||
;; Marvelous! 0
|
||||
;; Inconceivable! 1
|
||||
;; Fabulous! 2
|
||||
;; ⇒ nil
|
||||
</code></pre><h2 id="your-first-project">Your first project</h2><p>Create your first Clojure program like so:</p><pre><code class="bash">lein new app my-proj
|
||||
cd my-proj
|
||||
# Have a look at the "-main" function in src/my_proj/core.clj.
|
||||
lein run
|
||||
</code></pre><p>and see the output from that <code>println</code> function call in
|
||||
my_proj/core.clj!</p><h2 id="interactive-development">Interactive Development</h2><p>In your project directory, start up a repl (<code>lein repl</code>) and
|
||||
run your <code>-main</code> function to see its output in the repl:</p><pre><code>$ lein repl
|
||||
...
|
||||
my-proj.core=> (-main)
|
||||
Hello, World!
|
||||
nil
|
||||
</code></pre><p>(The prompt is now "my-proj.core=>" instead of "user=>" because lein
|
||||
has started the repl in an app project. More about that ("namespaces")
|
||||
in the topical guides.)</p><p>From elsewhere, open up your my-proj/src/my_proj/core.clj file
|
||||
in your editor. Modify the text in that <code>println</code> call.</p><p>Back in the repl, reload your source file and run <code>-main</code> again:</p><pre><code>my-proj.core=> (require 'my-proj.core :reload)
|
||||
my-proj.core=> (-main)
|
||||
</code></pre><p>to see your changes.</p><h2 id="see-also">See Also</h2><p>Other getting started documentation you might find useful:</p><ul><li><a href="http://yogthos.github.io/ClojureDistilled.html">Clojure Distilled</a>:
|
||||
introduction to core concepts necessary for working with Clojure</li><li><a href="http://www.unexpected-vortices.com/clojure/brief-beginners-guide/index.html">A Brief Beginner's Guide to
|
||||
Clojure</a>:
|
||||
contains a bit more overview and background material for learning your way
|
||||
around the landscape.</li><li><a href="https://cemerick.com/blog/2012/05/02/starting-clojure-mk-2.html">Starting Clojure screencast</a>:
|
||||
an extensive getting-started screencast using Eclipse to develop a webapp project.</li></ul><h2 id="next-stop">Next Stop</h2><p>Next stop: <a href="../introduction/index.html">the basic Clojure language tutorial</a>.</p><h2 id="contributors">Contributors</h2><p>John Gabriele <a href="mailto:jmg3000@gmail.com">jmg3000@gmail.com</a> (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../../content/index.html">« Table of Contents</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../introduction/index.html">Introduction to Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,385 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Growing a DSL with Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="Lisps like Clojure are well suited to creating rich DSLs that integrate seamlessly into the language.You may have heard Lisps boasting about code being data and data being code. In this article we will define a DSL that benefits handsomely from this fact.">
|
||||
|
||||
<meta property="og:description" content="Lisps like Clojure are well suited to creating rich DSLs that integrate seamlessly into the language.You may have heard Lisps boasting about code being data and data being code. In this article we will define a DSL that benefits handsomely from this fact.">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/tutorials/growing_a_dsl_with_clojure/" />
|
||||
<meta property="og:title" content="Growing a DSL with Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/tutorials/growing_a_dsl_with_clojure/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Growing a DSL with Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>Lisps like Clojure are well suited to creating rich DSLs that integrate seamlessly into the language.</p><p>You may have heard Lisps boasting about code being data and data being code. In this article we will define a DSL that benefits handsomely from this fact.</p><p>We will see our DSL evolve from humble beginnings, using successively more of Clojure’s powerful and unique means of abstraction.</p><h2 id="the-mission">The Mission</h2><p>Our goal will be to define a DSL that allows us to generate various scripting languages. The DSL code should look similar to regular Clojure code.</p><p>For example, we might use this Clojure form to generate either Bash or Windows Batch script output:</p><p>Input (Clojure form):</p><pre><code class="clojure">(if (= 1 2)
|
||||
(println "a")
|
||||
(println "b"))
|
||||
</code></pre><p>Output (Bash script):</p><pre><code class="sh">if [ 1 -eq 2 ]; then
|
||||
echo "a"
|
||||
else
|
||||
echo "b"
|
||||
fi
|
||||
</code></pre><p>Output (Windows Batch script):</p><pre><code class="bat">IF 1==2 (
|
||||
ECHO a
|
||||
) ELSE (
|
||||
ECHO b
|
||||
)
|
||||
</code></pre><p>We might, for example, use this DSL to dynamically generate scripts to perform maintenance tasks on server farms.</p><h2 id="baby-steps-mapping-to-our-domain-language">Baby Steps: Mapping to Our Domain Language</h2><p>I like Bash, so let’s start with a Bash script generator.</p><p>To start, we need to expose some parallels between Clojure’s core types and our domain language.</p><p>So which Clojure types have simple analogues in Bash script?</p><p>Strings and numbers should just simply return their String representation, so we will start with those.</p><p>Let’s define a function <code>emit-bash-form</code> that takes a Clojure form and returns a string that represents the equivalent Bash script.</p><pre style="visibility:hidden; height:0;"><code class="klipse-clojure nohighlight">
|
||||
(require '[chivorcam.core :refer [defmacro defmacfn]])
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defn emit-bash-form [a]
|
||||
"Returns a String containing the equivalent Bash script
|
||||
to its argument."
|
||||
(cond
|
||||
(string? a ) a
|
||||
(number? a) (str a)
|
||||
:else (throw (ex-info "Fell through" a))))
|
||||
</code></pre><p>The <code>cond</code> expression handles cases for strings and numbers or throws an exception.</p><pre><code class="klipse-clojure nohighlight">(emit-bash-form 1)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(emit-bash-form "a")
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(emit-bash-form {})
|
||||
</code></pre><p>Now if we want to add some more dispatches, we just need to add a new clause to our <code>cond</code> expression.</p><h2 id="echo-and-print">Echo and Print</h2><p>Let’s add a feature.</p><p>Bash prints to the screen using <code>echo</code>. You’ve probably seen it if you’ve spent any time with a Linux shell.</p><pre><code class="sh">ambrose@ambrose-desktop> echo asdf
|
||||
asdf
|
||||
</code></pre><p>clojure.core also contains a function <code>println</code> that has similar semantics to Bash's <code>echo</code>.</p><pre><code class="klipse-clojure nohighlight">(println "asdf")
|
||||
</code></pre><p>Wouldn’t it be cool if we could pass <code>(println "a")</code> to <code>emit-bash-form</code>?</p><pre><code class="clojure">(emit-bash-form (println "asdf"))
|
||||
</code></pre><p>At first, this seems like asking the impossible.</p><p>To made an analogy with Java, imagine calling this Java code and expecting the first argument to equal <code>System.out.println("asdf")</code>.</p><pre><code class="java">foo( System.out.println("asdf") );
|
||||
</code></pre><p>(Let’s ignore the fact that <code>System.out.println(...)</code> returns a void).</p><p>Java evaluates the arguments before you can even blink, resulting in a function call to println. How can we stop this evaluation and return the raw code?</p><p>Indeed this is an impossible task in Java. Even if this were possible, what could we expect do with the raw code?(!)</p><p><code>System.out.println("asdf")</code> is not a Collection, so we can’t iterate over it; it is not a <code>String</code>, so we can’t partition it with regular expressions.</p><p>Whatever "type" the raw code <code>System.out.println("asdf")</code> has, it’s not meant to be known by anyone but compiler writers.</p><p>Lisp turns this notion on its head.</p><h2 id="lisp-code-is-data">Lisp Code Is Data</h2><p>A problem with raw code forms in Java (assuming it is possible to extract them) is the lack of facilities to interrogate them. How does Clojure get around this limitation?</p><p>To get to the actual raw code at all, Clojure provides a mechanism to stop evaluation via quote. Prepending a quote to a code form prevents its evaluation and returns the raw Clojure form.</p><pre><code class="klipse-clojure nohighlight">'(println "a")
|
||||
</code></pre><p>So what is the type of our result?</p><pre><code class="klipse-clojure nohighlight">(type '(println "a"))
|
||||
</code></pre><p>It's a list!</p><p>We can now interrogate the raw code as if it were any old Clojure list (because it is!).</p><pre><code class="klipse-clojure nohighlight">(first '(println "a"))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(second '(println "a"))
|
||||
</code></pre><p>This is a result of Lisp’s remarkable property of code being data.</p><h2 id="a-little-closer-to-clojure">A Little Closer to Clojure</h2><p>Using quote, we can get halfway to a DSL that looks like Clojure code.</p><pre><code class="clojure">(emit-bash-form
|
||||
'(println "a"))
|
||||
</code></pre><p>Let’s add this feature to <code>emit-bash-form</code>. We need to add a new clause to the <code>cond</code> form. Which type should the dispatch value be?</p><p>So let’s add a clause for lists.</p><pre><code class="klipse-clojure nohighlight">(defn emit-bash-form [a]
|
||||
"Returns a String containing the equivalent Bash script
|
||||
to its argument."
|
||||
(cond
|
||||
(list? a)
|
||||
(case (name (first a))
|
||||
"println" (str "echo " (second a)))
|
||||
|
||||
(string? a) a
|
||||
(number? a) (str a)
|
||||
:else (throw (ex-info "Fell through" a))))
|
||||
</code></pre><p>As long as we remember to quote the argument, this is not bad.</p><pre><code class="klipse-clojure nohighlight">(emit-bash-form '(println "a"))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(emit-bash-form '(println "hello"))
|
||||
</code></pre><h2 id="multimethods-to-abstract-the-dispatch">Multimethods to Abstract the Dispatch</h2><p>We’ve made a good start, but I think it’s time for some refactoring.</p><p>Currently, to extend our implementation we add to our function emit-bash-form. Eventually this function will be too large to manage; we need a mechanism to split this function into more manageable pieces.</p><p>Essentially emit-bash-form is dispatching on the type of its argument. This dispatch style is a perfect fit for an abstraction Clojure provides called a multimethod.</p><p>Let’s define a multimethod called <code>emit-bash</code>.</p><p>The multimethod handles dispatch in a similar way to <code>cond</code>, but without having to actually write each case. Let’s compare this multimethod with our previous <code>cond</code> expression. <code>defmulti</code> is used to create a new multimethod, and associates it with a dispatch function.</p><pre><code class="klipse-clojure nohighlight">(defmulti emit-bash
|
||||
(fn [form]
|
||||
(cond
|
||||
(list? form) :list
|
||||
(string? form) :string
|
||||
(number? form) :number
|
||||
:else (throw (ex-info "Unknown type" form)))))
|
||||
</code></pre><p><code>defmethod</code> is used to add <em>methods</em> to an existing multimethod. Here <code>:string</code> is the <em>dispatch value</em>, and the method returns the form as is.</p><pre><code class="klipse-clojure nohighlight">(defmethod emit-bash
|
||||
:string
|
||||
[form]
|
||||
form)
|
||||
</code></pre><p>Similar for numbers and lists:</p><pre><code class="klipse-clojure nohighlight">(defmethod emit-bash
|
||||
:number
|
||||
[form]
|
||||
(str form))
|
||||
|
||||
(defmethod emit-bash
|
||||
:list
|
||||
[form]
|
||||
(case (name (first form))
|
||||
"println" (str "echo " (second form))))
|
||||
</code></pre><p>Adding new methods has the same result as extending our <code>cond</code> expression, except:</p><ul><li>multimethods handle the dispatch, instead of writing it manually</li><li>anyone can extend the multimethod at any point, without disturbing existing code</li></ul><p>So how can we use <code>emit-bash</code>? Calling a multimethod is just like calling any Clojure function.</p><pre><code class="klipse-clojure nohighlight">(emit-bash '(println "a"))
|
||||
</code></pre><p>The dispatch is silently handled under the covers by the multimethod.</p><h2 id="extending-our-dsl-for-batch-script">Extending our DSL for Batch Script</h2><p>Let’s say I’m happy with the Bash implementation. I feel like starting a new implementation that generates Windows Batch script. Let’s define a new multimethod, emit-batch.</p><pre><code class="klipse-clojure nohighlight">(defmulti emit-batch
|
||||
(fn [form]
|
||||
(cond
|
||||
(list? form) :list
|
||||
(string? form) :string
|
||||
(number? form) :number
|
||||
:else (throw (ex-info "Unknown type" form)))))
|
||||
|
||||
(defmethod emit-batch
|
||||
:list
|
||||
[form]
|
||||
(case (name (first form))
|
||||
"println" (str "ECHO " (second form))
|
||||
nil))
|
||||
|
||||
(defmethod emit-batch
|
||||
:string
|
||||
[form]
|
||||
form)
|
||||
|
||||
(defmethod emit-batch
|
||||
:number
|
||||
[form]
|
||||
(str form))
|
||||
</code></pre><p>We can now use <code>emit-batch</code> and <code>emit-bash</code> when we want Batch and Bash script output respectively.</p><pre><code class="klipse-clojure nohighlight">(emit-batch '(println "a"))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(emit-bash '(println "a"))
|
||||
"echo a"
|
||||
</code></pre><h2 id="ad-hoc-hierarchies">Ad-hoc Hierarchies</h2><p>Comparing the two implementations reveals many similarities. In fact, the only dispatch that differs is clojure.lang.PersistentList!</p><p>Some form of implementation inheritance would come in handy here.</p><p>We can tackle this with a simple mechanism Clojure provides to define global, ad-hoc hierarchies.</p><p>When I say this mechanism is simple, I mean non-compound; inheritance is not compounded into the mechanism to define classes or namespaces but rather is a separate functionality.</p><p>Contrast this to languages like Java, where inheritance is tightly coupled with defining a hierarchy of classes.</p><p>We can derive relationships from names to other names, and between classes and names. Names can be symbols or keywords. This is both very general and powerful!</p><p>We will use <code>(derive child parent)</code> to establishes a parent/child relationship between two keywords. <code>isa?</code> returns <code>true</code> if the first argument is derived from the second in a global hierarchy.</p><pre><code class="klipse-clojure nohighlight">(derive ::child ::parent)
|
||||
|
||||
(isa? ::child ::parent)
|
||||
</code></pre><p>Let’s define a hierarchy in which the Bash and Batch implementations are siblings.</p><pre><code class="klipse-clojure nohighlight">(derive ::bash ::common)
|
||||
(derive ::batch ::common)
|
||||
</code></pre><p>Let’s test this hierarchy.</p><pre><code class="klipse-clojure nohighlight">(parents ::bash)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(parents ::batch)
|
||||
</code></pre><h2 id="utilizing-a-hierarchy-in-a-multimethod">Utilizing a Hierarchy in a Multimethod</h2><p>We can now define a new multimethod emit that utilizes our global hierarchy of names.</p><pre><code class="klipse-clojure nohighlight">(defmulti emit
|
||||
(fn [form]
|
||||
[*current-implementation*
|
||||
(cond
|
||||
(list? form) :list
|
||||
(string? form) :string
|
||||
(number? form) :number
|
||||
:else (throw (ex-info "Unknown type" form)))]))
|
||||
</code></pre><p>The dispatch function returns a vector of two items: the current implementation (either <code>::bash</code> or <code>::batch</code>), and the class of our form (like <code>emit-bash</code>'s dispatch function).</p><p><code>*current-implementation*</code> is a dynamic var, which can be thought of as a thread-safe global variable.</p><pre><code class="klipse-clojure nohighlight">(def ^{:dynamic true}
|
||||
;; The current script language implementation to generate
|
||||
*current-implementation*)
|
||||
</code></pre><p>In our hierarchy, <code>::common</code> is the parent, which means it should provide the methods in common with its children. Let's fill in these common implementations.</p><p>Remember the dispatch value is now a vector, notated with square brackets. In particular, in each defmethod the first vector is the dispatch value (the second vector is the list of formal parameters).</p><pre><code class="klipse-clojure nohighlight">(defmethod emit [::common :string]
|
||||
[form]
|
||||
form)
|
||||
|
||||
(defmethod emit [::common :number]
|
||||
[form]
|
||||
(str form))
|
||||
</code></pre><p>This should look familiar. The only methods that needs to be specialized are those for clojure.lang.PersistentList, as we identified earlier. Notice the first item in the dispatch value is <code>::bash</code> or <code>::batch</code> instead of <code>::common</code>.</p><pre><code class="klipse-clojure nohighlight">(defmethod emit [::bash :list]
|
||||
[form]
|
||||
(case (name (first form))
|
||||
"println" (str "echo " (second form))
|
||||
nil))
|
||||
|
||||
(defmethod emit [::batch :list]
|
||||
[form]
|
||||
(case (name (first form))
|
||||
"println" (str "ECHO " (second form))
|
||||
nil))
|
||||
</code></pre><p>The <code>::common</code> implementation is intentionally incomplete; it merely exists to manage any common methods between its children.</p><p>We can test emit by rebinding <code>*current-implementation*</code> to the implementation of our choice with binding.</p><pre><code class="klipse-clojure nohighlight">(binding [*current-implementation* ::common]
|
||||
(emit "a"))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(binding [*current-implementation* ::batch]
|
||||
(emit '(println "a")))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(binding [*current-implementation* ::bash]
|
||||
(emit '(println "a")))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(binding [*current-implementation* ::common]
|
||||
(emit '(println "a")))
|
||||
</code></pre><p>Because we didn’t define an implementation for <code>[::common :list]</code>, the multimethod falls through and throws an Exception.</p><p>Multimethods offer great flexibility and power, but with power comes great responsibility. Just because we can put our multimethods all in one namespace doesn’t mean we should. If our DSL becomes any bigger, we would probably separate all Bash and Batch implementations into individual namespaces.</p><p>This small example, however, is a good showcase for the flexibility of decoupling namespaces and inheritance.</p><h2 id="icing-on-the-cake">Icing on the Cake</h2><p>We’ve built a nice, solid foundation for our DSL using a combination of multimethods, dynamic vars, and ad-hoc hierarchies, but it’s a bit of a pain to use.</p><pre><code class="klipse-clojure nohighlight">(binding [*current-implementation* ::bash]
|
||||
(emit '(println "a")))
|
||||
</code></pre><p>Let’s eliminate the boilerplate. But where is it?</p><p>The binding expression is an good candidate. We can reduce the chore of rebinding <em>current-implementation</em> by introducing with-implementation (which we will define soon).</p><pre><code class="clojure">(with-implementation ::bash
|
||||
(emit '(println "a")))
|
||||
</code></pre><p>That’s an improvement. But there’s another improvement that’s not as obvious: the quote used to delay our form’s evaluation. Let’s use script, which we will define later, to get rid of this boilerplate:</p><pre><code class="clojure">(with-implementation ::bash
|
||||
(script
|
||||
(println "a")))
|
||||
</code></pre><p>This looks great, but how do we implement script? Clojure functions evaluate all their arguments before evaluating the function body, exactly the problem the quote was designed to solve.</p><p>To hide this detail we must wield one of Lisp’s most unique forms: the macro.</p><p>The macro’s main drawcard is that it doesn’t implicitly evaluate its arguments. This is a perfect fit for an implementation of script.</p><pre><code class="klipse-clojure nohighlight">(defmacro script [form]
|
||||
`(emit '~form))
|
||||
</code></pre><p>To get an idea what is happening, here’s what a call to script returns and then implicitly evaluates.</p><pre><code class="klipse-clojure nohighlight">(macroexpand '(script (println "a")))
|
||||
</code></pre><p>It isn’t crucial that you understand the details, rather appreciate the role that macros play in cleaning up the syntax.</p><p>We will also implement with-implementation as a macro, but for different reasons than with script. To evaluate our script form inside a binding form we need to drop it in before evaluation.</p><pre><code class="klipse-clojure nohighlight">(defmacro with-implementation
|
||||
[impl & body]
|
||||
`(binding [cljs.user/*current-implementation* ~impl]
|
||||
~@body))
|
||||
</code></pre><p>Roughly, here is the lifecyle of our DSL, from the sugared wrapper to our unsugared foundations.</p><pre><code class="clojure">(with-implementation ::bash
|
||||
(script
|
||||
(println "a")))
|
||||
=>
|
||||
(with-implementation ::bash
|
||||
(emit
|
||||
'(println "a"))
|
||||
=>
|
||||
(binding [*current-implementation* ::bash]
|
||||
(emit
|
||||
'(println "a")))
|
||||
</code></pre><p>Let's see it in action for Bash:</p><pre><code class="klipse-clojure nohighlight">(with-implementation ::bash
|
||||
(script
|
||||
(println "a")))
|
||||
</code></pre><p>And for Windows:</p><pre><code class="klipse-clojure nohighlight">(with-implementation ::batch
|
||||
(script
|
||||
(println "a")))
|
||||
</code></pre><p>It’s easy to see how a few well-placed macros can put the sugar on top of strong foundations. Our DSL really looks like Clojure code!</p><h2 id="conclusion">Conclusion</h2><p>We have seen many of Clojure’s advanced features working in harmony in this DSL, even though we incrementally incorported many of them. Generally, Clojure helps us switch our implementation strategies with minimum fuss.</p><p>This is notable when you consider how much our DSL evolved.</p><p>We initially used a simple <code>cond</code> expression, which was converted into two multimethods, one for each implementation. As multimethods are just ordinary functions, the transition was seamless for any existing testing code. (In this case I renamed the function for clarity).</p><p>We then merged these multimethods, utilizing a global hierachy for inheritance and dynamic vars to select the current implementation.</p><p>Finally, we devised a pleasant syntactic interface with a two simple macros, eliminating that last bit of boilerplate that other languages would have to live with.</p><p>I hope you have enjoyed following the evolution of our little DSL. This DSL is based on a simplified version of <a href="https://github.com/pallet/stevedore">Stevedore</a> by <a href="http://hugoduncan.org/">Hugo Duncan</a>. If you are interested in how this DSL can be extended, you can do no better than browsing the source code of <a href="https://github.com/pallet/stevedore">Stevedore</a>.</p><h2 id="copyright">Copyright</h2><p>Copyright Ambrose Bonnaire-Sergeant, 2013</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../parsing_xml_with_zippers/index.html">« Parsing XML in Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
|
||||
<script>
|
||||
window.klipse_settings = {
|
||||
"selector" : ".klipse-clojure"
|
||||
};
|
||||
</script>
|
||||
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,968 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Introduction to Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This guide covers:">
|
||||
|
||||
<meta property="og:description" content="This guide covers:">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/tutorials/introduction/" />
|
||||
<meta property="og:title" content="Introduction to Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/tutorials/introduction/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Introduction to Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This guide covers:</p><ul><li>Clojure language basics</li><li>expressions, identifiers (locals, vars)</li><li><code>let</code> forms</li><li>scalars</li><li>functions</li><li>basic data types</li><li>introduction to immutable data structures</li><li>overview of Clojure reference types (vars, atoms, agents, refs)</li><li>looping and recursion</li><li>basics of Clojure macros</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="overview">Overview</h2><p>This is a brief beginner's introduction to Clojure. If you haven't
|
||||
already done so, have a look at the <a href="../getting_started/index.html">Getting
|
||||
Started</a> tutorial. Before
|
||||
continuing, make sure you've got Java and
|
||||
<a href="http://leiningen.org">Leiningen</a> installed, and can create a new
|
||||
project and run a REPL in it. The author expects you'll want to have a
|
||||
REPL running while following this introduction so you can type
|
||||
expressions into it as you go.</p><blockquote><p><strong>Note:</strong> In the code samples below, unless we're specifically
|
||||
discussing the REPL, to reduce clutter we've usually omitted showing
|
||||
the REPL prompt (ex. "<code>user=></code>" or "<code>my-proj.core=></code>").</p><p>Additionally: In Clojure, a semicolon begins a single-line comment,
|
||||
and in this document we use "<code>; ⇒</code>" (for trailing comments) and
|
||||
"<code>;; ⇒</code>" (for comments on their own line) to indicate what the
|
||||
previous expression evaluates to.</p></blockquote><p>This introduction is a whirlwind tutorial of most of the basics of
|
||||
Clojure. Its goal is to rapidly get you familiar with the core
|
||||
areas of the language without wasting your time and also without getting
|
||||
too bogged down in details or advanced topics (the various topics will
|
||||
get more comprehensive coverage in the topic guides anyway).</p><p>As we said in the Getting Started tutorial, Clojure is a wonderfully
|
||||
simple language, and you're going to love it.</p><h2 id="the-basics">The Basics</h2><p>Clojure is a general-purpose programming language, and a quite
|
||||
practical one at that.</p><p>The syntax for Clojure is like Lisp and is very simple: code is made
|
||||
up of expressions which are evaluated to some value. Here are some
|
||||
examples of expressions:</p><pre><code class="clojure">5 ; ⇒ 5
|
||||
"hi" ; ⇒ "hi"
|
||||
[1 2 3] ; evaluates to the vector `[1 2 3]`
|
||||
(+ 1 2) ; evaluates to the sum of 1 and 2
|
||||
(if true "yes" "no") ; evaluates to the string "yes"
|
||||
(println "hello!") ; evaluates to nil (but also prints "hello!")
|
||||
</code></pre><p>Clojure supports a few extra bits of syntax which will be noted as we
|
||||
encounter them.</p><p>Expressions can contain sub-expressions:</p><pre><code class="clojure">(+ 1
|
||||
(* 2 3)
|
||||
(/ 10 2)) ; ⇒ 1 + (2 * 3) + (10 / 2) = 12
|
||||
</code></pre><p>Expressions in (various types of) brackets are often referred to as
|
||||
"forms".</p><p>An expression in parentheses is usually treated as a function call,
|
||||
but may also be a macro or special form (more about those in the
|
||||
<a href="index.html#evaluation">Evaluation</a> section below).</p><p>Clojure is not whitespace-sensitive. Also, commas count as whitespace,
|
||||
so you can omit them (for example, you can write a vector as <code>[1 2 3]</code>
|
||||
instead of <code>[1, 2, 3]</code>).</p><p>Clojure code is block-structured and lexically scoped (though dynamic
|
||||
scope is supported as well, if you really need it).</p><p>Clojure is a compiled language. The Clojure reader reads your source
|
||||
code, then your code is compiled to JVM bytecode, and then it's run on
|
||||
the JVM. The reader supports a few extra bits of syntactic sugar (for
|
||||
example, a literal syntax for specifying regular expressions) that we
|
||||
will cover as we go along.</p><p>Throughout this tutorial, we will liberally reference and lean on the
|
||||
marvelous <a href="http://clojure.org/cheatsheet">Clojure Cheatsheet</a>. Aside
|
||||
from being a great organizational aide, it also handily includes links
|
||||
to the relevant <a href="http://clojuredocs.org/">Clojuredocs</a> pages where you
|
||||
can find docs for and examples of the various Clojure functions.</p><p>In the REPL, at any time you can see the documentation for a given
|
||||
function:</p><pre><code>(doc some-function)
|
||||
</code></pre><p>and even the source code for it:</p><pre><code>(source some-function)
|
||||
</code></pre><p>So, it will be of great use to you to have your REPL running so you
|
||||
can try things out while following along.</p><h2 id="identifiers">Identifiers</h2><p>Identifiers are used to name things. For example, in</p><pre><code class="clojure">(def the-answer 42)
|
||||
</code></pre><p>we've named something "the-answer" and given it the value 42.</p><p>In this document we'll mostly use lowercase letters, numbers, and
|
||||
dashes to name things, although some other characters are allowed too,
|
||||
such as <code>_<>!?*</code> (ex. <code>this->that</code>, <code>ready?</code>). We'll note more
|
||||
examples of those cases later on as they come up.</p><h2 id="scalars">Scalars</h2><p>Clojure has support for the following kinds of scalar values:</p><pre><code class="clojure">nil
|
||||
true, false
|
||||
</code></pre><p><code>nil</code> is like Python's None, or Java's null. It's just another value.
|
||||
Incidentally, there's no "undefined" value in Clojure --- if you try
|
||||
to use a symbol which you haven't defined, then it's undefined and the
|
||||
compiler will let you know about it.</p><p>As we go along, type those expressions into your REPL to see them
|
||||
evaluated. These too:</p><pre><code class="clojure">1 ; integer
|
||||
1N ; arbitrary-precision integer
|
||||
1.2 ; float/double/decimal
|
||||
1.2M ; arbitrary-precision decimal
|
||||
1.2e4 ; scientific notation
|
||||
1.2e4M ; sci notation of arbitrary-precision decimal
|
||||
|
||||
0x3a ; hex literal (58 in decimal)
|
||||
1/3 ; Rational number, or "ratio".
|
||||
\a ; The character "a".
|
||||
"hi" ; A string.
|
||||
</code></pre><p>Strings can span multiple lines --- just hit Enter and keep typing. If
|
||||
you want to include a double-quote mark in a string, backslash-escape
|
||||
it.</p><pre><code class="clojure">#"^foo\d?$" ; A regular expression.
|
||||
:foo ; A keyword.
|
||||
</code></pre><p>We'll have more to say about <a href="index.html#regular-expressions">regular
|
||||
expressions</a> later on.</p><p>Keywords are just scalars that evaluate to themselves and are useful
|
||||
where in other languages you might use little strings as identifiers
|
||||
(for example, as the keys in a hashmap). More about keywords in the
|
||||
next section (<a href="index.html#data-structures">Data Structures</a>).</p><pre><code class="clojure">'foo ; A symbol.
|
||||
</code></pre><p>A <em>symbol</em> is an object that represents the <em>name</em> of something. The
|
||||
single quote mark is there to keep Clojure from trying to figure out
|
||||
to what the symbol refers (the quote isn't part of the identifier of
|
||||
the symbol). When you want to represent the name of a thing --- rather
|
||||
than the value to which it refers --- you use a symbol. Their utility
|
||||
will become clearer later on when we briefly mention
|
||||
<a href="index.html#macros-and-special-forms">Macros</a>.</p><blockquote><p><strong>Terminology:</strong> By "object" we just mean the internal thing that
|
||||
Clojure uses to represent a value --- <em>not</em> "object" as in "object
|
||||
oriented programming". Clojure is not an object oriented
|
||||
language. Sure, you can easily access Java OOP objects from Clojure,
|
||||
but that is outside the scope of this tutorial.</p><p>Also, the words "reference" and "refer" are used in Clojure in the
|
||||
generic sense. A symbol refers to an object (it is not the object
|
||||
itself). Clojure <em>also</em> happens to support something called
|
||||
<em>reference types</em>. We'll cover them later on in the <a href="index.html#reference-types">Reference
|
||||
Types</a> section.</p></blockquote><h2 id="data-structures">Data Structures</h2><p>Clojure comes out of the box with nice literal syntax for the various
|
||||
core data structures:</p><pre><code class="clojure">[1 2 3] ; A vector (can access items by index).
|
||||
[1 :two "three"] ; Put anything into them you like.
|
||||
{:a 1 :b 2} ; A hashmap (or just "map", for short).
|
||||
</code></pre><p>A hashmap is your typical hash/dictionary data structure. In the above
|
||||
example, the keys are :a and :b, and the values are 1 and 2. One key-value
|
||||
pair in a map is called an <em>entry</em>.</p><p>Although it's most common to use keywords (as shown above) for hashmap
|
||||
keys, you can use any values you like for the keys as well as the
|
||||
values.</p><pre><code class="clojure">#{:a :b :c} ; A set (unordered, and contains no duplicates).
|
||||
'(1 2 3) ; A list (linked-list)
|
||||
</code></pre><p>You generally don't use lists very often for typical sequential data
|
||||
in your programs:</p><pre><code class="clojure">(def my-stuff '("shirt" "coat" "hat")) ; Works fine, but ...
|
||||
(def my-stuff ["shirt" "coat" "hat"]) ; this is more typical usage.
|
||||
</code></pre><p>Lists are most often used when treating code itself as just a bunch of
|
||||
nested lists --- see <a href="index.html#macros-and-special-forms">Macros</a>.</p><p>BTW, don't mind that single-quote mark before the list's open paren;
|
||||
it's just there to tell Clojure that this isn't a function call
|
||||
(discussed in <a href="index.html#function-calls">Function Calls</a>, below), but rather, an actual
|
||||
list.</p><blockquote><p>Note: In Clojure, we use the term "vector" rather than "array".
|
||||
"Array" would refer to the native Java array, whereas "vector"
|
||||
refers to the Clojure data structure.</p></blockquote><p>Nesting data structures works like you'd expect:</p><pre><code class="clojure">#{:a
|
||||
[1 2 3]
|
||||
{:foo 11 :bar 12}
|
||||
#{"shirt" "coat" "hat"}}
|
||||
</code></pre><p>We will see how to get at values inside nested data structures a little
|
||||
later on.</p><h3 id="abstractions">Abstractions</h3><p>The data structures we just looked at (lists, vectors, maps, and sets)
|
||||
are all concrete data types. The various Clojure functions for working
|
||||
on them (which we will get to later on) actually aren't written to
|
||||
work on the concrete types, but rather, are written to work on
|
||||
abstract data types. The concrete data types are implementations of
|
||||
the various abstract data types.</p><p>Some of the Clojure abstractions are:</p><ul><li>Collection (Lists, vectors, maps, and sets are all collections.)</li><li>Sequential (Lists and vectors are ordered collections.)</li><li>Associative (Hashmaps associate keys with values. Vectors associate numeric indices with values.)</li><li>Indexed (Vectors, for example, can be quickly indexed into.)</li></ul><p>In the docs for the various functions, you'll often see that they
|
||||
take, for example, a "coll". This means that the particular function
|
||||
will work on any of the collections.</p><blockquote><p>If you'd like to look under the covers and see what the type of
|
||||
an object is, try <code>(type my-stuff)</code>.</p></blockquote><h2 id="evaluation">Evaluation</h2><p>So far you've been typing various literal values (expressions) into
|
||||
the repl and Clojure has evaluated them and repeated their resulting
|
||||
values back to you (printed them out in the repl):</p><pre><code class="clojure">user=> "hi"
|
||||
;; "hi"
|
||||
user=> :foo
|
||||
;; :foo
|
||||
user=> [1 2 3]
|
||||
;; [1 2 3]
|
||||
</code></pre><p>Clojure evaluates the expressions you give it and tries to come up
|
||||
with a resulting value. If the expression starts with an open paren,
|
||||
Clojure treats it as either a macro, a <em>special form</em> (discussed
|
||||
below) or else a function call.</p><h3 id="function-calls">Function Calls</h3><p>If the symbol right after the open paren names a function, Clojure
|
||||
evaluates all of its function arguments first, then applies the
|
||||
function to the values of those args:</p><pre><code class="clojure">(my-func arg1 arg2 arg3)
|
||||
</code></pre><p>You can nest function calls as deep as tasteful discretion allows:</p><pre><code class="clojure">(my-func (my-func2 arg1
|
||||
arg2)
|
||||
(other-func arg-a
|
||||
(foo-bar arg-x
|
||||
arg-y
|
||||
(+ arg-xx
|
||||
arg-yy
|
||||
arg-zz))
|
||||
arg-b))
|
||||
</code></pre><p>Note that your code will be easiest to read if you line up args to
|
||||
functions vertically (as shown above). Your editor should take care of
|
||||
this for you automatically.</p><p>By the way, there are no "operators" in Clojure per se; just function
|
||||
names (symbols which refer to their corresponding functions). So, for
|
||||
example, <code>+</code>, <code>></code>, <code><=</code>, <code>=</code>, <code>*</code>, and <code>not=</code> are all just function
|
||||
names.</p><h3 id="macros-and-special-forms">Macros and Special Forms</h3><p>If an expression starts with an open paren, Clojure first checks to
|
||||
see if it's a macro or special form. These are forms which don't
|
||||
follow the regular evaluation rule and get special treatment from the
|
||||
Clojure compiler.</p><p>Macros are like functions which take as arguments regular Clojure code
|
||||
(which is, after all, just a list of expressions and (usually nested)
|
||||
other lists), and returns the code transformed / expanded in some
|
||||
useful way.</p><p>You write macros to add new syntax to the Clojure language, and
|
||||
usually it's only done when necessary, after you've already gotten as
|
||||
far as you can with plain functions.</p><p>Macros are created using <code>defmacro</code>. Writing them involves
|
||||
manipulating lists (Clojure code), just like you've already
|
||||
seen. Though quoting and unquoting is used to control evaluation of
|
||||
the code you're handling.</p><p>Macro calls in your code get expanded at compile-time, right before
|
||||
the rest of your code is compiled. Certain Clojure built-ins like
|
||||
<code>let</code>, <code>def</code>, and <code>if</code> are written as special forms which are
|
||||
hard-coded into the compiler rather than macros, but this is an
|
||||
implementation detail; the effect is the same.</p><p>This tutorial does not discuss macros further.</p><h3 id="quoting">Quoting</h3><p>If for whatever reason you'd rather Clojure <em>not</em> treat something like
|
||||
<code>(+ 1 2 3)</code> as a function call, you can "quote" it like so:</p><pre><code class="klipse-clojure nohighlight">'(+ 1 2 3)
|
||||
</code></pre><p>This causes Clojure to then regard it simply as a 4-element list;
|
||||
the first element of which is the symbol for some function. Reasons
|
||||
for wanting to do this will become clearer later on.</p><h2 id="let-and-locals">Let and Locals</h2><p>When you want some lexically-scoped named values to use in a section
|
||||
of your code, you can use the <code>let</code> expression:</p><pre><code class="klipse-clojure nohighlight">(let [width 10
|
||||
height 20
|
||||
thickness 2]
|
||||
(println "hello from inside the `let`.")
|
||||
(* width
|
||||
height
|
||||
thickness))
|
||||
</code></pre><p>The first thing inside the <code>let</code> is a binding vector. In it, you
|
||||
specify the local names you'd like to make available inside the <code>let</code>,
|
||||
along with their values.</p><blockquote><p><strong>Formatting note:</strong> Your readers might appreciate you vertically
|
||||
lining up the values used in the binding vector, as we've done
|
||||
above with 10, 20, and 2.</p></blockquote><p>These local names are symbols that refer directly to the values you
|
||||
set them to.</p><p>You can re-set the symbols in the binding vector multiple times
|
||||
(building it up into the value you need), if you find it useful:</p><pre><code class="klipse-clojure nohighlight">(let [x 2
|
||||
x (* x x)
|
||||
x (+ x 1)]
|
||||
x)
|
||||
</code></pre><p>The <code>let</code> expression itself evaluates to the last expression in its
|
||||
body. You can put other things inside the <code>let</code> (like our <code>println</code>
|
||||
expression, in the previous example), but the overall value of the
|
||||
<code>let</code> is its last expression.</p><blockquote><p>Note that the <code>println</code> expression just evaluates to nil. We don't
|
||||
use its value for anything --- we only care about its <em>side-effects</em>
|
||||
(printing out to the console). More about
|
||||
<a href="index.html#side-effects">Side-Effects</a> shortly.</p></blockquote><h2 id="namespaces">Namespaces</h2><p>Clojure uses <em>namespaces</em> to organize function names into groups
|
||||
and to keep them from colliding with other function names.
|
||||
All function names live in a namespace. All the core functions
|
||||
we've been using thus far are in the clojure.core namespace:</p><pre><code class="klipse-clojure nohighlight">(clojure.core/println "hi")
|
||||
</code></pre><p>That's the fully-qualified name of <code>println</code>. You'd normally have to
|
||||
use the fully-qualified name for functions (or else use an alias to
|
||||
the namespace --- covered in a moment), but Clojure makes all the
|
||||
clojure.core functions automatically available by their unqualified
|
||||
names (that is, sans namespace) for convenience.</p><p>Fully-qualified names are written "namespace/symbol". The namespace
|
||||
may have dots in it, which correspond to directories in your
|
||||
filesystem. For example, the function foo-bar.core/my-func corresponds
|
||||
to the my-func function in src/foo_bar/core.clj. (It's just a bit of
|
||||
the underlying Java platform showing through that you need to use
|
||||
underscores in your directory names instead of dashes).</p><p>It's most common for one source code file to correspond to one
|
||||
namespace, and often comprise one <em>library</em>. At the top of your source
|
||||
file, you write <code>(ns whatever)</code> and that declares the namespace for
|
||||
the rest of the file.</p><p>In the repl, you can make use of libraries --- and at the same time
|
||||
provide a handy alias for them --- by <em>requiring</em> them like so:</p><pre><code class="klipse-clojure nohighlight">(require '[clojure.string :as str])
|
||||
</code></pre><p>Now we can use all the functions in the clojure.string library by
|
||||
prefixing them with "str/". We'll do exactly this in the section below
|
||||
on <a href="index.html#functions-for-working-with-strings">Functions for working with
|
||||
strings</a>.</p><h2 id="functions-for-creating-data-structures">Functions for Creating Data Structures</h2><p>There are functions for creating the various data structures without
|
||||
using the usual literal syntax:</p><pre><code class="clojure">(list 1 2 3) ; ⇒ '(1 2 3)
|
||||
(vector 1 2 3) ; ⇒ [1 2 3]
|
||||
(hash-map :a 1 :b 2) ; ⇒ {:a 1 :b 2}
|
||||
(hash-set :a :b :c) ; ⇒ #{:a :b :c}
|
||||
</code></pre><p>And there are various functions for converting between vectors, sets,
|
||||
and maps:</p><pre><code class="clojure">(def my-vec [1 2 3])
|
||||
(set my-vec) ; ⇒ #{1 2 3}
|
||||
|
||||
(def my-map {:a 1 :b 2})
|
||||
(vec my-map) ; ⇒ [[:a 1] [:b 2]]
|
||||
(flatten (vec my-map)) ; ⇒ (:a 1 :b 2)
|
||||
(set my-map) ; ⇒ #{[:b 2] [:a 1]}
|
||||
|
||||
(def my-set #{:a :b :c :d})
|
||||
(vec my-set) ; ⇒ [:a :c :b :d]
|
||||
|
||||
;; And for fun:
|
||||
(zipmap [:a :b :c] [1 2 3]) ; ⇒ {:c 3 :b 2 :a 1}
|
||||
(apply hash-map [:a 1 :b 2]) ; ⇒ {:a 1 :b 2}
|
||||
</code></pre><p>(We cover <code>apply</code> in the <a href="index.html#bread-and-butter-functions">Bread and Butter
|
||||
functions</a> section.)</p><p>If you need to convert to a sequential collection but don't need fast
|
||||
random access to items via index, you can use <code>seq</code> instead of <code>vec</code>
|
||||
(to convert to a generic linked-list-like ("sequential") data
|
||||
structure). More about <code>seq</code> when we get to <a href="index.html#laziness">Laziness</a>.</p><blockquote><p>By the way, you may have noticed a pattern here: longer function
|
||||
names are for passing in values one-by-one to create the data
|
||||
structure, whereas the shorter function names are for passing in a
|
||||
whole data structure at once:</p><pre><code>literal long name short name
|
||||
------- --------- ------------------
|
||||
() list *{no short name}*
|
||||
[] vector vec
|
||||
{} hash-map *{no short name}*
|
||||
#{} hash-set set
|
||||
</code></pre><p>You might think of <code>seq</code> as the short name for <code>list</code>, but that's
|
||||
probably pushing it, since there are a few differences.</p></blockquote><h2 id="functions-for-working-with-data-structures">Functions For Working With Data Structures</h2><p>Getting values from data structures:</p><pre><code class="clojure">;; Vectors
|
||||
(def v [:a :b :c])
|
||||
(nth v 1) ; ⇒ :b
|
||||
(v 1) ; ⇒ :b (same)
|
||||
(first v) ; ⇒ :a
|
||||
(rest v) ; ⇒ (:b :c)
|
||||
(next v) ; ⇒ (:b :c)
|
||||
(last v) ; ⇒ :c
|
||||
|
||||
;; Lists
|
||||
;; Same as vectors, but can't index.
|
||||
|
||||
;; Maps
|
||||
(def m {:a 1 :b 2})
|
||||
(get m :a) ; ⇒ 1
|
||||
(m :a) ; ⇒ 1 (same)
|
||||
(:a m) ; ⇒ 1 (same!)
|
||||
(get m :x 44) ; ⇒ 44 (if no :x, 44 is the default)
|
||||
(keys m) ; ⇒ (:a :b)
|
||||
(vals m) ; ⇒ (1 2)
|
||||
;; Grab a key or a val from a single map entry:
|
||||
(key (first m)) ; ⇒ :a
|
||||
(val (first m)) ; ⇒ 1
|
||||
;; Of course, note that maps are not ordered.
|
||||
|
||||
;; Sets
|
||||
(def s #{:a :b :c})
|
||||
(s :a) ; ⇒ :a
|
||||
(s :z) ; ⇒ nil
|
||||
</code></pre><p>Data structures in Clojure are actually <em>immutable</em> --- you can't
|
||||
change them. Though it may sound batty, it actually works out nicely
|
||||
in practice, and we'll read more about in the
|
||||
<a href="index.html#values,-immutability,-and-persistence">Immutability</a> section
|
||||
below. For now, just note that data structures can't be mutated, but
|
||||
we <em>can</em> get a new modified copy of a data structure:</p><pre><code class="clojure">;; Vectors
|
||||
(def v [:a :b :c])
|
||||
(def li '(:a :b :c))
|
||||
(conj v :d) ; ⇒ [:a :b :c :d]
|
||||
(conj li :d) ; ⇒ (:d :a :b :c)
|
||||
|
||||
v ; ⇒ is still [:a :b :c]
|
||||
li ; ⇒ is still (:a :b :c)
|
||||
|
||||
;; Maps
|
||||
(def m {:a 1 :b 2})
|
||||
(assoc m :c 3) ; ⇒ {:a 1 :c 3 :b 2}
|
||||
(dissoc m :b) ; ⇒ {:a 1}
|
||||
|
||||
m ; ⇒ is still {:a 1 :b 2}
|
||||
|
||||
;; Sets
|
||||
(def s #{:a :b})
|
||||
(conj s :c) ; ⇒ #{:a :c :b}
|
||||
(disj s :a) ; ⇒ #{:b}
|
||||
|
||||
s ; ⇒ is still #{:a :b}
|
||||
</code></pre><p>See the <a href="http://clojure.org/cheatsheet">cheatsheet</a> for much more
|
||||
you can do with these core data structures.</p><h2 id="regular-expressions">Regular Expressions</h2><p>As you've seen, Clojure provides a handy literal syntax for regular
|
||||
expressions: <code>#"regex here"</code>. Clojure uses the same regular expression
|
||||
syntax as Java, which is nearly the same as what Perl 5 (and Python,
|
||||
and Ruby) uses. You can read more about the specifics in the Java
|
||||
<a href="http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html">java.util.regex Pattern
|
||||
docs</a>.</p><p>Clojure provides a number of functions for working with strings, and a
|
||||
number of those can make use of regexes. See the next section for some
|
||||
examples.</p><h2 id="functions-for-working-with-strings">Functions For Working With Strings</h2><p>There are a number of functions for working with strings listed in the
|
||||
Strings section of the cheatsheet. Here are some examples of a few of
|
||||
them:</p><pre><code class="clojure">(str "hi" "there")
|
||||
;; ⇒ "hithere"
|
||||
(count "hello")
|
||||
;; ⇒ 5
|
||||
(require '[clojure.string :as str])
|
||||
;; ⇒ nil
|
||||
(str/split "hello there" #" ")
|
||||
;; ⇒ ["hello" "there"]
|
||||
(str/join ["hello" "there"])
|
||||
;; ⇒ "hellothere"
|
||||
(str/join " " ["hello" "there"])
|
||||
;; ⇒ "hello there"
|
||||
(str/replace "hello there" "ll" "LL")
|
||||
;; ⇒ "heLLo there"
|
||||
</code></pre><p>Some of them make optional use of regexes. There's more in the
|
||||
cheatsheet. Try them out!</p><p>Incidentally, since strings are sequential, any function that works on
|
||||
sequentials works on strings. For example:</p><pre><code class="clojure">(first "hello")
|
||||
;; ⇒ \h
|
||||
(last "hello")
|
||||
;; ⇒ \o
|
||||
(rest "hello")
|
||||
;; ⇒ (\e \l \l \o)
|
||||
(nth "hello" 1)
|
||||
;; ⇒ \e
|
||||
(doseq [letter "hello"] (println letter))
|
||||
;; h
|
||||
;; e
|
||||
;; l
|
||||
;; l
|
||||
;; o
|
||||
;; ⇒ nil
|
||||
</code></pre><p>Again, see the cheatsheet for more.</p><h2 id="values-immutability-and-persistence">Values, Immutability, and Persistence</h2><p>A <em>value</em> is fundamentally a constant thing; For example, the letter
|
||||
"a" is a value. You don't "set" the letter "a" to some other value; it
|
||||
always stays the letter "a". It's immutable. The value 10 is always
|
||||
10. You can't ever "set 10 to 11". That makes no sense. If you want
|
||||
11, you just use 11 instead of 10.</p><p>In Clojure, <em>all scalars and core data structures are like this</em>. They
|
||||
are values. They are immutable. The map</p><pre><code class="clojure">{:name "John"
|
||||
:hit-points 200
|
||||
:super-power :resourcefulness}
|
||||
</code></pre><p>is a value. If you want to "change" John's hit-points, you don't
|
||||
change anything per se, but rather, you just conjure up a whole new
|
||||
hashmap value.</p><p><strong>But wait:</strong> If you've done any imperative style programming in
|
||||
C-like languages, this sounds crazy wasteful. <em>However</em>, the yin to
|
||||
this immutability yang is that --- behind the scenes --- Clojure
|
||||
shares data structures. It keeps track of all their pieces and
|
||||
re-uses them pervasively. For example, if you have a 1,000,000-item
|
||||
list and want to tack on one more item, you just tell Clojure, "give
|
||||
me a new one but with this item added" --- and Clojure dutifully gives
|
||||
you back a 1,000,001-item list in no time flat. Unbeknownst to you
|
||||
it's re-using the original list.</p><p>Clojure data structures are said to be <em>persistent</em>.</p><p>And, again: this works just fine because to you the data structures
|
||||
are all immutable. There is no "action at a distance". Other functions
|
||||
can't change the value of a data structure you're working on because
|
||||
values don't change.</p><blockquote><p>Note that, of course, Clojure doesn't do any unnecessary copying.
|
||||
For example, when you pass a large data structure to a function</p><pre><code>(my-func a-really-big-data-structure)
|
||||
</code></pre><p>it merely passes along a reference to the big data structure. You
|
||||
can't change it in the caller's scope, because of course it's
|
||||
immutable.</p><p>And of course, you don't get any action-at-a-distance in situations
|
||||
like this either:</p><pre><code class="clojure">(def a [1 2 3 4 5])
|
||||
(def b a)
|
||||
;; Do what you will with `b`, ...
|
||||
(my-func a) ; but it won't affect `a`.
|
||||
</code></pre><p>since, regardless, you can't mutate the vector (neither via <code>b</code><em>nor</em> <code>a</code>).</p></blockquote><p>If you're wondering how the heck it's even possible to program at all
|
||||
if you don't have "variables" and can't change anything, it will
|
||||
become clear as we continue.</p><h2 id="control-structures">Control Structures</h2><p>Clojure has most of the usual control structures you'd expect to find,
|
||||
for example: <code>if</code>, <code>and</code>, <code>or</code>, and <code>cond</code>. You can find them listed
|
||||
in the <a href="http://clojure.org/cheatsheet">Cheatsheet</a>.</p><p>Note that they are all <em>expressions</em> in Clojure, and evaluate to
|
||||
something. So, for example, this <code>if</code> expression:</p><pre><code class="clojure">(if motor-turning?
|
||||
"yes"
|
||||
"no")
|
||||
</code></pre><p>Evaluates to either the value "yes" or the value "no".</p><p>Looping is handled by either using one of the various built-in
|
||||
functions such as <code>map</code>, <code>filter</code>, <code>reduce</code>, <code>for</code>, etc., or else it's
|
||||
handled by manually using <code>loop</code> and using recursion. We'll get to
|
||||
these shortly.</p><p>Incidentally, looping is something that is required far less in
|
||||
Clojure than in imperative languages like Python and Java. The
|
||||
functions that Clojure provides often makes looping unnecessary.
|
||||
For example, where in Python you might do something like this:</p><pre><code class="python">specific_stuff = []
|
||||
for i in my_items:
|
||||
if is_what_i_want(i):
|
||||
specific_stuff.append(i)
|
||||
</code></pre><p>in Clojure you lose the loop and it becomes:</p><pre><code class="clojure">(def specific-stuff (filter what-i-want? my-items))
|
||||
</code></pre><p>This sort of thing comes up again and again, and we'll cover more
|
||||
examples of it in the <a href="index.html#bread-and-butter-functions">Bread and Butter
|
||||
functions</a> section.</p><h2 id="truthiness">Truthiness</h2><p>In <code>(if <test> <then-this> <otherwise-this>)</code> (and in <code>and</code>, <code>or</code>,
|
||||
<code>cond</code>, etc. expressions), Clojure checks if the <code><test></code> evaluates to
|
||||
something that looks either true or false. Clojure takes a very simple
|
||||
approach here: <code>nil</code> and <code>false</code> are falsey; everything else is
|
||||
truthy.</p><p>This means that zero, the empty string, and empty core data structures
|
||||
are all true:</p><pre><code class="clojure">(if 0 :t :f) ; ⇒ :t
|
||||
(if "" :t :f) ; ⇒ :t
|
||||
(if [] :t :f) ; ⇒ :t
|
||||
(if {} :t :f) ; ⇒ :t
|
||||
(if #{} :t :f) ; ⇒ :t
|
||||
</code></pre><p>If you want to check if one of those is <em>empty</em>, you could use the
|
||||
<code>empty?</code> function, though, the docs recommend using this idiom:</p><pre><code class="clojure">(if (seq my-stuff)
|
||||
"still has stuff left"
|
||||
"all gone")
|
||||
</code></pre><h2 id="equality">Equality</h2><p>You'll often check for equality using <code>=</code> (and likewise inequality
|
||||
using <code>not=</code>), for example:</p><pre><code class="clojure">(if (= tries max-tries)
|
||||
"you're done"
|
||||
"keep going")
|
||||
</code></pre><p><code>=</code> recursively checks equality of nested data structures (and
|
||||
considers lists and vectors containing the same values in the same
|
||||
order as equal), for example:</p><pre><code class="clojure">(= {:a [1 2 3] :b #{:x :y} :c {:foo 1 :bar 2}}
|
||||
{:a '(1 2 3) :b #{:y :x} :c {:bar 2 :foo 1}})
|
||||
;; ⇒ true
|
||||
</code></pre><p>There's also a double-equals function <code>==</code> that is more forgiving
|
||||
across various types of numbers:</p><pre><code class="clojure">(= 4 4.0)
|
||||
;; ⇒ false
|
||||
(== 4 4.0)
|
||||
;; ⇒ true
|
||||
</code></pre><p>See the docs for
|
||||
<a href="http://clojuredocs.org/clojure_core/clojure.core/=">=</a> and
|
||||
<a href="http://clojuredocs.org/clojure_core/clojure.core/==">==</a> for more
|
||||
info.</p><h2 id="predicates-and-comparators">Predicates and Comparators</h2><p><em>Predicates</em> are functions that take one or more arguments and return
|
||||
a true or false value. They usually are named with a trailing question
|
||||
mark, for example, <code>even?</code>, <code>odd?</code>, <code>nil?</code>, etc. Though, some names
|
||||
don't have the question mark, such as <code>></code>, <code>>=</code>, <code><</code>, <code><=</code>, <code>=</code>, <code>==</code>,
|
||||
and <code>not=</code>.</p><p><em>Comparators</em> are functions that take 2 args and return -1, 0, or 1
|
||||
depending upon whether the first arg is less than, equal to, or
|
||||
greater than the second arg. The main one is <code>compare</code>.</p><h2 id="vars">Vars</h2><p>Near the top of this tutorial is the following definition:</p><pre><code class="clojure">(def the-answer 42)
|
||||
</code></pre><p>The thing being defined here (behind the scenes) is officially called
|
||||
a <em>Var</em>. The symbol "<code>the-answer</code>" refers to that var which itself
|
||||
refers to the value 42:</p><p>the-answer (a symbol) → a var → 42 (a value).</p><p>When Clojure sees "<code>the-answer</code>", it automatically looks up the var,
|
||||
then from there finds and returns the value 42.</p><p>Recall that <a href="index.html#let-and-locals">locals</a> don't involve vars at all:
|
||||
those symbols refer directly to their values.</p><h2 id="functions-defining-your-own">Functions: Defining Your Own</h2><p>You can create a function using <code>fn</code>, and give it a name using <code>def</code>:</p><pre><code class="klipse-clojure nohighlight">(def my-func
|
||||
(fn [a b]
|
||||
(println "adding them!")
|
||||
(+ a b)))
|
||||
</code></pre><p>As you might guess, this actually creates the symbol <code>my-func</code> which
|
||||
refers to a var which itself refers to the function (which is a
|
||||
value). Call it:</p><pre><code class="klipse-clojure nohighlight">(my-func 10 20)
|
||||
</code></pre><p>But for creating top-level functions, it's more convenient to use
|
||||
<code>defn</code> (which uses <code>def</code> under the hood):</p><pre><code class="clojure">(defn my-func
|
||||
"Docstring goes here."
|
||||
[a b]
|
||||
(println "adding them!")
|
||||
(+ a b))
|
||||
</code></pre><p>A few points to note:</p><ul><li>The function parameters (<code>a</code> and <code>b</code>) are present in a vector
|
||||
(just like with the <code>let</code> expression, except we don't include
|
||||
values for them).</li><li>Inside <code>my-func</code> you can do a sequence of operations if you like
|
||||
(for example, our <code>println</code> call) --- just like in a <code>let</code> --- but
|
||||
the value of the last expression is what the function call as a
|
||||
whole will evaluate to.</li><li>Function definitions (using <code>defn</code>) should only go at the
|
||||
"top-level".</li></ul><p>Functions can return data structures instead of just scalars:</p><pre><code class="clojure">(defn foo
|
||||
[x]
|
||||
[x (+ x 2) (* x 2)])
|
||||
</code></pre><p>and you can of course pass them data structures as well:</p><pre><code class="clojure">(defn bar
|
||||
[x]
|
||||
(println x))
|
||||
|
||||
(bar {:a 1 :b 2})
|
||||
(bar [1 2 3])
|
||||
</code></pre><p>To define a function to take, say, two or more arguments:</p><pre><code class="clojure">(defn baz
|
||||
[a b & the-rest]
|
||||
(println a)
|
||||
(println b)
|
||||
(println the-rest))
|
||||
</code></pre><p>Any additional args you pass beyond the first two get packaged into a
|
||||
sequence assigned to <code>the-rest</code>. To have that function take <em>zero</em> or
|
||||
more arguments, change the parameter vector to just <code>[& the-rest]</code>.</p><h3 id="layout-of-functions">Layout of Functions</h3><p>Your author likes to write his functions in a top-down fashion:</p><pre><code class="clojure">;; BROKEN pseudocode
|
||||
|
||||
(do-it)
|
||||
|
||||
(defn do-it
|
||||
[]
|
||||
(... (my-func-a ...)))
|
||||
|
||||
(defn my-func-a
|
||||
[...]
|
||||
(... (my-func-b ...)))
|
||||
|
||||
(defn my-func-b ...)
|
||||
</code></pre><p>but Clojure doesn't like that because it wants to have at least
|
||||
<em>heard</em> about a function before you write a call to it. To let Clojure
|
||||
know about a function's existence, use <code>declare</code>:</p><pre><code class="clojure">;; pseudocode
|
||||
|
||||
(declare do-it)
|
||||
|
||||
(do-it)
|
||||
|
||||
(declare my-func-a)
|
||||
|
||||
(defn do-it
|
||||
[]
|
||||
(... (my-func-a ...)))
|
||||
|
||||
(declare my-func-b)
|
||||
|
||||
(defn my-func-a
|
||||
[...]
|
||||
(... (my-func-b ...)))
|
||||
|
||||
(defn my-func-b ...)
|
||||
</code></pre><h2 id="side-effects">Side-effects</h2><p>Some expressions in Clojure have side-effects. Many do not. All
|
||||
expressions evaluate to something.</p><p>For example, <code>(+ 1 2)</code> evaluates to 3 and has no side-effects.
|
||||
<code>(println "hi")</code> evaluates to nil and has the side-effect of printing
|
||||
"hi" to standard out. You usually call <code>println</code> for the side-effect,
|
||||
not for the return value.</p><p>Pure functions are those which have no side-effects and which do not
|
||||
depend upon anything outside to compute their return value(s): you
|
||||
pass it one or more values, and it returns one or more values.</p><p>If you want to make an expression that has some side-effects before
|
||||
it evaluates to a value, use <code>do</code>:</p><pre><code class="clojure">(do
|
||||
(println "Spinning up warp drive, captain ...")
|
||||
(spin-up-warp-drive)
|
||||
(get-engine-temperature))
|
||||
</code></pre><p>There are a handful of functions/macros/special-forms in Clojure for
|
||||
making use of side-effects, and they are spelled with a "do" at the
|
||||
beginning. Try these on for size:</p><pre><code class="klipse-clojure nohighlight">(def my-items ["shirt" "coat" "hat"])
|
||||
|
||||
(doseq [i my-items]
|
||||
(println i))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(dotimes [i 10]
|
||||
(println "counting:" i))
|
||||
</code></pre><p>There's also <code>dorun</code> and <code>doall</code>, both of which are discussed below in
|
||||
the section on <a href="index.html#laziness">Laziness</a>.</p><p>We say that <code>let</code> expressions and function bodies (and also <code>loop</code>
|
||||
expressions, which you'll read about later in <a href="index.html#looping-and-recursion">Looping and
|
||||
Recursion</a>) have an "implicit do": within them
|
||||
you can list expressions one after another, and they all get evaluated
|
||||
in order (presumably for the side-effects), but the last one is what
|
||||
determines the overall resulting value of the <code>let</code> expression.</p><blockquote><p>Incidentally, if in the binding vector of a <code>let</code> you'd like to have
|
||||
some side-effects happen and aren't really concerned about the local
|
||||
values involved, it's customary to use "_" (an underscore) as the
|
||||
identifier:</p><pre><code class="clojure">(let [_ (do-something)
|
||||
_ (println "done with that")
|
||||
x 10]
|
||||
...)
|
||||
</code></pre><p>There's nothing special about the identifier "_" --- it's just
|
||||
shorter to type than, say, "this-is-of-no-consequence".</p></blockquote><p>There's a version of <code>if</code> which supports no "else" expression and
|
||||
which provides an "implicit do": it's spelled "<code>when</code>" (and likewise
|
||||
with <code>if-not</code> ↔ <code>when-not</code>).</p><h2 id="destructuring">Destructuring</h2><p>Clojure provides a little bit of extra syntactic support for assigning
|
||||
values to locals in <code>let</code> expressions and function definitions. Using
|
||||
<code>let</code> as an example, suppose you have a nested data structure, and
|
||||
you'd like to assign some values in it to locals. Where you <em>could</em> do
|
||||
this:</p><pre><code class="clojure">(def games [:chess :checkers :backgammon :cards])
|
||||
|
||||
(let [game-a (games 0)
|
||||
game-b (games 1)
|
||||
game-c (games 2)
|
||||
game-d (games 3)]
|
||||
...
|
||||
...)
|
||||
</code></pre><p>Destructuring allows you to instead write:</p><pre><code class="clojure">(let [[game-a game-b game-c game-d] games]
|
||||
...
|
||||
...)
|
||||
</code></pre><p>The thing to the left of "games" in the binding vector is referred to
|
||||
as the "binding form". In the above case, the binding form is a
|
||||
vector.</p><p>The way it works is: if the binding form is a vector, Clojure assumes
|
||||
that the thing you're trying to assign to it must also be a vector,
|
||||
and so it unpacks the values from that data structure into the
|
||||
corresponding items listed in the binding form.</p><p>If you want to omit one or more of the values in the <code>games</code>, you
|
||||
can do so like this:</p><pre><code class="clojure">(let [[_ my-game _ your-game] games]
|
||||
...
|
||||
...)
|
||||
</code></pre><p>The underscore is just used as a placeholder. It's a valid identifier,
|
||||
but conventionally used when you don't care what value it gets. Above,
|
||||
my-game gets :checkers and your-game gets :cards.</p><p>Destructuring also works for maps in additon to vectors. For example,
|
||||
instead of:</p><pre><code class="clojure">(def concert {:band "The Blues Brothers"
|
||||
:location "Palace Hotel Ballroom"
|
||||
:promos "Ladies night, tonight"
|
||||
:perks "Free parking"})
|
||||
|
||||
(let [band (concert :band)
|
||||
location (concert :location)
|
||||
promos (concert :promos)
|
||||
perks (concert :perks)]
|
||||
...
|
||||
...)
|
||||
</code></pre><p>you <em>could</em> do:</p><pre><code class="clojure">(let [{band :band
|
||||
location :location
|
||||
promos :promos
|
||||
perks :perks} concert]
|
||||
...
|
||||
...)
|
||||
</code></pre><p>but an even better shortcut that destructuring provides for that is:</p><pre><code class="clojure">(let [{:keys [band location promos perks]} concert]
|
||||
...
|
||||
...)
|
||||
</code></pre><h2 id="laziness">Laziness</h2><p>Most of the sequences Clojure creates (via calls to <code>map</code>, <code>reduce</code>,
|
||||
<code>filter</code>, <code>for</code>, etc. --- covered in the next section) are <em>lazy</em>. A
|
||||
lazy sequence is one that isn't <em>realized</em> (computed) all at
|
||||
once. Instead, its values are only realized when you ask for them. If
|
||||
you've only asked for the first 5 values of a lazy seq, then that seq
|
||||
consists of 5 values plus a box that makes more values only when you
|
||||
ask for them. .</p><p>A nice feature of laziness is that you can create lazy infinite
|
||||
sequences but only realize (and consume memory for) the first <em>n</em> that
|
||||
you actually need.</p><p>Be aware that the repl causes lazy lists to be fully realized if you
|
||||
ask to see their value (which one is apt to do). After using the repl
|
||||
for a while, you start to get a false sense of eagerness. <code>;)</code></p><p>If you've got some code that generates a lazy seq and you want to realize
|
||||
the whole thing right then and there, you can either use</p><ul><li><code>(doall my-lazy-seq)</code> (to get the whole thing), or else</li><li><code>(dorun my-lazy-seq)</code> (to realize each value (presumably for some
|
||||
side-effects you're expecting to get in the process) but then
|
||||
forget it as you proceed to realize the next one).</li></ul><h2 id="bread-and-butter-functions">Bread and Butter Functions</h2><p>Given Clojure's extensive use of immutability, persistent data
|
||||
structures, and laziness, one of its strong suits is functional
|
||||
programming. To this author, functional programming means:</p><ul><li>treating functions just like any other regular value (for example,
|
||||
passing them as args to other functions)</li><li>writing and using functions that return other functions</li><li>avoiding mutable state, preferring instead Clojure's functional
|
||||
alternatives (<code>map</code>, <code>filter</code>, <code>reduce</code>, etc.) or else just
|
||||
directly using recursion.</li></ul><p>Let's try out some of the power tools that Clojure comes with. In the
|
||||
subsections that follow, we've left out the corresponding links to
|
||||
clojuredocs for the given functions, but you'll probably want to read
|
||||
the docs and see the examples there to get the full story for each.</p><h3 id="map">map</h3><p>With <code>map</code> you can apply a function to every value in a collection.
|
||||
The result is a new collection. You can often use <code>map</code> instead of
|
||||
manually looping over a collection. Some examples using <code>map</code>:</p><pre><code class="klipse-clojure nohighlight">(map inc [10 20 30])
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(map str [10 20 30])
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; You can define the function to be used on-the-fly:
|
||||
(map (fn [x] (str "=" x "=")) [10 20 30])
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; And `map` knows how to apply the function you give it
|
||||
;; to multiple collections in a coordinated way:
|
||||
(map (fn [x y] (str x y)) [:a :b :c] [1 2 3])
|
||||
</code></pre><p>When working on more than one collection at a time, <code>map</code> is smart
|
||||
enough to stop when the shorter of the colls runs out of items:</p><pre><code class="klipse-clojure nohighlight">(map (fn [x y] (str x y)) [:a :b :c] [1 2 3 4 5 6 7])
|
||||
</code></pre><h3 id="filter-and-remove">filter and remove</h3><p>Use <code>filter</code> with a predicate function to pare down a collection to
|
||||
just the values for which <code>(the-pred the-value)</code> returns true:</p><pre><code class="klipse-clojure nohighlight">(filter odd? (range 10))
|
||||
</code></pre><p>Use <code>remove</code> for the opposite effect (which amounts to <em>removing</em> the
|
||||
items for which <code>(pred val)</code> returns true):</p><pre><code class="klipse-clojure nohighlight">(remove odd? (range 10))
|
||||
</code></pre><p>You will often find yourself using these functions instead
|
||||
of writing loops like in imperative languages.</p><h3 id="apply">apply</h3><p><code>apply</code> is for when you have a function which takes individual args,
|
||||
for example, <code>max</code>, but the values you'd like to pass to it are in a
|
||||
collection. <code>apply</code> "unpacks" the items in the coll:</p><pre><code class="klipse-clojure nohighlight">(max 1 5 2 8 3)
|
||||
</code></pre><pre><code class="clojure">(max [1 5 2 8 3]) ;; ERROR
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(apply max [1 5 2 8 3])
|
||||
</code></pre><p>A nice feature of <code>apply</code> is that you can supply extra args which
|
||||
you'd like to be treated as if they were part of the collection:</p><pre><code class="klipse-clojure nohighlight">(apply max 4 55 [1 5 2 8 3])
|
||||
</code></pre><h3 id="for">for</h3><p><code>for</code> is for generating collections from scratch (again, without
|
||||
needing to resort to manually looping). <code>for</code> is similar to Python's
|
||||
"list comprehensions". Some examples of using <code>for</code>:</p><pre><code class="klipse-clojure nohighlight">(for [i (range 10)] i)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(for [i (range 10)] (* i i))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(for [i (range 10) :when (odd? i)] [i (str "<" i ">")])
|
||||
</code></pre><p>Notice we snuck a "<code>:when (odd? i)</code>" in there. <code>for</code> even supports a
|
||||
<code>:let</code> modifier in there to set up your values before getting to the
|
||||
body of the <code>for</code> expression.</p><h3 id="reduce">reduce</h3><p><code>reduce</code> is a gem. You use it to apply a function to the first and
|
||||
second items in a coll and get a result. Then you apply it to the
|
||||
result you just got and the 3rd item in the coll. Then the result of
|
||||
<em>that</em> and the 4th. And so on. The process looks something like this:</p><pre><code class="clojure">(reduce + [1 2 3 4 5])
|
||||
;; → 1 + 2 [3 4 5]
|
||||
;; → 3 [3 4 5]
|
||||
;; → 3 + 3 [4 5]
|
||||
;; → 6 [4 5]
|
||||
;; → 6 + 4 [5]
|
||||
;; → 10 [5]
|
||||
;; → 10 + 5
|
||||
;; => 5
|
||||
</code></pre><p>And, of course, you can supply your own function if you like:</p><pre><code class="clojure">(reduce (fn [x y] ...) [...])
|
||||
</code></pre><p>A nice additional feature of <code>reduce</code> is that you can supply a value
|
||||
for it to start off with:</p><pre><code class="klipse-clojure nohighlight">(reduce + 10 [1 2 3 4 5])
|
||||
</code></pre><p>This by itself is pretty handy. But it gets even better. Since you can
|
||||
supply an initial argument, and you can supply your own function, you
|
||||
can use a <em>data structure</em> as that initial argument and have your
|
||||
function "build it up" as you go. For example:</p><pre><code class="clojure">(reduce (fn [accum x]
|
||||
(assoc accum
|
||||
(keyword x)
|
||||
(str x \- (rand-int 100))))
|
||||
{}
|
||||
["hi" "hello" "bye"])
|
||||
|
||||
;; → {}
|
||||
;; → {:hi "hi-29"}
|
||||
;; → {:hi "hi-29" :hello "hello-42"}
|
||||
;; ⇒ {:hi "hi-29" :hello "hello-42" :bye "bye-10"}
|
||||
</code></pre><p>Building up some accumulator using <code>reduce</code> and your own custom
|
||||
function is a fairly common pattern (and once again allows us to
|
||||
avoid looping and manipulations of anything mutable).</p><h3 id="partial-comp-and-iterate">partial, comp, and iterate</h3><p>With <code>partial</code> you can create a function which wraps another one and
|
||||
passes it some standard arguments every time, along with the ones you
|
||||
supply right when you call it. For example:</p><pre><code class="klipse-clojure nohighlight">(defn lots-of-args [a b c d] (str/join "-" [a b c d]))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(lots-of-args 10 20 30 40)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(def fewer-args (partial lots-of-args 10 20 30))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(fewer-args 40)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(fewer-args 99)
|
||||
</code></pre><p><code>comp</code> is for composing a function from other ones. That is, <code>(comp foo bar baz)</code> gives you a function that will first call baz on
|
||||
whatever you pass it, then bar on the result of that, then foo on the
|
||||
result of <em>that</em>, and finally returns the result. Here's a silly
|
||||
example:</p><pre><code class="klipse-clojure nohighlight">(defn wrap-in-stars [s] (str "*" s "*"))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defn wrap-in-equals [s] (str "=" s "="))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(defn wrap-in-ats [s] (str "@" s "@"))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(def wrap-it (comp wrap-in-ats
|
||||
wrap-in-equals
|
||||
wrap-in-stars))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(wrap-it "hi")
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">;; Which is the same as:
|
||||
(wrap-in-ats (wrap-in-equals (wrap-in-stars "hi")))
|
||||
</code></pre><p><code>(iterate foo x)</code> yields an infinite lazy list consisting
|
||||
of:</p><pre><code class="clojure">(x
|
||||
(foo x)
|
||||
(foo (foo x))
|
||||
(foo (foo (foo x)))
|
||||
...)
|
||||
</code></pre><p>To just take the first, say, 5 values from an infinite list, try this:</p><pre><code class="klipse-clojure nohighlight">(defn square [x] (* x x))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(take 5 (iterate square 2))
|
||||
</code></pre><h2 id="looping-and-recursion">Looping and Recursion</h2><p>As you've seen in the previous section, looping is often just handled
|
||||
by various built-in functions such as <code>map</code>, <code>filter</code>, and <code>reduce</code>.
|
||||
You should use those whenever you can. For times when you need more
|
||||
manual control, you can write loops yourself. By-hand.</p><p>A <code>loop</code> expression looks like a <code>let</code>; you set up locals in its
|
||||
binding vector, then the body of the loop is executed. The body has an
|
||||
implicit do, just like <code>let</code> and function bodies. However, within the
|
||||
body of the <code>loop</code> expression you exit at some point with what you
|
||||
have or else loop again. When you loop again, you call the loop (using
|
||||
<code>recur</code>) as if it's a function, passing new values in for the ones you
|
||||
previously set up in the binding vector. The loop calling itself like
|
||||
this is called <em>recursion</em>. Here's a trivial example:</p><pre><code class="klipse-clojure nohighlight">(loop [accum []
|
||||
i 1]
|
||||
(if (= i 10)
|
||||
accum
|
||||
(recur (conj accum i)
|
||||
(inc i))))
|
||||
</code></pre><p>The state in this loop is carried in the <code>accum</code> vector, which we
|
||||
update each time through the loop. <code>i</code> is the counter, and we finally
|
||||
exit the loop (which evaluates to <code>accum</code>) when i equals 10.</p><p><code>accum</code> could be any other data structure, and that call <code>(conj accum i)</code> could be any expression that yields a new data structure to take
|
||||
the old one's place the next time through.</p><p>You don't actually need a <code>loop</code> to use <code>recur</code>. If you use <code>recur</code> in
|
||||
a function body, it will just call the function again, replacing the
|
||||
args it was previously called with with the ones you pass to <code>recur</code>.</p><p>Finally, recall that if you just need looping for the side-effects
|
||||
only, see <code>doseq</code> and <code>dotimes</code>.</p><h2 id="reference-types">Reference Types</h2><p>Although we've been saying all along that Clojure doesn't have
|
||||
"variables", and that everything is immutable, ... that's not entirely
|
||||
true.</p><p>For when you really do need mutability, Clojure offers <em>reference
|
||||
types</em>. And Clojure provides built-in support for helping you mutate
|
||||
them in safe ways.</p><p>Aside from vars (which is a sort of special reference type), there are
|
||||
3 kinds of reference types:</p><ul><li>Atoms</li><li>Refs</li><li>Agents</li></ul><p>You might typically create a reference type like this:</p><pre><code class="clojure">(def my-atom (atom {}))
|
||||
</code></pre><p>This reference type is an atom, and its state is a hashmap (an empty
|
||||
one, for now). Here, the <code>my-atom</code> symbol refers to a var which refers
|
||||
to the atom.</p><p>Although you <em>still</em> can't literally change the value of the atom, you
|
||||
<em>can</em> swap in a new hashmap value for it any time you like. To retrieve
|
||||
the value of the atom, you "deref" it, or just use the shorter "@"
|
||||
syntax. Here's an (atom-specific) example:</p><pre><code class="klipse-clojure nohighlight">(def my-atom (atom {:foo 1}))
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">@my-atom
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">(swap! my-atom update-in [:foo] inc)
|
||||
</code></pre><pre><code class="klipse-clojure nohighlight">@my-atom
|
||||
</code></pre><p>... and we've just changed the state of the atom. (Note, <code>swap!</code> is a
|
||||
function used only for atoms. There are other specific functions for
|
||||
working with the other reference types.)</p><p>The point of having reference types is that, in your programs, you may
|
||||
want to represent an <em>identity</em>. An identity is something that may
|
||||
change its state over time, but is still the same entity,
|
||||
regardless. In Clojure, an identity is represented by a reference
|
||||
type, and its state is represented by a value.</p><p>We won't discuss reference types further in this tutorial. Perhaps
|
||||
someone will write a good topical guide...</p><h2 id="see-also">See Also</h2><ul><li><a href="https://www.4clojure.com/">4Clojure</a> --- try out what you've
|
||||
learned so far by interactively solving a set of interesting
|
||||
programming problems.</li></ul><h2 id="not-covered-in-this-tutorial">Not Covered In This Tutorial</h2><p>To keep this tutorial down to a manageable length, advanced topics or
|
||||
other far (or not so far) corners not covered herein include but
|
||||
aren't limited to: function literals, multiple-arity functions,
|
||||
exceptions, dynamic scoping of vars, namespaced keywords, metadata,
|
||||
any substantial coverage of macros, transients, zippers, delays,
|
||||
futures, promises, refs, agents or anything about multithreading,
|
||||
thread-first, thread-last, trampolines, datatypes, protocols,
|
||||
multimethods, and Java interop.</p><h2 id="contributors">Contributors</h2><p>John Gabriele <a href="mailto:jmg3000@gmail.com">jmg3000@gmail.com</a> (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../getting_started/index.html">« Getting Started with Clojure</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../emacs/index.html">Clojure with Emacs »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="../parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
|
||||
<script>
|
||||
window.klipse_settings = {
|
||||
"selector" : ".klipse-clojure"
|
||||
};
|
||||
</script>
|
||||
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,486 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides: Parsing XML in Clojure</title>
|
||||
|
||||
|
||||
<meta name="description" content="This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.What Version of Clojure Does This Guide Cover?">
|
||||
|
||||
<meta property="og:description" content="This work is licensed under a Creative Commons
|
||||
Attribution 3.0 Unported License (including images &
|
||||
stylesheets). The source is available on
|
||||
Github.What Version of Clojure Does This Guide Cover?">
|
||||
|
||||
<meta property="og:url" content="https://clojure-doc.github.io/articles/tutorials/parsing_xml_with_zippers/" />
|
||||
<meta property="og:title" content="Parsing XML in Clojure" />
|
||||
<meta property="og:type" content="article" />
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/articles/tutorials/parsing_xml_with_zippers/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ><a href="../../../index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="custom-page">
|
||||
<div id="page-header">
|
||||
<h2>Parsing XML in Clojure</h2>
|
||||
</div>
|
||||
|
||||
<p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
|
||||
Attribution 3.0 Unported License</a> (including images &
|
||||
stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on
|
||||
Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.4 and Leiningen 2.x.</p><h2 id="overview">Overview</h2><p>Try as you might, XML is difficult to avoid. This is particularly true
|
||||
in the Java ecosystem. This guide will show you how to parse XML with
|
||||
the minimum amount of pain using the excellent tools available in
|
||||
Clojure.</p><h2 id="parsing-nzb-files">Parsing NZB files</h2><p>For the purpose of the tutorial I have chosen a simple and fairly well
|
||||
known XML file format: NZB. An NZB file is used to describe files to
|
||||
download from NNTP servers. In this tutorial we will take a basic NZB
|
||||
document and turn it into a Clojure map.</p><p>Let us start by creating a new project (for details on using
|
||||
Leiningen, see <a href="https://clojure-doc.org/articles/tutorials/leiningen/">this guide</a>:</p><pre><code class="bash">$ lein new nzb
|
||||
</code></pre><p>Now edit <code>project.clj</code> to contain the following:</p><pre><code class="clojure">(defproject nzb "0.1.0-SNAPSHOT"
|
||||
:description ""
|
||||
:url ""
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:dependencies [[org.clojure/clojure "1.4.0"]
|
||||
[org.clojure/data.zip "0.1.1"]])
|
||||
</code></pre><p>We are including a dependency on
|
||||
<a href="https://github.com/clojure/data.zip">clojure.data.zip</a>, which is a
|
||||
"system for filtering trees, and XML trees in particular".</p><p>Make a dir called <code>dev-resources</code> at the root of your project, and
|
||||
create a file named <code>example.nzb</code> inside of it. This will be the file
|
||||
we use to test our code (taken from
|
||||
<a href="http://en.wikipedia.org/wiki/NZB">wikipedia</a>). <code>dev-resources</code> is by
|
||||
convention the location to store file resources you use during
|
||||
development / testing.</p><p>Put the following XML in the example.nzb file:</p><pre><code class="xml"><?xml version="1.0" encoding="iso-8859-1" ?>
|
||||
<!-- <!DOCTYPE nzb PUBLIC "-//newzBin//DTD NZB 1.1//EN" "http://www.newzbin.com/DTD/nzb/nzb-1.1.dtd"> -->
|
||||
<nzb xmlns="http://www.newzbin.com/DTD/2003/nzb">
|
||||
<head>
|
||||
<meta type="title">Your File!</meta>
|
||||
<meta type="tag">Example</meta>
|
||||
</head>
|
||||
<file poster="Joe Bloggs &lt;bloggs@nowhere.example&gt;" date="1071674882" subject="Here's your file! abc-mr2a.r01 (1/2)">
|
||||
<groups>
|
||||
<group>alt.binaries.newzbin</group>
|
||||
<group>alt.binaries.mojo</group>
|
||||
</groups>
|
||||
<segments>
|
||||
<segment bytes="102394" number="1">123456789abcdef@news.newzbin.com</segment>
|
||||
<segment bytes="4501" number="2">987654321fedbca@news.newzbin.com</segment>
|
||||
</segments>
|
||||
</file>
|
||||
</nzb>
|
||||
</code></pre><p><em><em>Note</em> The eagle eyed among you will notice that I have commented out the
|
||||
DOCTYPE declaration, as this causes an Exception to be thrown. I will
|
||||
show you how to get around this towards the end of the tutorial.</em></p><p>Let's write a high level test to illustrate more clearly what we are
|
||||
trying to do. Open up the <code>test/nzb/core_test.clj</code> file and make enter
|
||||
the following:</p><pre><code class="clojure">(ns nzb.core-test
|
||||
(:use clojure.test
|
||||
nzb.core)
|
||||
(:require [clojure.java.io :as io]))
|
||||
|
||||
(deftest test-nzb->map
|
||||
(let [input (io/resource "example.nzb")]
|
||||
(is (= {:meta {:title "Your File!"
|
||||
:tag "Example"}
|
||||
:files [{:poster "Joe Bloggs <bloggs@nowhere.example>"
|
||||
:date 1071674882
|
||||
:subject "Here's your file! abc-mr2a.r01 (1/2)"
|
||||
:groups ["alt.binaries.newzbin"
|
||||
"alt.binaries.mojo"]
|
||||
:segments [{:bytes 102394
|
||||
:number 1
|
||||
:id "123456789abcdef@news.newzbin.com"}
|
||||
{:bytes 4501
|
||||
:number 2
|
||||
:id "987654321fedbca@news.newzbin.com"}]}]}
|
||||
(nzb->map input)))))
|
||||
</code></pre><p>This should be fairly self-explanatory, I have directly translated the
|
||||
XML into Clojure data structures of maps and vectors. If we were to
|
||||
just use the <code>clojure.xml</code> library to parse the NZB file, we get a
|
||||
tree based representation. For example:</p><pre><code class="clojure">(-> "example.nzb" io/resource io/file xml/parse)
|
||||
{:tag :nzb,
|
||||
:attrs {:xmlns "http://www.newzbin.com/DTD/2003/nzb"},
|
||||
:content
|
||||
[{:tag :head,
|
||||
:attrs nil,
|
||||
:content
|
||||
[{:tag :meta, :attrs {:type "title"}, :content ["Your File!"]}
|
||||
{:tag :meta, :attrs {:type "tag"}, :content ["Example"]}]}
|
||||
{:tag :file,
|
||||
:attrs
|
||||
{:poster "Joe Bloggs <bloggs@nowhere.example>",
|
||||
:date "1071674882",
|
||||
:subject "Here's your file! abc-mr2a.r01 (1/2)"},
|
||||
:content
|
||||
[{:tag :groups,
|
||||
:attrs nil,
|
||||
:content
|
||||
[{:tag :group, :attrs nil, :content ["alt.binaries.newzbin"]}
|
||||
{:tag :group, :attrs nil, :content ["alt.binaries.mojo"]}]}
|
||||
{:tag :segments,
|
||||
:attrs nil,
|
||||
:content
|
||||
[{:tag :segment,
|
||||
:attrs {:bytes "102394", :number "1"},
|
||||
:content ["123456789abcdef@news.newzbin.com"]}
|
||||
{:tag :segment,
|
||||
:attrs {:bytes "4501", :number "2"},
|
||||
:content ["987654321fedbca@news.newzbin.com"]}]}]}]}
|
||||
</code></pre><p>That's great, and can sometimes be enough. But I would rather work
|
||||
with the representation I have in the test. To do that, we need a way
|
||||
of traversing this tree and picking out the pieces of information we
|
||||
require. The <code>clojure.zip</code> and <code>clojure.data.zip</code> libraries are
|
||||
perfect for this. The
|
||||
<a href="http://clojure.github.com/data.zip/">documentation</a> for the
|
||||
<code>data.zip</code> library on github is nice, but it initially left me a
|
||||
little confused as to how to go about using the library (not being
|
||||
familiar with zippers).</p><h3 id="a-simple-example">A Simple Example</h3><p>Zippers allow you to easily traverse a data structure. Let's play with
|
||||
it in a REPL and start with the root node of our NZB file:</p><pre><code class="clojure">(require '[clojure.java.io :as io])
|
||||
(require '[clojure.xml :as xml])
|
||||
(require '[clojure.zip :as zip])
|
||||
(require '[clojure.data.zip.xml :as zip-xml])
|
||||
|
||||
(def root (-> "example.nzb" io/resource io/file xml/parse zip/xml-zip))
|
||||
</code></pre><p>Now we have a zipper for the root element of our document, we can
|
||||
start traversing it for information. The two main functions we will
|
||||
use for this are <code>xml-></code> and <code>xml1-></code>. The former returns a sequence
|
||||
of items based on the predicates given to it, the latter returning the
|
||||
first matching item. As an example, let's get the meta data from the NZB
|
||||
document <code>root</code> and create a Clojure map:</p><pre><code class="clojure">(into {}
|
||||
(for [m (zip-xml/xml-> root :head :meta)]
|
||||
[(keyword (zip-xml/attr m :type))
|
||||
(zip-xml/text m)]))
|
||||
;; => {:title "Your File!", :tag "Example"}
|
||||
</code></pre><p>A couple of things are happening here. First of all we use <code>xml-></code> to
|
||||
return a sequence of <code><meta></code> tags that live under the <code><head></code> tag:</p><pre><code class="clojure">(zip-xml/xml-> root :head :meta)
|
||||
</code></pre><p>We use the <code>for</code> list comprehension macro to evaluate each item in the
|
||||
sequence. For each item we find the contents of the <code>:type</code> attribute
|
||||
using the <code>attr</code> function:</p><pre><code class="clojure">(keyword (zip-xml/attr m :type))
|
||||
</code></pre><p>This returns us the contents of the attribute as a string, which we
|
||||
turn into a <code>keyword</code> to use as the key in the map. We then use the
|
||||
<code>text</code> function to get the textual contents of the meta tag:</p><pre><code class="clojure">(zip-xml/text m)
|
||||
</code></pre><p>We make a tuple of these values, and pass the resulting sequence to
|
||||
<code>into</code> to build the map.</p><h2 id="putting-it-together">Putting It Together</h2><p>Using only these functions, we can parse the raw XML into the Clojure
|
||||
data structure from our unit test. If you like, open
|
||||
<code>./src/nzb/core.clj</code>, and make the changes as you read along.</p><p>First let's define our <code>nzb->map</code> function from the test, and pull in
|
||||
the code we have already written for parsing the metadata of the NZB:</p><pre><code class="clojure">(ns nzb.core
|
||||
(:require [clojure.xml :as xml]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.zip :as zip]
|
||||
[clojure.data.zip.xml :as zip-xml]))
|
||||
|
||||
(defn meta->map
|
||||
[root]
|
||||
(into {}
|
||||
(for [m (zip-xml/xml-> root :head :meta)]
|
||||
[(keyword (zip-xml/attr m :type))
|
||||
(zip-xml/text m)])))
|
||||
|
||||
(defn file->map
|
||||
[file]
|
||||
;; TODO
|
||||
)
|
||||
|
||||
(defn nzb->map
|
||||
[input]
|
||||
(let [root (-> input
|
||||
io/input-stream
|
||||
xml/parse
|
||||
zip/xml-zip)]
|
||||
{:meta (meta->map root)
|
||||
:files (mapv file->map (zip-xml/xml-> root :file))}))
|
||||
</code></pre><p>The only new thing here is the use of <code>io/input-stream</code> to allow us to
|
||||
use anything as <code>input</code> that the <code>io/input-stream</code> supports. These are
|
||||
currently <code>OutputStream</code>, <code>File</code>, <code>URI</code>, <code>URL</code>, <code>Socket</code>, <code>byte array</code>, and <code>String</code> arguments. See the
|
||||
<a href="http://clojure.github.com/clojure/clojure.java.io-api.html">clojure.java.io</a>
|
||||
docs for details.</p><p>Now let's fill in the <code>file->map</code> function:</p><pre><code class="clojure">(defn segment->map
|
||||
[seg]
|
||||
{:bytes (Long/valueOf (zip-xml/attr seg :bytes))
|
||||
:number (Integer/valueOf (zip-xml/attr seg :number))
|
||||
:id (zip-xml/xml1-> seg zip-xml/text)})
|
||||
|
||||
(defn file->map
|
||||
[file]
|
||||
{:poster (zip-xml/attr file :poster)
|
||||
:date (Long/valueOf (zip-xml/attr file :date))
|
||||
:subject (zip-xml/attr file :subject)
|
||||
:groups (vec (zip-xml/xml-> file :groups :group zip-xml/text))
|
||||
:segments (mapv segment->map
|
||||
(zip-xml/xml-> file :segments :segment))})
|
||||
</code></pre><p>Again, nothing new. We simply pick out the pieces of the document we
|
||||
wish to process using a combination of the <code>xml1-></code>, <code>xml-></code>, <code>attr</code>,
|
||||
and <code>text</code> functions. Run the test, and it should pass.</p><h3 id="prevent-parsing-the-dtd">Prevent Parsing the DTD</h3><p>Interestingly, if we uncomment the DTD declaration in the
|
||||
<code>example.nzb</code> file, our code now explodes with an Exception:</p><pre><code>org.xml.sax.SAXParseException: The markup declarations contained or pointed to by the document type declaration must be well-formed
|
||||
</code></pre><p>We can fix this by swapping out the <code>SAXParserFactory</code> and setting a
|
||||
feature to not validate the DTD. Here's how:</p><p>Update the <code>ns</code> declaration to include some required classes:</p><pre><code class="clojure">(ns nzb.core
|
||||
(:require [clojure.xml :as xml]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.zip :as zip]
|
||||
[clojure.data.zip.xml :as zip-xml])
|
||||
(:import (javax.xml.parsers SAXParser SAXParserFactory)))
|
||||
</code></pre><p>Define a function to switch out the SAXParserFactory:</p><pre><code class="clojure">(defn startparse-sax
|
||||
"Don't validate the DTDs, they are usually messed up."
|
||||
[s ch]
|
||||
(let [factory (SAXParserFactory/newInstance)]
|
||||
(.setFeature factory "http://apache.org/xml/features/nonvalidating/load-external-dtd" false)
|
||||
(let [^SAXParser parser (.newSAXParser factory)]
|
||||
(.parse parser s ch))))
|
||||
</code></pre><p>Update our nzb->map definition to use it:</p><pre><code class="clojure">(defn nzb->map
|
||||
[input]
|
||||
(let [root (-> input
|
||||
io/input-stream
|
||||
(xml/parse startparse-sax)
|
||||
zip/xml-zip)]
|
||||
{:meta (meta->map root)
|
||||
:files (mapv file->map (zip-xml/xml-> root :file))}))
|
||||
</code></pre><p>Yay, our test passes again.</p><h2 id="query-predicates">Query Predicates</h2><p>There are a few other useful functions in the <code>clojure.data.zip.xml</code>
|
||||
ns we haven't yet looked at, namely: <code>text=</code>, <code>attr=</code>, and <code>tag=</code>.
|
||||
These functions allow you to construct query predicates to run against
|
||||
a given node. As an example, let's pull out the first file segment
|
||||
from the <code>example.nzb</code> file using the <code>attr=</code> function:</p><pre><code class="clojure">(zip-xml/xml1-> root
|
||||
:file
|
||||
:segments
|
||||
:segment
|
||||
(zip-xml/attr= :number "1"))
|
||||
zip-xml/text)
|
||||
"123456789abcdef@news.newzbin.com"
|
||||
</code></pre><p>From the root node of the document we reach down into <code>:file</code>,
|
||||
<code>:segments</code>, and <code>:segment</code> in turn, then use the <code>attr=</code> query
|
||||
predicate to match a <code>:segment</code> with a value of <code>"1"</code>.</p><p>Interestingly enough, the other two query predicates have shortcuts
|
||||
for their use. You have already been using the <code>tag=</code> query predicate
|
||||
every time you use a keyword to locate a tag. To use the <code>text=</code>
|
||||
predicate easily, just use a string. For example, to retrieve the
|
||||
second <code>:segment</code> based on its content of
|
||||
<code>987654321fedbca@news.newzbin.com</code>:</p><pre><code class="clojure">(zip-xml/xml1-> root
|
||||
:file
|
||||
:segments
|
||||
:segment
|
||||
"987654321fedbca@news.newzbin.com")
|
||||
;; ... the resulting node
|
||||
</code></pre><p>Finally, you can combine these query predicates to match multiple
|
||||
things on a given node by using a vector:</p><pre><code class="clojure">(zip-xml/xml1-> root
|
||||
:file
|
||||
:segments
|
||||
:segment
|
||||
[(zip-xml/attr= :number "1")
|
||||
(zip-xml/attr= :bytes "102394")]
|
||||
zip-xml/text)
|
||||
"123456789abcdef@news.newzbin.com"
|
||||
</code></pre><p>Here we are matching on both the <code>:number</code> attribute being <code>"1"</code>, and
|
||||
the <code>:bytes</code> attribute being <code>"102394"</code>. Obviously, you can use
|
||||
strings here to match against content too.</p><h2 id="creating-new-predicates">Creating New Predicates</h2><p>OK, now let's suppose we want to use some kind of numerical comparison
|
||||
in our XML (like we might do with XPath). As it stands, we have no way
|
||||
to do that with the built-in functions but we can easily define our
|
||||
own.</p><p>Let's start with a general function for comparing attribute values:</p><pre><code class="clojure">(defn attr-fn
|
||||
[attrname f test-val & [conv-fn]]
|
||||
(fn [loc]
|
||||
(let [conv-fn (or conv-fn identity)
|
||||
val (conv-fn (zip-xml/attr loc attrname))]
|
||||
(f val test-val))))
|
||||
</code></pre><p>This function takes an attribute name (<code>attrname</code>), a function for
|
||||
making a comparison (<code>f</code>), a value to test agains (<code>test-val</code>) and
|
||||
optionally a conversion function. Imagine our <code>example.nzb</code> file had
|
||||
100 segments, and we only wanted to get segments over 75. We could now
|
||||
achieve this using our general function:</p><pre><code class="clojure">(zip-xml/xml-> root
|
||||
:file
|
||||
:segments
|
||||
:segment
|
||||
(attr-fn :number > 75 #(Long/valueOf %))
|
||||
zip-xml/text)
|
||||
</code></pre><p>Let's provide a helper for this to make the syntax clearer:</p><pre><code class="clojure">(defn attr>
|
||||
[attrname val]
|
||||
(attr-fn attrname > val #(Long/valueOf %)))
|
||||
|
||||
(zip-xml/xml-> doc
|
||||
:file
|
||||
:segments
|
||||
:segment
|
||||
(attr> :number 75)
|
||||
zip-xml/text)
|
||||
</code></pre><p>We could build a whole suite of helper functions for examining XML
|
||||
nodes, if we are unlucky enough to be required to do so :)</p><h2 id="conclusion">Conclusion</h2><p>I hope these simple examples have given you an idea of the ease with
|
||||
which you can process XML using Clojure, and how simple it is to
|
||||
extend the tools already provded in interesting directions.</p><h2 id="contributors">Contributors</h2><p><a href="http://blog.gaz-jones.com">Gareth Jones</a>, 2012 (original author)</p>
|
||||
|
||||
<div id="prev-next">
|
||||
|
||||
<a href="../basic_web_development/index.html">« Basic Web Development</a>
|
||||
|
||||
|
||||
||
|
||||
|
||||
|
||||
<a href="../growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="../../about/index.html">About</a></li>
|
||||
|
||||
<li><a href="../../content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="../getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="../introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="../emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="../vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="../eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="../basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="../growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
168
clones/clojure-doc.org/css/screen.css
Normal file
168
clones/clojure-doc.org/css/screen.css
Normal file
|
@ -0,0 +1,168 @@
|
|||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Alegreya';
|
||||
}
|
||||
|
||||
body {
|
||||
color: #333;
|
||||
background-color: #f2f2f2;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 0 0,0 6px 12px rgba(34,34,34,0.3);
|
||||
}
|
||||
|
||||
.navbar-default {
|
||||
background-color: #428bca;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-brand {
|
||||
color: #fff;
|
||||
font-family: 'Alegreya';
|
||||
}
|
||||
|
||||
.navbar-default .navbar-brand:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-nav li a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-nav li a:hover {
|
||||
color: #fff;
|
||||
background-color: #3d80ba;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-nav .active a {
|
||||
color: #fff;
|
||||
background-color: #3d80ba;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-toggle:hover{
|
||||
background-color: #3d80ba;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-toggle .icon-bar {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
margin-left: 15px;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
#content {
|
||||
background-color: #fff;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 0 0,0 6px 12px rgba(34,34,34,0.1);
|
||||
}
|
||||
|
||||
#content img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
padding-top: 75px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
blockquote footer {
|
||||
text-align: left;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
#post-tags {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
#prev-next {
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.post-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.post-header h2 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
#post-meta {
|
||||
font-size: 14px;
|
||||
color: rgba(0,0,0,0.4)
|
||||
}
|
||||
|
||||
#page-header {
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#page-header h2 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow-x: auto;
|
||||
}
|
||||
pre code {
|
||||
display: block;
|
||||
padding: 0.5em;
|
||||
overflow-wrap: normal;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #428bca;
|
||||
}
|
||||
|
||||
pre, code, .hljs {
|
||||
background-color: #f7f9fd;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar {
|
||||
min-height: 70px;
|
||||
}
|
||||
.navbar-nav>li>a {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
.navbar-default .navbar-brand {
|
||||
font-size: 36px;
|
||||
padding: 25px 15px;
|
||||
}
|
||||
#content{
|
||||
margin-top: 30px;
|
||||
padding: 30px 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
body{
|
||||
font-size: 14px;
|
||||
}
|
||||
.navbar-default .navbar-brand {
|
||||
font-size: 30px;
|
||||
}
|
||||
#content{
|
||||
padding: 15px;
|
||||
}
|
||||
#post-meta .right {
|
||||
float:left;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
237
clones/clojure-doc.org/index.html
Normal file
237
clones/clojure-doc.org/index.html
Normal file
|
@ -0,0 +1,237 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Clojure Guides</title>
|
||||
|
||||
<meta name="description" content="Clojure Documentation">
|
||||
<meta name="keywords" content="">
|
||||
|
||||
<link rel="canonical" href="https://clojure-doc.github.io/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
|
||||
<link href="css/screen.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">Clojure Guides</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="active" ><a href="index.html">Home</a></li>
|
||||
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div id="content">
|
||||
|
||||
<div id="post">
|
||||
<div>
|
||||
|
||||
<div class="jumbotron">
|
||||
<div class="container">
|
||||
<h1>Clojure Documentation</h1>
|
||||
<p>Welcome to the community-driven documentation site for the Clojure programming language.</p>
|
||||
|
||||
<a class='btn btn-primary btn-lg' href='articles/tutorials/getting_started/index.html'>Get Started! »</a>
|
||||
<a class="btn btn-lg" href="articles/content/index.html">See all content »</a>
|
||||
<a class='btn btn-lg' href='https://github.com/clojure-doc/clojure-doc.github.io'>Contribute »</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='row'>
|
||||
<div class='col-md-4'>
|
||||
<h2><a href='articles/content/index.html#essentials'>Essentials</a></h2>
|
||||
<p>Tutorials aimed at new users.</p>
|
||||
</div>
|
||||
|
||||
<div class='col-md-4'>
|
||||
<h2><a href='articles/content/index.html#language-guides'>Language Guides</a></h2>
|
||||
<p>Comprehensive guides on every aspect of the core language.</p>
|
||||
</div>
|
||||
|
||||
<div class='col-md-4'>
|
||||
<h2>Contributor-friendly</h2>
|
||||
<p>
|
||||
This material is not covered by the Clojure Contributor Agreement and is developed using <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">pull-requests on GitHub</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='row'>
|
||||
<div class='col-md-4'>
|
||||
<h2><a href='articles/content/index.html#the-clojure-ecosystem'>Ecosystem & Tools</a></h2>
|
||||
<p>Guides covering areas outside of the core language.</p>
|
||||
</div>
|
||||
|
||||
<div class='col-md-4'>
|
||||
<h2><a href='articles/content/index.html#tutorials-and-cookbooks'>Tutorials and Cookbooks</a></h2>
|
||||
<p>Subject-specific tutorials and guides.</p>
|
||||
</div>
|
||||
|
||||
<div class='col-md-4'>
|
||||
<h2>Interactive Examples</h2>
|
||||
<p><a href="https://github.com/viebel/klipse">Klipse</a> is used in several sections to provide
|
||||
live, interactive code examples that you can edit to explore the concepts being
|
||||
discussed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div id="sidebar">
|
||||
<h3>Links</h3>
|
||||
<ul id="links">
|
||||
|
||||
<li><a href="articles/about/index.html">About</a></li>
|
||||
|
||||
<li><a href="articles/content/index.html">Table of Contents</a></li>
|
||||
|
||||
<li><a href="articles/tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
|
||||
|
||||
<li><a href="articles/tutorials/introduction/index.html">Introduction to Clojure</a></li>
|
||||
|
||||
<li><a href="articles/tutorials/emacs/index.html">Clojure with Emacs</a></li>
|
||||
|
||||
<li><a href="articles/tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
|
||||
|
||||
<li><a href="articles/tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
|
||||
|
||||
<li><a href="articles/tutorials/basic_web_development/index.html">Basic Web Development</a></li>
|
||||
|
||||
<li><a href="articles/tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
|
||||
|
||||
<li><a href="articles/tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
|
||||
|
||||
<li><a href="articles/language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
|
||||
|
||||
<li><a href="articles/language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
|
||||
|
||||
<li><a href="articles/language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
|
||||
|
||||
<li><a href="articles/language/functions/index.html">Functions in Clojure</a></li>
|
||||
|
||||
<li><a href="articles/language/laziness/index.html">Laziness in Clojure</a></li>
|
||||
|
||||
<li><a href="articles/language/interop/index.html">Clojure interoperability with Java</a></li>
|
||||
|
||||
<li><a href="articles/language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
|
||||
|
||||
<li><a href="articles/language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
|
||||
|
||||
<li><a href="articles/language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
|
||||
|
||||
<li><a href="articles/language/glossary/index.html">Clojure Terminology Guide</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/web_development/index.html">Web Development (Overview)</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/community/index.html">Clojure Community</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/user_groups/index.html">Clojure User Groups</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
|
||||
|
||||
<li><a href="articles/cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
|
||||
|
||||
<li><a href="articles/cookbooks/strings/index.html">Strings</a></li>
|
||||
|
||||
<li><a href="articles/cookbooks/math/index.html">Mathematics with Clojure</a></li>
|
||||
|
||||
<li><a href="articles/cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
|
||||
|
||||
<li><a href="articles/cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
|
||||
|
||||
<li><a href="articles/cookbooks/middleware/index.html">Middleware in Clojure</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
|
||||
|
||||
<li><a href="articles/ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>Copyright © 2021 Multiple Authors
|
||||
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
|
||||
<script src="js/highlight.pack.js" type="application/javascript"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
2
clones/clojure-doc.org/js/highlight.pack.js
Normal file
2
clones/clojure-doc.org/js/highlight.pack.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -90,6 +90,7 @@ var link_dirs = [
|
|||
["bs", "../bs"],
|
||||
["bug-report", "../bug-report"],
|
||||
["buid", "../buid"],
|
||||
["bv", "../bv"],
|
||||
["bzip2", "../bzip2"],
|
||||
["c", "../c"],
|
||||
["c-defs", "../c-defs"],
|
||||
|
@ -153,6 +154,7 @@ var link_dirs = [
|
|||
["cpu-affinity", "../cpu-affinity"],
|
||||
["cpuinfo", "../cpuinfo"],
|
||||
["crc32c", "../crc32c"],
|
||||
["crontab-manual", "../crontab-manual"],
|
||||
["crypto", "../crypto"],
|
||||
["cs135-drtools", "../cs135-drtools"],
|
||||
["cs2500f16-jsonlab", "../cs2500f16-jsonlab"],
|
||||
|
@ -256,6 +258,7 @@ var link_dirs = [
|
|||
["ebml", "../ebml"],
|
||||
["ebuild", "../ebuild"],
|
||||
["ec", "../ec"],
|
||||
["eclass2scrbl", "../eclass2scrbl"],
|
||||
["ecmascript", "../ecmascript"],
|
||||
["ee-lib", "../ee-lib"],
|
||||
["effection", "../effection"],
|
||||
|
@ -296,6 +299,7 @@ var link_dirs = [
|
|||
["font-finder", "../font-finder"],
|
||||
["for-helpers", "../for-helpers"],
|
||||
["foreign", "../foreign"],
|
||||
["forged-ocelot", "../forged-ocelot"],
|
||||
["formatted-string", "../formatted-string"],
|
||||
["forms", "../forms"],
|
||||
["forth", "../forth"],
|
||||
|
@ -409,6 +413,7 @@ var link_dirs = [
|
|||
["inexact-number-lang", "../inexact-number-lang"],
|
||||
["infix-manual", "../infix-manual"],
|
||||
["infix-syntax", "../infix-syntax"],
|
||||
["ini", "../ini"],
|
||||
["inside", "../inside"],
|
||||
["interactive-brokers-api", "../interactive-brokers-api"],
|
||||
["interconfection", "../interconfection"],
|
||||
|
@ -492,6 +497,7 @@ var link_dirs = [
|
|||
["magenc", "../magenc"],
|
||||
["magnolisp", "../magnolisp"],
|
||||
["main", "../main"],
|
||||
["majordomo2", "../majordomo2"],
|
||||
["make", "../make"],
|
||||
["make-log-interceptor", "../make-log-interceptor"],
|
||||
["manual-flomat", "../manual-flomat"],
|
||||
|
@ -728,6 +734,7 @@ var link_dirs = [
|
|||
["racket-paint", "../racket-paint"],
|
||||
["racket-quandl", "../racket-quandl"],
|
||||
["racket-route-match", "../racket-route-match"],
|
||||
["racket-tree-sitter", "../racket-tree-sitter"],
|
||||
["racket_turtle", "../racket_turtle"],
|
||||
["racketscript", "../racketscript"],
|
||||
["racketui", "../racketui"],
|
||||
|
@ -801,6 +808,7 @@ var link_dirs = [
|
|||
["rokit-racket", "../rokit-racket"],
|
||||
["roman-numeral", "../roman-numeral"],
|
||||
["roomba", "../roomba"],
|
||||
["rosette-guide", "../rosette-guide"],
|
||||
["routy", "../routy"],
|
||||
["rparallel", "../rparallel"],
|
||||
["rpn", "../rpn"],
|
||||
|
@ -814,6 +822,7 @@ var link_dirs = [
|
|||
["ruckus", "../ruckus"],
|
||||
["runomatic", "../runomatic"],
|
||||
["russian", "../russian"],
|
||||
["russian-lang", "../russian-lang"],
|
||||
["rws-html-template", "../rws-html-template"],
|
||||
["rx-tx-async-channel", "../rx-tx-async-channel"],
|
||||
["s3-sync", "../s3-sync"],
|
||||
|
@ -1142,7 +1151,7 @@ function demand_load(p, callback) {
|
|||
|
||||
var loaded_link_targets = [];
|
||||
var link_targets = [];
|
||||
var num_link_target_bins = 20;
|
||||
var num_link_target_bins = 21;
|
||||
|
||||
function convert_all_links() {
|
||||
var elements = document.getElementsByClassName("Sq");
|
||||
|
@ -1166,7 +1175,7 @@ function convert_all_links() {
|
|||
}
|
||||
}
|
||||
if (tag) {
|
||||
var v = hash_string(decodeURIComponent(tag[0].substring(4))) % 20;
|
||||
var v = hash_string(decodeURIComponent(tag[0].substring(4))) % 21;
|
||||
if (!loaded_link_targets[v]) {
|
||||
loaded_link_targets[v] = true;
|
||||
var p = "../local-redirect/local-redirect_" + v + ".js";
|
||||
|
|
File diff suppressed because one or more lines are too long
180
clones/docs.racket-lang.org/reference/Byte_and_String_Input.html
Normal file
180
clones/docs.racket-lang.org/reference/Byte_and_String_Input.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
152
clones/docs.racket-lang.org/reference/Command-Line_Parsing.html
Normal file
152
clones/docs.racket-lang.org/reference/Command-Line_Parsing.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
202
clones/docs.racket-lang.org/reference/Equality.html
Normal file
202
clones/docs.racket-lang.org/reference/Equality.html
Normal file
File diff suppressed because one or more lines are too long
19
clones/docs.racket-lang.org/reference/Exiting.html
Normal file
19
clones/docs.racket-lang.org/reference/Exiting.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
850
clones/docs.racket-lang.org/reference/Filesystem.html
Normal file
850
clones/docs.racket-lang.org/reference/Filesystem.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
49
clones/docs.racket-lang.org/reference/Generators.html
Normal file
49
clones/docs.racket-lang.org/reference/Generators.html
Normal file
File diff suppressed because one or more lines are too long
22
clones/docs.racket-lang.org/reference/Interactive_Help.html
Normal file
22
clones/docs.racket-lang.org/reference/Interactive_Help.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6
clones/docs.racket-lang.org/reference/Macros.html
Normal file
6
clones/docs.racket-lang.org/reference/Macros.html
Normal file
File diff suppressed because one or more lines are too long
237
clones/docs.racket-lang.org/reference/Manipulating_Paths.html
Normal file
237
clones/docs.racket-lang.org/reference/Manipulating_Paths.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
207
clones/docs.racket-lang.org/reference/Namespaces.html
Normal file
207
clones/docs.racket-lang.org/reference/Namespaces.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
51
clones/docs.racket-lang.org/reference/Printer_Extension.html
Normal file
51
clones/docs.racket-lang.org/reference/Printer_Extension.html
Normal file
File diff suppressed because one or more lines are too long
44
clones/docs.racket-lang.org/reference/Random_generation.html
Normal file
44
clones/docs.racket-lang.org/reference/Random_generation.html
Normal file
File diff suppressed because one or more lines are too long
10
clones/docs.racket-lang.org/reference/Reader_Extension.html
Normal file
10
clones/docs.racket-lang.org/reference/Reader_Extension.html
Normal file
File diff suppressed because one or more lines are too long
185
clones/docs.racket-lang.org/reference/Reading.html
Normal file
185
clones/docs.racket-lang.org/reference/Reading.html
Normal file
File diff suppressed because one or more lines are too long
511
clones/docs.racket-lang.org/reference/Sandboxed_Evaluation.html
Normal file
511
clones/docs.racket-lang.org/reference/Sandboxed_Evaluation.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
63
clones/docs.racket-lang.org/reference/Surrogates.html
Normal file
63
clones/docs.racket-lang.org/reference/Surrogates.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue