Showing posts with label Clojure. Show all posts
Showing posts with label Clojure. Show all posts

2022-05-09

Trampolines - fun way to make recursion not stack overflow

(We'll use JavaScript for today, running in the Firefox developer console.)

No doubt when you learned recursion, you learned that each recursive function call uses stack space to do its work.  There's only so much stack space.  So unless your programming language has a special feature (called tail call elimination), a recursive function can eventually exhaust all stack space, leading to the famous stack overflow error (not the web site).

For example, let's write a loop to sum up numbers from 0 up to some N like this:

let sum = 0;
for (let i = 0; i < 10000; ++i) sum += i;
console.log(sum); // prints: 49995000

Now a recursive version might look like this:

const loop = function(i, sum){
  if (i < 10000){
    sum += i;
    return loop(i + 1, sum);
  } else {
    return sum;
  }
};
console.log(loop(0, 0)); // prints: 49995000

As you can see, a loop is just a recursive function call.

The above is a nice, simple demo of converting a loop to recursion.  But if instead of summing up to 10,000, we sum up to 100,000, then the loop prints 4999950000.  But the recursive function prints:

Uncaught InternalError: too much recursion

With a link to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Too_much_recursion

Trampolines

No surprise, like the title says, trampolines can fix this!

The key is to not allow the loop function to run loop(i + 1, sum), because that'd consume stack space!  Instead, the loop function will return a function called a thunk.  That thunk function, when run, will run and return loop(i + 1, sum).  This also means a thunk can return a thunk!

The function that runs loop, and any thunk functions, is called a trampoline.  That's because the thunk function the trampoline runs may return a thunk, and if so, that thunk will get run too.  The thunks keep bouncing off of the trampoline function.  Until one day a thunk returns a value instead of a thunk!  Then the trampoline's job is done, and that value is returned.

Because the thunk itself only ever uses a single "frame" of space on the stack, rather than recursively using more and more stack space with recursive function calls, and so the stack overflow error is avoided.

Here's how the code will look:

const loop = function(i, sum){
  if (i < 100000){
    sum += i;
    let thunk = ()=>{return loop(i + 1, sum)};
    return thunk; // rather than returning loop(i + 1, sum)
  } else {
    return sum;
  }
};
 

const trampoline = function(){
  let tv = loop(0, 0);
  while (typeof tv === 'function'){
    tv = tv(); // returns either a thunk function or a value
  }
  return tv;
};
 

console.log(trampoline()); // prints: 4999950000

Trampolines running thunk-returning thunk functions is a generic technique that's applicable in other languages and situations too!


Tail Call Elimination: no need for trampolines

If your programming language has full support for an optimization called Tail Call Elimination, the above trampoline technique is completely unnecessary.

It turns out JavaScript at one point had this optimization planned.  It was to be an "invisible" opportunistic tail call optimization (TCO).  Meaning that if you correctly wrote a proper recursive function call in tail position, then TCO would kick in, and it wouldn't consume stack space (thus no stack overflow).

TCO is currently only available on Apple's Safari browser and on iOS [1].

Google's V8 team apparently came to the conclusion that TCO makes the wrong tradeoff.  Because it's an opportunistic optimization, it's very easy for a programmer to write code they think would get TCO, but actually won't, and it can be very difficult to discover the error during testing.  They advocated for an explicit syntactic way to designate a recursive function call as requiring tail call elimination.  But... well, it all fell by the wayside and was never picked back up [2].

Interestingly, the Clojure programming language's creator, Rich Hickey, basically made the same argument.  In Clojure, recursive function calls requiring TCO must be written explicitly with a recur syntax.  In that case, no trampoline is needed, and the recursive function call won't overflow the stack.

 

[1] https://kangax.github.io/compat-table/es6/

[2] https://stackoverflow.com/a/42788286

2016-11-19

Clojure Programming Cookbook - a Book Review

It's been a while since I've looked into programming Clojure again, having been preoccupied with C++ lately.  With the new Clojure Programming Cookbook, I've gotten to see a number of new developments in the Clojure ecosystem that is quite exciting.


The book is very example oriented, basically being a collection of code recipes for accomplishing common programming tasks with Clojure.  I like how it starts from very basic interactive programming and macro usage (this is a Lisp after all), all the way to talking about concurrency, parallel processing, and cloud-based tasks.  Each recipe is accompanied by brief explanations, preambles, and some cross references to related recipes in the book.

Each recipe is basically self-contained, so I wouldn't necessarily recommend reading the book like a book, but instead to use it as a "random access" set of references.  The ebook version is probably best in terms of allowing for full-text searching for key terms, and for clicking on relevant topics in the table of contents to look up useful recipes.  I'd personally prefer having the PDF ebook, but that's just me.

As a reference book, it's great if you already have familiarity with Clojure, but this book is probably okay for those of you programmers who don't yet know Clojure and want to learn it.  I'm not sure it's that great if you just don't know how to program and want to learn programming starting from nothing.  On the other hand, I could imagine a course instructor might be able to bootstrap the book with additional content to help absolute beginners get started, then point out recipes in the book to try out from time to time.

What's neat about this book for me is that as I've been away from Clojure for a few years, looking at this book I see recipes for topics I'd like to try out.  Topics I've heard of but just haven't followed along with over the years.  Like Transducers, and Om.Next.  So it's nice to see some self-contained examples of these exciting new developments in the Clojure ecosystem.  But given that it is just a cookbook, it doesn't seem to get deep enough into any specific particular topic for me to feel like I really know what's going on.  For that, I'd need to look to additional resources.

So there's definitely some caveats, but it is a pretty decent and easy going kind of book.  For a lower intermediate Clojure programmer, it's definitely a good resource to look into, maybe to help get you from programming on a single machine to programming for the cloud (AWS, etc.).  For upper intermediate programmers who's been away from Clojure for a while and want to see some new Clojure developments, I'd definitely see about getting it if you chance upon a sale.  The book lives up to its name as a cookbook.

2014-08-31

Haskell Data Analysis Cookbook - a Book Review

As with my previous post, Clojure Data Analysis Cookbook - a Book Review, I was this time offered to review Haskell Data Analysis Cookbook by Nishant Shukla.  First impressions: those are two very similar and related books that have some overlapping ideas, but not only are the programming languages used totally different in "genre", the content itself also cover some different data analysis grounds and could be treated as complementary books in that way.


The book itself is very example oriented (much like the Clojure Data Analysis Cookbook), basically being a collection of code recipes for accomplishing various common tasks for data analysis.  It does give you some quick explanations of why and what else to "see also".

It gives you recipes to take in raw data in the form of CSV, JSON, XML, or whatever, including data that lives on web servers (via HTTP GET or POST requests).  Then there are recipes to build up datasets in MongoDB, or SQLite databases.  To recipes to clean up that data, do analysis (e.g. clustering with k-means), to visualizing, presenting, and exporting that analysis.

Each recipe is more or less self-contained, without much in building on top of previous recipes.  It makes the book more "random access".  It's less a book to read through cover to cover, and more of a handy reference to use by full-text searching for key terms, clicking on the relevant topic in the table of contents, or by looking up terms in the index.  It's definitely a book I'd rather have as a PDF ebook so that I can access it anywhere in the world, and so I can do full-text search in.  It does come in Mobi as well as ePub formats, and code samples are provided in a separate zipped download as well.

Having said that, you can tell whether a book was made to be seriously used as a reference or not by looking at its index.  There are 9 pages of indices, equivalent to about 2.9% of the number of pages previous to the index.  This book can certainly be used as a reference.

As a reference book, it's great for people who have already a familiarity with Haskell in general.  If you don't know Haskell, this book won't teach it to you.  That is, unfortunately, possibly a missed marketing opportunity, as those who don't know Haskell (but have knowledge of another programming language) really only needs a small bit to understand enough of how functions are written in Haskell to pick up what's going on in the book.  This means if you know another programming language, know a bit about data analysis, you could use this book to learn some Haskell so long as you pick up the basic syntax with another tutorial in hand (so it's really not a show stopper to using this book).

Similarly, I'd say you had best be familiar with how to do data analysis as a discipline in itself.  If you don't know whether to do clustering or regression, or whether to use a K-NN or K-means, this book won't teach it to you.

Much of that is, of course, echoing the Clojure Data Analysis Cookbook.  Where the Haskell Data Analysis Cookbook differs, makes the two books have a set of complementary ideas.  Whereas both books talk about concurrency and parallelism, the Clojure DAC goes into those topics (including distributed computing) in much more detail.

On the other hand, whereas both books talk about preparing and processing data (prior to performing statistics or machine learning on it), the Haskell DAC goes into much more detail on topics like processing strings with more advanced algorithms (as in computing the Jaro-Winkler distance between strings, not like doing substring/concat operations), computing hashes and using bloom filters, and working with trees and graphs (as in node-and-link graph theory graphs, not grade-school bar graphs).

So in some sense, the Haskell Data Analysis Cookbook has more theory heavy topics (graphs and trees!), whilst the Clojure Data Analysis Cookbook has more "engineering" topics (concurrency, parallelism, and distributed computing).

Neither books are comprehensive treatise on the topic, but someone who needs a practical refresher on working with graphs and trees may find Haskell Data Analysis Cookbook to be quite useful.

All in all, I'd say this is a decent book, because if you have some familiarity of Haskell, have some familiarity with some of the basic technologies like JSON, MongoDB, or SQLite, have taken a class or two of data analysis or machine learning in university (or a MOOC?), and aren't expecting a lot of hand holding from the book, then this book is a great guide to start you off to doing some data analysis with Haskell.

2014-08-15

Java has deep expression problem for beginning students

There are many problems with Java as the first programming language to teach students if we wish to provide the most effective learning experience.  I've written on this in Learn Python instead of Java as your first language in the past even.  So what now?

Newbie, meet the Expression Problem

Stuart Sierra provides a very lucid explanation of the Expression Problem, a classic problem in software programming, in Solving the Expression Problem with Clojure 1.2.  Needless to say, Clojure provides a very clean solution.

Java, however, is a quagmire and requires some heavy OOP software engineering concepts to solve the Expression Problem.  One wouldn't ordinarily think this has anything to do with beginning students just learning to program though, but it does, and here's how.

Imagine our beginning student, "Sam", starts to learn Java and eventually starts to write a classic game of asteroids.  Sam plugs away and gets a decent game of a single player ship shooting lasers at one kind of asteroids to begin working.  Not bad!  But Sam wants to do more.  Sam wants to not just have one kind of (big) asteroids, he also wants to have small asteroids to shoot at.

Alright, so Sam begins to modify the BigAsteroids class to also be able to represent a smaller sized kind of asteroids.  The teacher catches wind of this and tells Sam, "no, that's not good", and that Sam needs to use OOP principles to write a different class for SmallAsteroids.

Now most students would say "why, Mr. Teach", my way works.  But Sam is a good student and does as he's told.

So Sam goes and creates a second class for SmallAsteroids.  Except his program was built presuming that the only things to draw, to shoot lasers at, and to move around, were BigAsteroids.  None of those methods he wrote to draw, to shoot lasers at, and to move around BigAsteroids work for SmallAsteroids.  hmm...  Welcome to the Expression Problem, Sam.

2013-08-01

Select columns by filtering on column names with Clojure Incanter

Incanter is a pretty amazing library for working with data.  With a table of data, it's easy to select rows of data to work on by filtering on the data in each row.

For example:

(let [data (to-dataset [{:a 1 :b 2} {:a 3 :b 4}])]
        ($where {:a {:$gt 2}} data))

That will select all rows where given a row, its data under column :a is greater than 2.

But what if you want to filter the dataset to get rid of certain columns?  Say you only want column 0.  This can do that:

(let [data (to-dataset [{:a 1 :b 2} {:a 3 :b 4}])]
        ($ :all 0 data))

What if you want columns 0 and 2?  This can do that:

(let [data (to-dataset [{:a 1 :b 2 :c 3} {:a 3 :b 4 :c 5}])]
        ($ :all [0 2] data))

What if you want to select by column name, e.g. only selecting columns with name :a and :b?  This can do that:

(let [data (to-dataset [{:a 1 :b 2 :c 3} {:a 3 :b 4 :c 5}])]
        ($ :all [:a :b] data))

Here's the tricky one.  What if you want to select by column name, but you want to match the column name against a regular expression (say only names containing a vowel)?  The $where method only lets you select rows of data based on a query — it's a row-wise filtering operation.  We want column-wise filtering on column-names.

My first attempt involved taking the dataset, turning it into a clojure map, then filtering on the map's keys, and finally turning it back into an Incanter dataset.  But doing so ran into a problem in Incanter: to-dataset and to-map are not inverse functions of each other.

That is to say, there exists a file of data *filepath* that can be read into Incanter with read-dataset such that this does not work: (to-dataset (to-map (read-dataset *filepath*))).  For example, if the file is a CSV spreadsheet file with missing data in some cells (not nil, not 0, just no data).

Turns out the solution is much easier:

(let [data (to-dataset [{:a 1 :b 2 :c 3} {:a 3 :b 4 :c 5}])
        columns (filter #(re-find #"aeiou" (str %)) (:column-names data))]
        ($ columns data))

The idea is to create a list of column names of the columns you want, then use that list of column names with $ to select them out of the dataset.

Column-wise selection isn't as convenient as selecting row-wise, but this is one way that works.


2013-07-08

Clojure Data Analysis Cookbook - a Book Review

Like yogthos, I was recently asked to review Clojure Data Analysis Cookbook.  With Incanter, data analysis has been one of the "selling points" of Clojure as a practical language.  A practical lisp for practical data analysis.

(Edit 2016: a second edition is available!)

The book is very example oriented, basically being a collection of code recipes for accomplishing apparently common tasks for data analysis.  It gives you recipes to go from taking raw data in the form of CSV, JSON, or whatever, to making an Incanter dataset, to doing analysis on those datasets (e.g. clustering the data by using a self-organizing map), to saving, viewing, or charting the resultant data.  Each recipe is accompanied by brief explanations, and cross-references to other related recipes in the book.

Each recipe is more or less self-contained, without much in building on top of previous recipes.  It makes the book more "random access".  It's less a book to read through cover to cover, and more of a handy reference to use by full-text searching for key terms, clicking on the relevant topic in the table of contents, or by looking up terms in the index.  It's definitely a book I'd rather have as a PDF ebook so that I can access it anywhere in the world, and so I can do full-text search in.

Having said that, you can tell whether a book was made to be seriously used as a reference or not by looking at its index.  There are 10 pages of indices, equivalent to about 3.2% of the number of pages previous to the index.  This counts as a book to be seriously used as a reference.

As a reference book, it's great for people who have already a familiarity with Clojure (and better yet, Incanter) in general.  If you don't know Clojure, this book won't teach it to you.  If you don't know Incanter, you can pick it up from this book if you're a fast learner (don't expect a lot of hand holding in learning Incanter though).

Similarly, I'd say you had best be familiar with how to do data analysis as a discipline in itself.  If you don't know whether to do clustering or regression, or whether to use a SOM or K-means, this book won't teach it to you.

Also, as a reference book, it is not comprehensive.  For example, as far as neural networks go, it only includes self-organizing maps.  There are no other kinds mentioned.  If you want another kind of neural network, you best know where to look for another Java or Clojure library.

Even with all those caveats, I'd still say this is a pretty decent book.  Why?  Because if you have some familiarity of Clojure, played around with Incanter for a bit to learn that library, have taken a class or two of data analysis in university, and aren't expecting a lot of hand holding from the book, then this book is a great guide to start you off on the road to doing data analysis with Clojure, Incanter, Weka, OpenCL, Cascalog, etc.


2011-02-26

Serializing Records and Incanter Matrix in Clojure with print-dup

For most Clojure data structures, you can get a serialization by doing something like this:


(def x (some-data-structure 1 2 3))
(def x2 (binding [*print-dup* true]
          (prn-str x)))
;; now x2 is a string serialization of x
;; You can save x2 to disk or whatever
;; Then to read it back in:
(def x-clone (read-string x2))

(Note: you really shouldn't be programming in Clojure with a bunch of def'ed vars like that...)

Unfortunately, Clojure records does not support this functionality quite just yet. There is defrecord2 that implements this serialization functionality for records though, described in this discussion in the Clojure group.

I had a second problem though, since I had records storing Incanter matrices, which are Parallel Colt matrices, and they don't print-dup in a way that can be read back in with read (or read-string). So to solve both problems at the same time, we can just implement our own print-dup for the record we create with defrecord (print-dup is a multmethod).

2011-02-16

How to change JPEG compression in Clojure

Not just how to change JPEG compression, but to do so without losing the JPEG's metadata. That's the problem I had to figure out for this program I'm writing in Clojure. I found out and wrote about how to do this in Java yesterday. Finally, here's the same thing in Clojure:

Short explanation of what the code is doing is interwoven with the code here. The code without the comments (for easy copy/paste'ing) is at bottom.

First, we import a few Java classes:

(require 'clojure.java.io)
(import [javax.imageio IIOImage ImageIO]
        [javax.imageio.plugins.jpeg JPEGImageWriteParam])
(try

Then we get the default JPEG image reader and writer:

(let [image-reader (.next (ImageIO/getImageReadersByFormatName "jpg"))
      image-writer (.next (ImageIO/getImageWritersByFormatName "jpg"))]

Now we open up streams to the input and output JPEG Files. Note that with-open will close the streams for you, so you don't have to later.

(with-open [image-input-stream (ImageIO/createImageInputStream 
                                 (clojure.java.io/file "path/to/inputFile.jpg"))
            image-output-stream (ImageIO/createImageOutputStream 
                                 (clojure.java.io/file "path/to/outputFile.jpg"))]

Next, mate the reader/writer to the respective streams:

(.setInput image-reader image-input-stream)
(.setOutput image-writer image-output-stream)

Then we'll get the JPEG input file into a container that will also contain the metadata:

(let [iio-image (IIOImage. (.read image-reader 0) nil
                             (.getImageMetadata image-reader 0))

Now set up the JPEG quality (ie, compression) level desired. Here it's set to 0.7 (where 1 is highest quality, and 0 is highest compression):

jpeg-params (doto (.getDefaultWriteParam image-writer)
               (.setCompressionMode JPEGImageWriteParam/MODE_EXPLICIT)
               (.setCompressionQuality 0.7))]

Finally we get to write out the JPEG file with the new compression level:

(.write image-writer nil iio-image jpeg-params)))

Lastly, make sure to clean up the reader/writer's so they don't continue to hog system resources (yes, you have to do this even though Java has garbage collection):

(finally
 (do (.dispose image-writer)
     (.dispose image-reader)))))

To recap, here it is again without the comments, just the bare code:

2010-12-18

Getting Leiningen 1.4.1 working on Windows (and a weird bug)

First the bug, don't make a project named "test" in Leiningen or else it will throw a NullPointerException.

Second, when getting Leiningen 1.4.1 installed on Windows 7 64bit, I had to jump through a few hoops.  Firstly, don't place the lein folder in "Program Files (x64)".  Put it in "Program Files" instead.  The parentheses will mess up the lein.bat script otherwise.

Secondly, after running "lein self-install", you'll have to fetch the downloaded "leiningen-1.4.1-standalone.jar" file from "C:\Users\my_user_name_here\AppData\Local\VirtualStore\Program Files\lein" and move it into "C:\Program Files\lein".  (Refer to this).

I'm assuming you've copied the lein folder into "C:\Program Files" of course.

2010-07-20

Various forms of named parameters in Clojure, kind of...

The short of it, from Measuring Measures' Named Parameters in Clojure post is this:

user> (defn a [{b :b c :c}] (- b c))
#'user/a
user> (a {:c 5 :b 11})
6

But that's very repetitive for too many named parameters, so there's this other form too:

user> (defn a [{:keys [b c]}] (- b c))
#'user/a
user> (a {:c 5 :b 11})
6

That's very useful when there's just a lot of arguments to pass around. What if you want to keep all those arguments in a map to continue passing around? There's this option:

user> (defn a [{:keys [b c] :as args}] (- (:b args) c))
#'user/a
user> (a {:c 5 :b 11})
6

These are technically not named parameters at all, but a way to destructure maps passed as an argument to a function.

Caution is in order, however, as you should only destructure in the arg[ument] list if you want to communicate the substructure as part of the caller contract. Otherwise, destructure in a first-line let [Clojure Library Coding Standards], perhaps like so:

user> (defn a [argmap] (let [{:keys [b c]} argmap] (- b c)))
#'user/a
user> (defn a2 [argmap] (let [{b :b c :c} argmap] (- b c)))
#'user/a2
user> (a {:c 5 :b 11})
6
user> (a2 {:c 5 :b 11})
6

2010-06-08

Building Clojure projects with Leiningen

When I first started learning Clojure, I had no idea the ecosystem it was situated in. C and C++ is situated in an ecosystem where there's make for managing the building of an application, and GCC for compiling the code. You can use a plain text editor to actually write the program if you like, there's a debugger GDB, and version control can be with CVS, Git, or whatever else. emacs is a plain text editor that also has the option of pulling together all these separate parts together to work as one integrated development environment.

Of course, nowadays people seem to like the whole IDE concept so much it's probably the most popular way to program (Apple Xcode, Eclipse, NetBeans, etc). For Clojure, you can use some of those IDEs too, eg, using NetBeans with the Enclojure plug-in.

If you like the "bag of separate tools" way (the "unix way") of programming, as I do and as I described for C at the beginning, then you'll like Leiningen - I think of it as a much better make, but for Clojure. It's documentation is sparse right now, so the following may help you.

2010-06-07

Namespaces in Clojure: How to use the multitude of ns options

Namespaces in Clojure allow you to use various options, like :require, :use, etc. The syntax of those isn't as well documented as I'd like though in the API documentation.

Stack Overflow again comes to the rescue with some good answers, distinguishing between using :use versus :require.

The syntax is easily understood by an example, like so:


(ns example-namespace.core
  (:gen-class)
  (:require [incanter
            [core :as i.c :only [col-names sel $ dim]]
             [io   :as i.io :only read-dataset]]
            [mmemail.core :as mail]
            [clojure.contrib
             [seq          :as c.c.seq :only positions]
             [except       :as c.c.except :only throwf]
             [json         :as c.c.json :only read-json]
             [command-line :as c.c.cmd :only with-command-line]]))

:use and :require has the same syntax, except the :as short-namespace-name is useless for :use since :use allows you access to the functions in that namespace without qualifying which namespace it's from.

The :only function-name and :only [fun1 fun2] parts says only the specified functions are being used in this namespace from the specified namespace.

The [package.name class1 class2] or alternatively [package.name [class1 :only fun1] [class2 :only fun2]] tells us which "classes" or specific namespaces are being used from the specified package of namespaces.

You need :gen-class if you want to one day compile and package your program for distribution as a Java jar.

There's a subtlety with having namespaces or packages with a dash in it as in example-namespace. See my previous post on namespaces for details.

2010-06-06

Building a Clojure app that handles command-line arguments

Clojure-contrib has a great package (clojure.contrib.command-line) that'll process command-line arguments for you so you don't have keep writing custom code to parse it yourself.

Only problem is the documentation is sparse, to say the least.

Look to Stack Overflow instead, where there's a great answer that's essentially the API's documentation.

2010-06-05

Leiningen 1.1.0 bugs

There's a pesky bug in Leiningen 1.1.0 right now. Actually two.

One attacks modules with dashes in their name (you should get a java.lang.NoClassDefFoundError error when you try to java -jar your-app.jar).

The second attacks when your Clojure program depends on a Java signed jar (you should get a Exception in thread "main" java.lang.SecurityException: no manifiestsection for signature file entry some/package/some.class).

Here's the workaround.

Let's say you have a project pesky-bug, and you build it into a standalone application using lein uberjar, which outputs a pesky-bug-standalone.jar.

Jar's are just zipped containers, so open it up as a zipped file. I use the Archive Manager in Ubuntu, which is great for this purpose.

Look for directory META-INF. Inside, there should be a file called MANIFEST.MF.  Both bugs will require you to fix this MANIFEST.MF file.

2010-05-15

Few reminders to the very 1st steps to application development in Clojure

Just a few reminders for myself for now...

Get lein.
Get emacs with elpa; use it to get swank-clojure into emacs.

Do lein new project_name
Modify project.clj to use Incanter and Swank, so it should include something like this:

:dependencies [[org.clojure/clojure "1.2.0-master-SNAPSHOT"]
              [org.clojure/clojure-contrib "1.2.0-SNAPSHOT"]
              [incanter "1.2.3-SNAPSHOT"]]
:dev-dependencies [[swank-clojure "1.2.1"]
                   [jline "0.9.94"]]

(edit: turns out incanter will use/import the right clojure libraries for you, but you still should specify which version of clojure and contrib you want to use.  Also note leiningen/lein-swank "1.1.0" is being replaced by swank-clojure)

Do lein deps
Do lein swank
From emacs, do M-x slime-connect

Write your code in files in src/project_name
From the repl in emacs, do (load-file "path/to/code_file.clj") to load the code so you can run it manually to inspect it.  Do (remove-ns 'main-ns-in-code-file) to start over and re-load the file for further inspection (say after you'd done some edits).

(edit: unfortunately, it looks like Java doesn't allow dynamically changing the classpath, so if you write a new file new_file.clj then you'll have to restart the repl or the swank server)

Oh, make sure file names do not include the "-" character even if the namespace you use has a "-" character in it; replace it with a "_" instead in the file name (the namespace can continue to use the "-" though).

You'll need a -main function and an addition to the project.clj file, but that's all I want to note down for now.

(edit: also check out this intro for more readable info from the Incanter web site. My stuff above was more like a brain-dump of mental notes.)

Namespaces in Clojure

The only succinct documentation of namespaces I found on Clojure is here.  To quote Brian there:
:refer shouldn't be used by you at all.

:import brings in java classes

:use brings in the names from another clojure namespace without requiring namespace qualifications

:require brings in the names from another clojure namespace requiring namespace qualifications.

So if ns foo has a var bar, if you do (use 'foo), you can just refer to bar, but if you do (require 'foo), you have to say foo/bar, not just bar.
I should add: make sure file names do not include the "-" character even if the namespace you define with (ns ...) has a "-" character in it; replace it with a "_" instead in the file name (the namespace can continue to use the "-" though).

(edit: I will write more about namespaces, especially the syntax of the (ns ...) later.)

2010-04-09

My Programming Workflow (and specifically for Clojure)

My programming work flow is basic. Suppose I'm writing up some program in Matlab or Octave. I'll have the .m file open in a text editor, and a Matlab (or Octave) terminal (aka REPL?) open. Then I edit, save, load in terminal to run, see result, and repeat. Maybe I'll use the built-in editor in Matlab or QtOctave, or maybe I'll use Emacs. It doesn't matter much.

Then I come to this Clojure thing. Spent all my time setting up SLIME in Emacs so I have a REPL right there inside Emacs, along with my file of code (the .clj file). But actually all I really care for is setting up my edit, save, load in terminal/REPL to run, see result, and repeat loop!

Turns out it's simple, the key being using (load-file "...").

2010-03-30

My learning experience with the bare basics of Clojure

I've been too busy to really dig into Clojure programming, but I thought it'd be a good exercise as a mental note of what the last thing I was learning is by writing down a bit of my experience in the learning process...

As I begin to learn Clojure programming, I wanted to get some data to play with, to get a sense of what different functions do in Clojure, so I started with this:

(def x (for [x (range 5)] (rand)))
(def y (for [x (range 5)] (rand)))


This way I have two lists of random numbers, each having 5 numbers.  I wanted to match each element in x with its corresponding element in y (so that, eg, I could later add them together element wise as though they were vectors in math; or multiply them together later, then summed, as though I was doing a convolution, etc.)

So here's my first try:

2010-03-19

Clojure Incanter Startup Basics

Assuming your incanter folder resides in ~/bin, we'll start up the Swank server that runs the Clojure code, start up emacs, and execute some code. Here's how.

In your terminal, do the following three:
cd ~/bin/incanter
bin/swank
emacs


Now in emacs, do the following two to play with the ants demo:
C-x C-f path/to/ants.clj
M-x slime-connect


Accept default Host: 127.0.0.1 with a return-key press; same for port. Now the REPL is running within emacs. Go back to the buffer with the ants.clj file open and page down to the very bottom.

To compile the file, do C-c C-k.

To interactively execute code, go to the end of the line of code you want to execute from ants.clj, say the line (send-off animator animation) then do C-c C-e

Not hard at all!

(Edit 2010-04-09: Weird! C-c C-e to execute the last sexp didn't work for a while here. Had to kill the file buffer and reopen the file. Now it works! Odd.

Oh I see, using Clojure Mode, a quick M-x clojure-enable-slime-on-existing-buffers solved the problem. Although the keybindings changed somewhat. C-x C-e to get the above behaviour, while C-c C-e lets you type in a quick one-line to execute.)

2010-02-24

Easy Way to Building Clojure Projects: Leiningen

A good intro to using Leiningen to build Clojure projects without having to deal with all the classpath issues in Java: Building Clojure Projects with Leiningen.