<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>AstRecipes</title>
    <description>AstRecipes is a community effort to share tasty recipes for your Asterisk PBX.</description>
    <link>http://astrecipes.net/</link>
    <atom:link href="http://astrecipes.net/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 16 Oct 2023 09:26:29 +0200</pubDate>
    <lastBuildDate>Mon, 16 Oct 2023 09:26:29 +0200</lastBuildDate>
    <generator>Jekyll v4.3.2</generator>
    
      <item>
        <title>Command-line apps with Clojure and GraalVM: 300x better start-up times</title>
        <description>&lt;p&gt;I am a fond Clojure user, and love to use it for both server-side projects and small 
command-line tools. I love its expressivity and concision, and I like the fact
that I can have a small “script” and hack on it. The problem I have is that while
this is fine for interactive use (I don’t mind waiting a second to see results) or
for server side projects (where initialization happens just once), it is not a sweet
spot for a command-line tool that I want to deploy to automate something.&lt;/p&gt;

&lt;p&gt;In this case, the cost of firing up the JVM, Clojure and the script itself, vastly
outnumbers the actual processing happening, that is maybe scanning some files; so I see
CPU spikes when my cron jobs run those tasks. Nothing bad, but it’s quite useless
to load a complex environment to process a 100k text file and then throw everything away.&lt;/p&gt;

&lt;h2 id=&quot;enter-graalvm&quot;&gt;Enter GraalVM&lt;/h2&gt;

&lt;p&gt;GraalVM is an extension of the Java virtual machine to support more languages and execution modes. 
The Graal project includes a new high performance Java compiler which can be used in a just-in-time 
configuration on the HotSpot VM, or in an ahead-of-time configuration on the Substrate VM.&lt;/p&gt;

&lt;p&gt;Substrate VM is a framework that allows &lt;em&gt;ahead-of-time compilation of Java applications&lt;/em&gt; 
under closed-world assumption into executable images or shared objects.&lt;/p&gt;

&lt;p&gt;This means that we can create a Clojure command-line app, compile it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:aot :all&lt;/code&gt; into
a set of Java class files, and then compile those into a single, standalone binary file.&lt;/p&gt;

&lt;p&gt;As all initialization is run before compiling into a binary file, it is already present
in the generated image and therefore does not require running again! &lt;em&gt;for Clojure,
with its complex initialization phase, this is a major win&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;limits-of-graalvm--substrate-vm&quot;&gt;Limits of GraalVM / Substrate VM&lt;/h3&gt;

&lt;p&gt;Substrate VM does not support all features of Java to keep the implementation small and concise, 
and also to allow aggressive ahead-of-time optimizations (though it does some pretty impresive things,
e.g. most of Java reflection).&lt;/p&gt;

&lt;p&gt;The biggest issue that we will face is that &lt;strong&gt;dynamic class loading
is not allowed&lt;/strong&gt;. We may not care much about that if we have a closed project where all classes
are defined at compile time, but as Clojure itself uses it in 
&lt;a href=&quot;https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java&quot;&gt;clojure.lang.RT.resourceAsStream&lt;/a&gt;, 
at the moment we have to tell
Graal not to abort at compile time but to defer such exceptions to runtime. Not very elegant,
but it works.&lt;/p&gt;

&lt;p&gt;This also means that not all valid Java libraries will work out-of-the box in GraalVM - see e.g. &lt;a href=&quot;https://discuss.lightbend.com/t/akka-and-graal-s-native-image-tool/940&quot;&gt;Akka and Graal’s native image tool&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The second limit of GraalVM is that in order to compile an even moderately simple executable, it will
require &lt;strong&gt;a lot of RAM&lt;/strong&gt; - 6G to 8G not being uncommon from my own experiments.&lt;/p&gt;

&lt;h2 id=&quot;our-first-graalvm-clojure-application&quot;&gt;Our first GraalVM Clojure application&lt;/h2&gt;

&lt;p&gt;To get started, we want to implement the simple “toycalc” app that ships with CLI-matic - a handy
library that lets you define declaratively the CLI parameters your application accepts, and
takes care of generating online help, validating paramaters, casting values and all of those nitty-gritty
tasks you really do not want to handle when writing a command-line app.&lt;/p&gt;

&lt;h3 id=&quot;the-clojure-side&quot;&gt;The Clojure side&lt;/h3&gt;

&lt;p&gt;First, we create a new Leiningen project:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lein new testgraal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Its main class will be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testgraal.core&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So we edit its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;project.clj&lt;/code&gt; to include our dependencies and set everything up for AOT compilation:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(defproject testgraal &quot;0.1&quot;
  :description &quot;My first test with GraalVM&quot;
  :license {:name &quot;Eclipse Public License&quot;
            :url &quot;http://www.eclipse.org/legal/epl-v10.html&quot;}
  :dependencies [[org.clojure/clojure &quot;1.9.0&quot;]
                 [cli-matic &quot;0.1.14&quot;]]
  :main testgraal.core
  :aot  :all)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We then edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/testgraal/core.clj&lt;/code&gt;; copy and paste the source from &lt;a href=&quot;&quot;&gt;https://github.com/l3nz/cli-matic/blob/master/examples/toycalc.clj&lt;/a&gt;
and modify the namespace declaration so that this namespace is compiled AOT.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(ns testgraal.core
  (:require [cli-matic.core :refer [run-cmd]])
  (:gen-class))

(defn add_numbers
  &quot;Sums A and B together, and prints it in base `base`&quot;
  [{:keys [a1 a2 base]}]
  (println
   (Integer/toString (+ a1 a2) base)))

(defn subtract_numbers
  &quot;Subtracts B from A, and prints it in base `base` &quot;
  [{:keys [pa pb base]}]
  (println
   (Integer/toString (- pa pb) base)))

(def CONFIGURATION
  {:app         {:command     &quot;toycalc&quot;
                 :description &quot;A command-line toy calculator&quot;
                 :version     &quot;0.1&quot;}
   :global-opts [{:option  &quot;base&quot;
                  :as      &quot;The number base for output&quot;
                  :type    :int
                  :default 10}]
   :commands    [{:command     &quot;add&quot; :short &quot;a&quot;
                  :description [&quot;Adds two numbers together&quot;
                                &quot;&quot;
                                &quot;Looks great, doesn&apos;t it?&quot;]
    :opts        [{:option &quot;a1&quot; :short &quot;a&quot; :env &quot;AA&quot; :as &quot;First addendum&quot; :type :int :default 0}
                  {:option &quot;a2&quot; :short &quot;b&quot; :as &quot;Second addendum&quot; :type :int :default 0}]
                  :runs        add_numbers}
                 {:command     &quot;sub&quot;  :short &quot;s&quot;
                  :description &quot;Subtracts parameter B from A&quot;
                  :opts        [{:option &quot;pa&quot; :short &quot;a&quot; :as &quot;Parameter A&quot; :type :int :default 0}
                                {:option &quot;pb&quot; :short &quot;b&quot; :as &quot;Parameter B&quot; :type :int :default 0}]
                  :runs        subtract_numbers}]})

(defn -main
  &quot;This is our entry point.
  Just pass parameters and configuration.
  Commands (functions) will be invoked as appropriate.&quot;
  [&amp;amp; args]
  (run-cmd args CONFIGURATION))                                
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point we can have Leiningen generate everything we need:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lein compile &amp;amp;&amp;amp; lein uberjar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And we get a single, standalone JAR file under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ls -l target/
total 5480
drwxr-xr-x 7 ll staff     224 Jul 19 21:55 classes
drwxr-xr-x 3 ll staff      96 Jul 19 21:55 stale
-rw-r--r-- 1 ll staff 4698514 Jul 19 21:55 testgraal-0.1-standalone.jar
-rw-r--r-- 1 ll staff  298095 Jul 19 21:55 testgraal-0.1.jar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And we can test it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ java -jar target/testgraal-0.1-standalone.jar --help
NAME:
 toycalc - A command-line toy calculator
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;creating-a-standalone-executable-on-linux&quot;&gt;Creating a standalone executable on Linux&lt;/h3&gt;

&lt;p&gt;Now for the fun part: create a CentOS 7 Linux VM with at least 4-6G of RAM; log in and download the
latest version of GraalVM Community Edition from &lt;a href=&quot;https://github.com/oracle/graal/releases&quot;&gt;GitHub&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc4/graalvm-ce-1.0.0-rc4-linux-amd64.tar.gz
tar zxvf graalvm-ce-1.0.0-rc4-linux-amd64.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There is no actual installation to do - for the moment we will just run files within the uncompresed archive.&lt;/p&gt;

&lt;p&gt;We will also need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zlib&lt;/code&gt; for compiling:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yum install zlib-devel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Believe it or not, that’s all we have to do.&lt;/p&gt;

&lt;p&gt;Now we copy our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testgraal-0.1-standalone.jar&lt;/code&gt; uberjar to our work directory, and run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;native-image&lt;/code&gt; executable of GraalVM:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ./graalvm-ce-1.0.0-rc4/bin/native-image \
                             -H:+ReportUnsupportedElementsAtRuntime \
                             -J-Xmx3G -J-Xms3G --no-server \
                             -jar testgraal-0.1-standalone.jar
   classlist:   1,397.10 ms
       (cap):     673.76 ms
       setup:   1,087.60 ms
   (objects):  14,990.15 ms
  (features):   4,894.45 ms
    analysis:  78,338.83 ms
    universe:   7,191.33 ms
     (parse):   5,807.91 ms
    (inline):   4,237.25 ms
   (compile):  25,749.35 ms
     compile:  37,432.01 ms
       image:   3,398.08 ms
       write:     662.61 ms
     [total]: 129,597.44 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please note that I specify:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-H:+ReportUnsupportedElementsAtRuntime&lt;/code&gt; so Graal won’t abort for dynamic class loading;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-J-Xmx3G -J-Xms3G&lt;/code&gt; so we can specify the Java heap (not really needed here, but you may need this option to compile larger projects);&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-server&lt;/code&gt; so it runs as one single process without spawning a compiler daemon.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And after a couple of minutes and a lot of CPU under the bridge….&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ls -l
total 265392
drwxr-xr-x  8 root root      4096 Jul 19 21:38 graalvm-ce-1.0.0-rc4
-rw-r--r--  1 root root 236474318 Jul 17 18:02 graalvm-ce-1.0.0-rc4-linux-amd64.tar.gz
-rwxr-xr-x  1 root root  25873378 Jul 19 22:12 testgraal-0.1-standalone
-rw-r--r--  1 root root   4698514 Jul 19 21:55 testgraal-0.1-standalone.jar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There it is! the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testgraal-0.1-standalone&lt;/code&gt; executable, ~25M that we can compress to ~7M with gzip.&lt;/p&gt;

&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;

&lt;p&gt;If a normal execution of out &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testgraal&lt;/code&gt; takes a couple of seconds on our VM, using
Graal’s own Java VM…..&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# time ./graalvm-ce-1.0.0-rc4/bin/java -jar testgraal-0.1-standalone.jar -?
NAME:
 toycalc - A command-line toy calculator

USAGE:
 toycalc [global-options] command [command options] [arguments...]

VERSION:
 0.0.1

COMMANDS:
   add, a               Adds two numbers together
   sub, s               Subtracts parameter B from A

GLOBAL OPTIONS:
       --base N  10  The number base for output
   -?, --help


real    0m1.563s
user    0m2.755s
sys     0m0.156s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we run the compiled image we get the very same result….&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# time ./testgraal-0.1-standalone -?
NAME:
 toycalc - A command-line toy calculator

USAGE:
 toycalc [global-options] command [command options] [arguments...]

VERSION:
 0.0.1

COMMANDS:
   add, a               Adds two numbers together
   sub, s               Subtracts parameter B from A

GLOBAL OPTIONS:
       --base N  10  The number base for output
   -?, --help


real    0m0.005s
user    0m0.003s
sys     0m0.002s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Same result, but &lt;strong&gt;300x faster&lt;/strong&gt; in real time, and &lt;strong&gt;900x faster&lt;/strong&gt; in term of user time. Not half bad, I’d say!&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;you have no excuses&lt;/strong&gt; not to build your CLI tools in Clojure now.&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

&lt;h1 id=&quot;see-also&quot;&gt;See also&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.graalvm.org/&quot;&gt;GraalVM home page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/l3nz/cli-matic&quot;&gt;CLI-matic, a compact, hands-free subcommand line parsing library for Clojure&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md&quot;&gt;Limitations of Substrate VM&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Fri, 20 Jul 2018 00:00:00 +0200</pubDate>
        <link>http://astrecipes.net/blog/2018/07/20/cmd-line-apps-with-clojure-and-graalvm/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2018/07/20/cmd-line-apps-with-clojure-and-graalvm/</guid>
        
        <category>clojure</category>
        
        <category>cli-matic</category>
        
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>Learning Clojure: Transducers how-to</title>
        <description>&lt;p&gt;Clojure 1.8 introduced transducers to the world. They are quite an interesting idea that has 
many possible uses; but as they are very flexible and have a scary name, it is sometimes
hard to get started with them.&lt;/p&gt;

&lt;h1 id=&quot;so---what-are-transducers&quot;&gt;So - what are transducers?&lt;/h1&gt;

&lt;p&gt;Transducers are funny “objects” that receive values, one by one, and for each value “in” can return
zero or more values “out”. They decouple the logic of computing new values from the logic that iterates through
a set of them and feeds the transducers - so they don’t really care if it’s a lazy sequence, a vector,
a channel or whatever you want.&lt;/p&gt;

&lt;h1 id=&quot;and-why-should-i-care&quot;&gt;And why should I care?&lt;/h1&gt;

&lt;p&gt;This means transducers can…..&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;work like a “map”&lt;/li&gt;
  &lt;li&gt;work like a “filter”&lt;/li&gt;
  &lt;li&gt;work like a fancy “reducer”&lt;/li&gt;
  &lt;li&gt;filter-and-aggregate elements&lt;/li&gt;
  &lt;li&gt;be composed together without a need for intermediate sequences&lt;/li&gt;
  &lt;li&gt;work on anything, be it a sequence or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;core.async&lt;/code&gt; channel&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;writing-your-first-transducer&quot;&gt;Writing your first transducer&lt;/h1&gt;

&lt;p&gt;Transducers have a fixed form: and this is what you need to copy and paste to get started.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template-transducer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
    	&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; SET-UP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 	         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      	&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; PROCESS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      	&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; TEAR-DOWN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is the basic form:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A function of an external function “xf” - you don’t know and do not care about it. This is what 
makes them composable. This function allows composition - you call it to “move forward” with  processing.&lt;/li&gt;
  &lt;li&gt;A zero-arity call: starting a processing chain&lt;/li&gt;
  &lt;li&gt;A two-arity call: receives a current result and a value to add to it. Returns a call to “xf”
passing the current result and (usually) some transformation on the input. If you don’t want to 
return any new value, return “result” as it is.&lt;/li&gt;
  &lt;li&gt;A one-arity call: transducer is terminating. Return an application of “xf” to the final result,
or whatever the “result” should be.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see this is very simple, but you’re left wondering. But practice makes perfect, so 
let’s start.&lt;/p&gt;

&lt;p&gt;So, say we want to write a transducer that retains only odd numbers in a sequence, and returns them
incremented. Something like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;odd?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; &apos;(2 4 6 8 10)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So here is what we do:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plain-filter-odd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
    	&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; START&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      	&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; STOP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      	&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; PROCESS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cond&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;odd?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; apply xf to result adding 1 to input&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ELSE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; do nothing - return result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;


&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plain-filter-odd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; [2 4 6 8 10]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In this case we do not need the set-up and tear-down parts, so we are basically write some logic only 
in the two arity version. Look how we return “result” as-is if we do not want to change it, while we 
“return” values by calling xf with a (new) intermediate value.&lt;/p&gt;

&lt;h1 id=&quot;using-transducers&quot;&gt;Using transducers&lt;/h1&gt;

&lt;p&gt;There are a few simple ways to “run” transducers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;into&lt;/code&gt;: as the vanilla version does, appends to an existing collection the transducer applied to a collection&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transduce&lt;/code&gt;: applies transducer to a collection, and then using a “reducing” function to create our final result&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sequence&lt;/code&gt;: returns a new sequence by applying the transducer to a sequence. This sequence is lazy, so if it’s
not needed, it’s not computed either.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;educe&lt;/code&gt;: captures the transduction process into a function.&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plain-filter-odd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; [2 4 6 8 10]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plain-filter-odd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; [2 4 6 8 10]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plain-filter-odd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; &apos;(2 4 6 8 10)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note that as you have no idea of how many items of the source sequence 
need to be consumed to produce your final sequence, there is no 1:1 equivalence.&lt;/p&gt;

&lt;h1 id=&quot;duplicating-values&quot;&gt;Duplicating values&lt;/h1&gt;

&lt;p&gt;As we said above, you can return 0 or more values from a transducer:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;To return no new values, just return “result” as it is&lt;/li&gt;
  &lt;li&gt;To return one value, return “(xf result value)”&lt;/li&gt;
  &lt;li&gt;To return multiple values, just call “(xf result value1)”, then “(xf result value2)”, and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To show how thos works, this transducer duplicates odd values and removes even ones:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duplicates-odd-vals&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cond&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;odd?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
           	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;               
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; first odd value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; second odd value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ELSE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; do nothing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duplicates-odd-vals&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; [1 1 3 3 5 5 7 7 9 9]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;stateful-transducers&quot;&gt;Stateful transducers&lt;/h1&gt;

&lt;p&gt;Having the ability to control when a value is emitted is especially 
important for trasducers that must keep track of a current (inner) state.&lt;/p&gt;

&lt;p&gt;Our transducer will return a sequence of odd numbers intertwined with their unique 
sequence number for each item in our sequence, starting from 100 onwards 
(we count all numbers for a change).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stateful-transducer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;atom&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;swap!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cond&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;odd?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
	           	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;S:&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	           &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ELSE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; do nothing - return result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stateful-transducer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;[1 &quot;S:102&quot; 3 &quot;S:104&quot; 5 &quot;S:106&quot; 7 &quot;S:108&quot; 9 &quot;S:110&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To do this we use an atom (or possibly a transient) that we use as a 
keeper of current state. See how this atom is unique per function
invocation, so it’s local mutable state (and how this starts to look a bit like
an “object” encapsulating state).&lt;/p&gt;

&lt;p&gt;Of course, a stateful transducer depends on its internal state and is not
inherently parallelizable if run in parallel, so beware!&lt;/p&gt;

&lt;h1 id=&quot;transducers-as-reducers&quot;&gt;Transducers as reducers&lt;/h1&gt;

&lt;p&gt;You can easily turn a stateful transducer into a reducer, just like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reducer-transducer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;atom&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
    	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;swap!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reducer-transducer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; 42&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;See how we basically ignore result and use a result of our own making at the 
end of the eduction.&lt;/p&gt;

&lt;h1 id=&quot;parametrized-transducers&quot;&gt;Parametrized transducers&lt;/h1&gt;

&lt;p&gt;You can easily create your own parametrized transducers with “partial”,
so that you capture all parameters but “xf”:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string-transducer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
		&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;:&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;partial&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string-transducer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; [&quot;x:0&quot; &quot;x:1&quot; &quot;x:2&quot; &quot;x:3&quot; &quot;x:4&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;composing-transducers&quot;&gt;Composing transducers&lt;/h1&gt;

&lt;p&gt;One of the best things abut transducers is that they are composable;
you can create a new transducer out of a set of existing ones.&lt;/p&gt;

&lt;p&gt;This is more efficient than mapping/filtering over multiple sequences
in turn.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;partial&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string-transducer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
                 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;partial&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string-transducer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; [&quot;A:B:0&quot; &quot;A:B:1&quot; &quot;A:B:2&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;See how B was applied first, and then A.&lt;/p&gt;

&lt;h1 id=&quot;putting-it-all-together---separating-text-paragraphs&quot;&gt;Putting it all together - separating text paragraphs&lt;/h1&gt;

&lt;p&gt;We want to create a transducer that receives a sequence of
lines (maybe over a core.async channel) and separates them
into paragraphs.&lt;/p&gt;

&lt;p&gt;This is a typical stateful transducer, and something that 
we cannot do easily with just map and filter.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line-wrapper&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;atom&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
       	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;final-result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; (A)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;final-result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                      &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; (C)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; if empty line, push accumlated lines at once&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cond&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
			  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                       &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; (B)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        
          &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ELSE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
			   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
            	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;swap!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       
       
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line-wrapper&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
           &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;[[&quot;A&quot; &quot;B&quot;] [&quot;C&quot;] [] [&quot;D&quot;] []]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In this case it is important that if we have open stanzas when the sequence terminates,
we reprocess them (see A, and see it’s the very same thing we’re doing in B) and then we generate the 
final result, that will in turn be sent down the pipeline (C).&lt;/p&gt;

&lt;p&gt;This is pretty cool, as a lot of problems can be expressed in a way that  is similar 
to this; and IMHO this alone makes transducers worth learning.&lt;/p&gt;

&lt;h1 id=&quot;but-the--very-best-thing-about-transducers-is&quot;&gt;But the  very best thing about transducers is…..&lt;/h1&gt;

&lt;p&gt;The really best thing about transducers is that (often) you don’t need to write them.
If you had to write transducers from scratch every time like in the examples above,
they would be still  be cool,  but in 99%  of the cases you would prefer the old
map/filter/reduce - it might not be as efficient or powerful, but is concise and compact
and easy to read.&lt;/p&gt;

&lt;p&gt;Truth is that you can have the best of both worlds - you can use your classic
friends map&amp;amp;co. and use transducers. This is because many functions in core that used to 
have a collection as their last parameter now have an arity that produces a transducer
by just omoiiting that collection. So if you say:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;you get a transducer for free. In Clojure 1.8, it’s actually the other way around - when you run
your plainest Miss Apple Pie function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some-seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;what Clojure internally does is:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some-seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So you can use transducers everywhere and create highly-efficient and easy to read pipelines
by composing some custom transducers and plenty of common Clojure functions, like in:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;c1&quot;&gt;; Remove empty stanzas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
            	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line-wrapper&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;complement&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; [[&quot;A&quot; &quot;B&quot;] [&quot;C&quot;] [&quot;D&quot;]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In the example above, for example, we could add a check within our newly created transducer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;line-wrapper&lt;/code&gt; 
that makes sure that if we have multiple empty lines,
then only the first one is counted. Sure, it would be easy. But filtering in the pipeline is easier.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;c1&quot;&gt;; Remember: order counts! This is likely *not* what you want.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
				&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;complement&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line-wrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;conj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; [[&quot;A&quot; &quot;B&quot; &quot;C&quot; &quot;D&quot;]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h1&gt;

&lt;p&gt;Transducers take a while getting used to; so make sure you open your REPL and experiment a bit.&lt;/p&gt;

&lt;p&gt;The following functions have a transducer arity:  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mapcat&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remove&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take-while&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take-nth&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drop&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drop-while&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replace&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;partition-by&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;partition-all&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keep&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keep-indexed&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map-indexed&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;distinct&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interpose&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dedupe&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random-sample&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By looking at their source, they can be used as a reference as hot to write your own transducers if you get lost.&lt;/p&gt;

&lt;h1 id=&quot;see-also&quot;&gt;See also&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;&quot;&gt;http://clojure.org/reference/transducers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Thu, 24 Nov 2016 00:00:00 +0100</pubDate>
        <link>http://astrecipes.net/blog/2016/11/24/transducers-how-to/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2016/11/24/transducers-how-to/</guid>
        
        <category>clojure</category>
        
        <category>transducer</category>
        
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>Play endless random sounds for testing</title>
        <description>&lt;p&gt;This Extension will play random sounds from asterisk standard sound set forever.
You might have to adjust the path uses. This example works on Debian:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;exten =&amp;gt; endless,1,Answer()
exten =&amp;gt; endless,2,Set(RANDSOUND=${SHELL(ls /var/lib/asterisk/sounds/en | shuf -n 1 | head -n1 | cut -f1 -d&quot;.&quot; | tr -d &quot;\n&quot;)})
exten =&amp;gt; endless,3,NoOp(*${RANDSOUND}*)
exten =&amp;gt; endless,4,Playback(${RANDSOUND})
exten =&amp;gt; endless,5,Goto(2)
exten =&amp;gt; endless,n,Hangup()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 20 Dec 2014 21:05:32 +0100</pubDate>
        <link>http://astrecipes.net/blog/2014/12/20/play-endless-random-sounds-for-testing/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2014/12/20/play-endless-random-sounds-for-testing/</guid>
        
        <category>testing</category>
        
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>Compiling Asterisk13 on Centos65 64-bit</title>
        <description>&lt;p&gt;This is a quick tutorial to get started with Asterisk 13 (currently beta) on Centos 6.5 64-bit.&lt;/p&gt;

&lt;p&gt;Luckily the installation procedure is very similar to Asterisk 12 and it is very easy to go through.&lt;/p&gt;

&lt;p&gt;First we disable selinux and update the system and install binary dependencies - it may take a while.&lt;/p&gt;

&lt;p&gt;Disable selinux:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sed -i &apos;s/SELINUX=enforcing/SELINUX=disabled/&apos; /etc/selinux/config
reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that if you copy the commands below that you must either 1) make single commands all on one line, or 2) add back slashes to carry over to a new line (this wiki does not display the back slashes).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yum update
yum install -y screen lsof mlocate wget
yum install -y gcc-c++ make gnutls-devel kernel-devel libxml2-devel ncurses-devel \
  subversion doxygen texinfo curl-devel net-snmp-devel neon-devel \
  uuid-devel libuuid-devel sqlite-devel sqlite \
  git speex-devel gsm-devel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Installing Jansson&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://www.digip.org/jansson/releases/jansson-2.5.tar.gz
tar zxvf jansson-2.5.tar.gz
cd jansson-2.5
./configure --libdir=/usr/lib64
make &amp;amp; make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Installing SRTP&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://srtp.sourceforge.net/srtp-1.4.2.tgz
tar zxvf srtp-1.4.2.tgz
cd srtp
autoconf
./configure --enable-pic --libdir=/usr/lib64
make &amp;amp;&amp;amp; make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Installing PJSIP&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/asterisk/pjproject pjproject
cd pjproject
./configure --libdir=/usr/lib64 --prefix=/usr --enable-shared \
             --disable-sound --disable-resample --disable-video --disable-opencore-amr \
             --with-external-speex --with-external-srtp --with-external-gsm 
make dep &amp;amp;&amp;amp; make &amp;amp;&amp;amp; make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Downloading and installing Asterisk 13&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://downloads.asterisk.org/pub/telephony/asterisk/releases/asterisk-13.0.0-beta1.tar.gz
tar zxvf asterisk-13.0.0-beta1.tar.gz
cd asterisk-13.0.0-beta1
./configure --libdir=/usr/lib64
make menuselect
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that as of 2014 November this works with Asterisk 13.0.0 too now that it is out of beta. Just substitute the current URL for the wget and other commands above, for example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-13-current.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Under Channels you chack that there is  “&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;chan_pjsip”&lt;/p&gt;

    &lt;p&gt;make &amp;amp;&amp;amp;  make install &amp;amp;&amp;amp;  make samples
  cd ..&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running Asterisk&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;asterisk 
asterisk -vvvr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And you get&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;localhost*CLI&amp;gt; core show version
Asterisk 13.0.0-beta1 built by root @ localhost.localdomain on a x86_64 running Linux on 2014-09-12 08:28:08 UTC
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

&lt;p&gt;..and of course, if you implement call-centers in Asterisk, do not forget to check out &lt;a href=&quot;http://queuemetrics.com&quot;&gt;QueueMetrics&lt;/a&gt; for reporting and &lt;a href=&quot;http://wombatdialer.com&quot;&gt;WombatDialer&lt;/a&gt; for implementing outbound added value services. :)&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Sep 2014 13:08:43 +0200</pubDate>
        <link>http://astrecipes.net/blog/2014/09/12/compiling-asterisk13-on-centos65-64-bit/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2014/09/12/compiling-asterisk13-on-centos65-64-bit/</guid>
        
        <category>PJSIP</category>
        
        <category>CentOS</category>
        
        <category>Asterisk</category>
        
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>Getting started with ARI</title>
        <description>&lt;p&gt;Setting up the Asterisk REST Interface on an Asterisk 12 system for an introductory test-drive is quite straightforward. The idea behind ARI is that you have a RESTful part where you send commands and a websocket to receive events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asterisk configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Edit &lt;em&gt;/etc/asterisk/http.conf&lt;/em&gt; so that:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[general]
enabled=yes
bindaddr=0.0.0.0
bindport=8088
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then create an ARI user in &lt;em&gt;/etc/asterisk/ari.conf&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[general]
enabled = yes

[aritest]
type = user
read_only = no
password = testme
password_format = plain
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This creates a new user called &lt;em&gt;aritest&lt;/em&gt; with password &lt;em&gt;testme&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now have Asterisk reload and test that the configuration was picked up.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;localhost*CLI&amp;gt; reload
...
localhost*CLI&amp;gt; ari show users
r/o?  Username
----  --------
No    aritest
localhost*CLI&amp;gt; ari show status
ARI Status:
Enabled: Yes
Output format: compact
Auth realm: Asterisk REST Interface
Allowed Origins:
User count: 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Testing ARI - the websocket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can install the &lt;em&gt;wscat&lt;/em&gt; tool to test the ARI. It is a part of the EPEL repository that, though not technically a part of CentOS 6, it surely complements it (see &lt;a href=&quot;https://fedoraproject.org/wiki/EPEL&quot;&gt;https://fedoraproject.org/wiki/EPEL&lt;/a&gt; if you do not have it available).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yum install nodejs nodejs-options nodejs-commander
yum install nodejs-ws
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then type:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# wscat --connect &apos;ws://localhost:8088/ari/events?app=hello&amp;amp;api_key=aritest:testme&apos;
connected (press CTRL+C to quit)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If so far everything is okay, we can now run a test that is a little more meaningful.&lt;/p&gt;

&lt;p&gt;First edit your &lt;em&gt;/etc/asterisk/extensions.conf&lt;/em&gt; and add the following stanza:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[testari]
exten =&amp;gt; 1,1,Noop()
same =&amp;gt; n,Stasis(hello,world)
same =&amp;gt; n,Hangup()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This just creates a new context that sends calls to the Stasis (ARI) app called “hello” with a parameter of “world”.&lt;/p&gt;

&lt;p&gt;We reload Asterisk so that the dialplan is picked up and in  a new window we run again the &lt;em&gt;wscat&lt;/em&gt; command above.&lt;/p&gt;

&lt;p&gt;While &lt;em&gt;wscat&lt;/em&gt; is running, go to the Asterisk CLI and type:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;localhost*CLI&amp;gt; channel originate Local/1@testari application wait 100
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On the websocket window you’ll see:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt; {
  &quot;type&quot;: &quot;StasisStart&quot;,
  &quot;timestamp&quot;: &quot;2013-12-30T15:21:45.688+0100&quot;,
  &quot;args&quot;: [
    &quot;world&quot;
  ],
  &quot;channel&quot;: {
    &quot;id&quot;: &quot;1388413305.5&quot;,
    &quot;name&quot;: &quot;Local/1@testari-00000002;2&quot;,
    &quot;state&quot;: &quot;Ring&quot;,
    &quot;caller&quot;: {
      &quot;name&quot;: &quot;&quot;,
      &quot;number&quot;: &quot;&quot;
    },
    &quot;connected&quot;: {
      &quot;name&quot;: &quot;&quot;,
      &quot;number&quot;: &quot;&quot;
    },
    &quot;accountcode&quot;: &quot;&quot;,
    &quot;dialplan&quot;: {
      &quot;context&quot;: &quot;testari&quot;,
      &quot;exten&quot;: &quot;1&quot;,
      &quot;priority&quot;: 2
    },
    &quot;creationtime&quot;: &quot;2013-12-30T15:21:45.688+0100&quot;
  },
  &quot;application&quot;: &quot;hello&quot;
}

&amp;lt; {
  &quot;type&quot;: &quot;StasisEnd&quot;,
  &quot;timestamp&quot;: &quot;2013-12-30T15:22:15.716+0100&quot;,
  &quot;channel&quot;: {
    &quot;id&quot;: &quot;1388413305.5&quot;,
    &quot;name&quot;: &quot;Local/1@testari-00000002;2&quot;,
    &quot;state&quot;: &quot;Ring&quot;,
    &quot;caller&quot;: {
      &quot;name&quot;: &quot;&quot;,
      &quot;number&quot;: &quot;&quot;
    },
    &quot;connected&quot;: {
      &quot;name&quot;: &quot;&quot;,
      &quot;number&quot;: &quot;&quot;
    },
    &quot;accountcode&quot;: &quot;&quot;,
    &quot;dialplan&quot;: {
      &quot;context&quot;: &quot;testari&quot;,
      &quot;exten&quot;: &quot;1&quot;,
      &quot;priority&quot;: 2
    },
    &quot;creationtime&quot;: &quot;2013-12-30T15:21:45.688+0100&quot;
  },
  &quot;application&quot;: &quot;hello&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Ok I turned on pretty printing in ari.conf in order to make this readable).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing ARI - the RESTful part&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Running REST commands through the interface is very straightforward:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[root@localhost ~]# curl -v -u aritest:testme -X GET &quot;http://localhost:8088/ari/asterisk/info&quot;
* About to connect() to localhost port 8088 (#0)
*   Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8088 (#0)
* Server auth using Basic with user &apos;aritest&apos;
&amp;gt; GET /ari/asterisk/info HTTP/1.1
&amp;gt; Authorization: Basic YXJpdGVzdDp0ZXN0bWU=
&amp;gt; User-Agent: curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
&amp;gt; Host: localhost:8088
&amp;gt; Accept: */*
&amp;gt;
&amp;lt; HTTP/1.1 200 OK
&amp;lt; Server: Asterisk/12.0.0
&amp;lt; Date: Wed, 01 Jan 2014 13:20:15 GMT
&amp;lt; Connection: close
&amp;lt; Cache-Control: no-cache, no-store
&amp;lt; Content-Length: 535
&amp;lt; Content-type: application/json
&amp;lt;
{
  &quot;build&quot;: {
    &quot;os&quot;: &quot;Linux&quot;,
    &quot;kernel&quot;: &quot;2.6.32-431.el6.i686&quot;,
    &quot;machine&quot;: &quot;i686&quot;,
    &quot;options&quot;: &quot;LOADABLE_MODULES, OPTIONAL_API&quot;,
    &quot;date&quot;: &quot;2013-12-30 13:25:41 UTC&quot;,
    &quot;user&quot;: &quot;root&quot;
  },
  &quot;system&quot;: {
    &quot;version&quot;: &quot;12.0.0&quot;,
    &quot;entity_id&quot;: &quot;52:54:00:26:9d:07&quot;
  },
  &quot;config&quot;: {
    &quot;name&quot;: &quot;&quot;,
    &quot;default_language&quot;: &quot;en&quot;,
    &quot;setid&quot;: {
      &quot;user&quot;: &quot;&quot;,
      &quot;group&quot;: &quot;&quot;
    }
  },
  &quot;status&quot;: {
    &quot;startup_time&quot;: &quot;2013-12-30T14:30:39.344+0100&quot;,
    &quot;last_reload_time&quot;: &quot;2013-12-30T15:21:39.354+0100&quot;
  }
* Closing connection #0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll have to look up each command - so better using a library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;See also:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.asterisk.org/wiki/display/AST/Getting+Started+with+ARI&quot;&gt;Getting started with ARI&lt;/a&gt; on which this tutorial is based&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/l3nz/ari4java&quot;&gt;The ari4java library&lt;/a&gt; wraps ARI calls so that it is accessible from Java.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 30 Dec 2013 16:07:50 +0100</pubDate>
        <link>http://astrecipes.net/blog/2013/12/30/getting-started-with-ari/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2013/12/30/getting-started-with-ari/</guid>
        
        <category>Stasis</category>
        
        <category>ARI</category>
        
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>Compiling Asterisk 12 on CentOS 6.5</title>
        <description>&lt;p&gt;Compiling Asterisk 12 (with PJSIP support) on a brand-new CentOS 6 system is pretty straightforward. Most of the packages come prebuilt so it’s not very complex to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s check the current version.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[root@localhost ~]# cat /etc/redhat-release
CentOS release 6.5 (Final)
[root@localhost ~]# uname -a
Linux localhost.localdomain 2.6.32-431.el6.i686 #1 SMP Fri Nov 22 00:26:36 UTC 2013 i686 i686 i386 GNU/Linux
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First we update the system so that we have everything needed to compile plus the packages we need.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yum update
yum install gcc-c++ make gnutls-devel kernel-devel libxml2-devel ncurses-devel subversion doxygen texinfo curl-devel net-snmp-devel neon-devel
yum install uuid-devel libuuid-devel sqlite-devel sqlite git speex-devel gsm-devel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Compiling PJSIP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have everything for PJSIP but the SRTP library.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://srtp.sourceforge.net/srtp-1.4.2.tgz
tar zxvf srtp-1.4.2.tgz
cd srtp
autoconf
./configure
make
make install
cp /usr/local/lib/libsrtp.a /lib
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we are ready for PJSIP itself. Make sure you download the patched version that works with Asterisk.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/asterisk/pjproject pjproject
cd pjproject/
./configure --prefix=/usr --enable-shared --disable-sound --disable-resample --disable-video --disable-opencore-amr --with-external-speex --with-external-srtp --with-external-gsm
make dep
make
make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; if compiling on a 64-bit CentOS system, remember to add &lt;em&gt;–libdir=/usr/lib64&lt;/em&gt; to have Asterisk find it later (Thanks Jakub!)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compiling Asterisk&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We start by compiling Jansson - it is available in the CentOS repos, but it’s an old version.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://www.digip.org/jansson/releases/jansson-2.5.tar.gz
tar zxvf jansson-2.5.tar.gz
cd jansson-2.5
./configure --prefix=/
make
make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; I had to set &lt;em&gt;–prefix=/usr/&lt;/em&gt; to stop Asterisk borking when detecting the Jansson library. (Centos 6.5 (Final) 64-bit)&lt;/p&gt;

&lt;p&gt;Now it’s time for Asterisk itself:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-12-current.tar.gz
cd asterisk-12.0.0/
./configure
make menuselect
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Under &lt;em&gt;Channel Drivers&lt;/em&gt; check that &lt;em&gt;chan_pjsip&lt;/em&gt; is checked (and disable &lt;em&gt;chan_sip&lt;/em&gt; is you really feel brave!).
If building on a KVM box, better uncheck &lt;em&gt;BUILD_NATIVE&lt;/em&gt; under &lt;em&gt;Compiler Flags&lt;/em&gt; (press &lt;strong&gt;x&lt;/strong&gt;
 to save).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;make 
make install
make samples
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If all went well…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[root@localhost asterisk-12.0.0]# asterisk
[root@localhost asterisk-12.0.0]# asterisk -vvvvr
Asterisk 12.0.0, Copyright (C) 1999 - 2013 Digium, Inc. and others.
Created by Mark Spencer &amp;lt;markster@digium.com&amp;gt;
Asterisk comes with ABSOLUTELY NO WARRANTY; type &apos;core show warranty&apos; for details.
This is free software, with components licensed under the GNU General Public
License version 2 and other licenses; you are welcome to redistribute it under
certain conditions. Type &apos;core show license&apos; for details.
=========================================================================
Connected to Asterisk 12.0.0 currently running on localhost (pid = 18101)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;See also&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2013/12/30/getting-started-with-ari/&quot;&gt;Getting started with ARI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 30 Dec 2013 14:45:44 +0100</pubDate>
        <link>http://astrecipes.net/blog/2013/12/30/compiling-asterisk-12-on-centos-6.5/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2013/12/30/compiling-asterisk-12-on-centos-6.5/</guid>
        
        <category>PJSIP</category>
        
        <category>CentOS</category>
        
        <category>Asterisk</category>
        
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>WombatDialer</title>
        <description>&lt;p&gt;WombatDialer is a next-generation dialer platform for the Asteriskâ„¢ PBX.&lt;/p&gt;

&lt;p&gt;It is meant as a way to implement a number of functions that are not easily handled in an Asterisk-based call center, like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Queue call-backs: allow callers to leave their numbers instead of waiting on a queue, and then being calls them back&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Web-based contact-us forms: allow customers to be called back by using a web form&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Automated surveys / Reverse IVRs: call a list of numbers and gather information that is entered as DTMF&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Number verification services: automatically check the validity of phone numbers&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Telecasting of pre-recorded messages: send pre-recorded messages to thousands of recipents quickly and efficiently&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Automated appointment reminders: remind and take appointments, with our without human interventions&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Automated subscription expiration: automatically call expiring subscriptions, and have clients renew their subscription&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Automated quality surveys: run quality/performance interviews and feed them back into the QueueMetrics QA module&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Generic progressive dialing: keep your outbound agents busy by having a dialer compose numbers, handle retries and pre-qualify leads&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;…and many many more!&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WombatDialer is a platform, meaning that it lets you develop specific solutions that are custom-tailored for the task at hand. It hides away the complexisties of tracking calls, handling redials, getting data to and from extenal servers, scaling up to multiple Asterisk servers, and more.&lt;/p&gt;

&lt;p&gt;WombatDialer was built from the scratch to work well with QueueMetrics, so that you can use QueueMetrics to report on its activities together with your existing inbound/outbound activities; it may work with agents on queues therefore leveraging the way you currently work with QueueMetrics, and can use the QueueMetrics agent and Real-time pages as a control point for its activities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;See also:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://www.wombatdialer.com/&quot;&gt;WombatDialer Home Page&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://www.wombatdialer.com/features.jsp&quot;&gt;WombatDialer Features&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://www.wombatdialer.com/integration.jsp#tutorials&quot;&gt;WombatDialer Tutorials&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://www.wombatdialer.com/installation.jsp&quot;&gt;Download and install a free evaluation versions&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 20 Nov 2012 13:21:21 +0100</pubDate>
        <link>http://astrecipes.net/blog/2012/11/20/wombatdialer/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2012/11/20/wombatdialer/</guid>
        
        <category>wombatdialer</category>
        
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>Compiling Asterisk 1.8 on CentOS 5.5 64-bit</title>
        <description>&lt;p&gt;After the first official release of Asterisk 1.8, I decided to test how it is and compiled it on a clean-slate 64-bit CentOS system. The experience went very smoothly and the &lt;em&gt;configure&lt;/em&gt; script was able to pick up all the packages needed to activate various interesting extensions (it was not always so with previous releases).&lt;/p&gt;

&lt;p&gt;Our target is compiling Asterisk 1.8.0 with the following features/extensions:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;MySQL storage and dialplan&lt;/li&gt;
  &lt;li&gt;DAHDI drivers (I still call them Zaptel!) for analog lines&lt;/li&gt;
  &lt;li&gt;Full docs&lt;/li&gt;
  &lt;li&gt;CURL available for usage and dialplan&lt;/li&gt;
  &lt;li&gt;HTTP integration&lt;/li&gt;
  &lt;li&gt;SNMP agents&lt;/li&gt;
  &lt;li&gt;Calendars (they look yummy!)&lt;/li&gt;
  &lt;li&gt;Google Talk integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setting up the environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The following packages have to be installed in order to allow compilation of all required modules. Some of these packages may or may not be already installed on your system.
I use as a reference a CentOS 5.5 x86_64 with kernel 2.6.18-194.17.4.el5-x86_64.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[root@Centos64]# uname -a
Linux Centos64 2.6.18-194.el5 #1 SMP Fri Apr 2 14:58:14 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following packages should all be present to allow compiling Asterisk:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yum install gcc-c++ make gnutls-devel kernel-devel
yum install libxml2-devel ncurses-devel subversion doxygen texinfo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And these are optional and required for specific modules to build:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yum install curl-devel net-snmp-devel neon-devel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We will compile under &lt;em&gt;/usr/src&lt;/em&gt; .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compiling DAHDI and tools and libpri&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since the days of Zaptel, we now have to install both DAHDI and its tools. 
Luckily both are packaged together and can be compiled in a single pass.&lt;/p&gt;

&lt;p&gt;I immediately relize that the symbolic link used to find the kernel top &lt;em&gt;.configure&lt;/em&gt; is broken; in my freshly-installed CentOS system &lt;em&gt;/lib/modules/2.6.18-194.el5/build&lt;/em&gt; is a link 
to a non-existent &lt;em&gt;../../../usr/src/kernels/2.6.18-194.el5-x86_64&lt;/em&gt;. Finding the correct location for the kernel build is easy-peasy anyway; instead of correcting the link, I
prefer to override the check using an environment variable:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;export KSRC=/usr/src/kernels/2.6.18-194.17.4.el5-x86_64
wget http://downloads.asterisk.org/pub/telephony/dahdi-linux-complete/dahdi-linux-complete-2.4.0+2.4.0.tar.gz
tar zxvf dahdi-linux-complete-2.4.0+2.4.0.tar.gz
cd dahdi-linux-complete-2.4.0+2.4.0
make
make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Internet connectivity is needed to download the firmwares for the various modules to be built - wouldn’t  it be better to package them with the main files?&lt;/p&gt;

&lt;p&gt;Compiling libpri is - as always - a breeze:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tar zxvf libpri-1.4.12-beta2.tar.gz
cd libpri-1.4.12-beta2
make
make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Compiling optional dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A couple of libraries are not availble in the CentOS repository and need to be installed manually to enjoy the benefits of 1.8.&lt;/p&gt;

&lt;p&gt;The first is &lt;em&gt;iksemel&lt;/em&gt;, an XML parser library for Jabber applications that is needed for GTalk integration:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://iksemel.googlecode.com/files/iksemel-1.4.tar.gz
tar zxvf iksemel-1.4.tar.gz
cd iksemel-1.4
./configure
make
make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The other one is &lt;em&gt;libical&lt;/em&gt;, an iCal library used to activate integration with iCal and WebDAV calendars:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://sourceforge.net/projects/freeassociation/files/libical/libical-0.44/libical-0.44.tar.gz/download
tar zxvf libical-0.44.tar.gz
cd libical-0.44
./configure --enable-shared
make
make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used version 0.44 because the 0.46 version does not compile out-of-the-box - it misses the template Makefiles from the downloadable archive. By the way, how many projects on the Internet bear the very  name name &lt;em&gt;libical&lt;/em&gt;? please… On the other side, by adding this library, we get res_calendar_caldav,  res_calendar_exchange and res_calendar_icalendar - that’s not
too bad.&lt;/p&gt;

&lt;p&gt;If you also want to build the PDF manual (but you don’t need to, you will need an optional tool called rubber that compiles Latex into a PDF:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://launchpad.net/rubber/trunk/1.1/+download/rubber-1.1.tar.gz
tar zxvf rubber-1.1.tar.gz
cd rubber-1.1
./configure
make
make install
cd ..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run target &lt;em&gt;make pdf&lt;/em&gt; after building Asterisk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compiling Asterisk 1.8&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nothing could be easier than building Asterisk now.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-1.8.0.tar.gz
tar zxvf asterisk-1.8.0.tar.gz
cd asterisk-1.8.0
./configure
make menuselect
make

#this is ony for format MP3 - SVN required
contrib/scripts/get_mp3_source.sh
  
make install
make samples
make progdocs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running menuselect, choose the modules you want to build.&lt;/p&gt;

&lt;p&gt;I always build docs so that I can copy them to an intranet webserver sand use them as a local reference in case it’s needed. Asterisk 1.8 comes with an almost 300-page User Manual available under &lt;em&gt;doc/tex/asterisk.pdf&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Starting Asterisk&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start Asterisk by typing:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;asterisk 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will start Asterisk in the background.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[root@Centos64]# asterisk -rx &quot;core show version&quot;
Asterisk 1.8.0 built by root @ Centos64 on a x86_64 running Linux on 2010-10-28 16:12:05 UTC
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Will display the Asterisk version.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;asterisk -r
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Will connect you to the Asterisk shell. Good luck!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;See also&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2007/07/21/compiling-asterisk-1.4-with-tdm400-and-h323/&quot;&gt;Compiling Asterisk 1.4 with TDM400 and H323&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2005/04/25/compiling-asterisk-with-oh323/&quot;&gt;Compiling Asterisk with OH323&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2006/11/09/installing-the-asterisk-gui/&quot;&gt;Installing the Asterisk GUI&lt;/a&gt; for 1.4&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2006/11/09/removing-asterisk/&quot;&gt;Removing Asterisk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 29 Oct 2010 18:23:14 +0200</pubDate>
        <link>http://astrecipes.net/blog/2010/10/29/compiling-asterisk-1.8-on-centos-5.5-64-bit/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2010/10/29/compiling-asterisk-1.8-on-centos-5.5-64-bit/</guid>
        
        <category>x86_64</category>
        
        <category>snmp</category>
        
        <category>ical</category>
        
        <category>gtalk</category>
        
        <category>dahdi</category>
        
        <category>curl</category>
        
        <category>compiling</category>
        
        <category>centos</category>
        
        <category>calendar</category>
        
        <category>Asterisk</category>
        
        <category>64bit</category>
        
        <category>1.8</category>
        
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>Exporting queue_log data</title>
        <description>&lt;p&gt;This query lets you export the contents of the queue_log table in SQL format for inspection. Instead of exporting the whole table, it exports only the period specified.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/usr/bin/mysqldump \\
  -h localhost \\
  -u queuemetrics -p \\
  --databases queuemetrics \\
  --tables queue_log \\
  --where=&quot;time_id between \\
           unix_timestamp(&apos;2008-10-23 00:00:00&apos;) \\
       and unix_timestamp(&apos;2008-10-28 00:00:00&apos;)&quot; \\
  &amp;gt; outfile.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When you run an export, make sure that you express the time period in GMT time, as that is usually the one used by Asterisk.&lt;/p&gt;

&lt;p&gt;If you have to send the file over for inspection, you can greatly reduce its size by compressing it, e.g.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bzip2 outfile.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will create a file called ‘oufile.sql.bz2’.&lt;/p&gt;

</description>
        <pubDate>Fri, 19 Feb 2010 11:59:03 +0100</pubDate>
        <link>http://astrecipes.net/blog/2010/02/19/exporting-queue_log-data/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2010/02/19/exporting-queue_log-data/</guid>
        
        <category>queue_log</category>
        
        <category>mysqldump</category>
        
        <category>MySQL</category>
        
        <category>export</category>
        
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>New server 100210</title>
        <description>&lt;p&gt;We are now running on a new web server that is located at Rackspace.&lt;/p&gt;

&lt;p&gt;It should be better than the old one, and likely way better!&lt;/p&gt;

&lt;p&gt;We took some measures to avoid referer spam, an deleted over 150,000 fake refer entries.&lt;/p&gt;
</description>
        <pubDate>Wed, 10 Feb 2010 13:35:51 +0100</pubDate>
        <link>http://astrecipes.net/blog/2010/02/10/new-server-100210/</link>
        <guid isPermaLink="true">http://astrecipes.net/blog/2010/02/10/new-server-100210/</guid>
        
        
        <category>update</category>
        
      </item>
    
  </channel>
</rss>
