2018-09-06: Notes On Leiningen

I started another tutorial on Purely Functional. One thing I learned is you should put a “:main” option in your lein project.clj.

When you start a Clojure REPL, by default you will go into the “user” namespace. From here you could call “require” on any namespace in your project.clj or any namespace that comes with Clojure, and use their functions. But to go to another namespace and have access to everything, you need to specify a “:main” option.

One of his lessons is about using Clojure to work with JSON, using a repo called AnApiOfIceAndFire, after the book series “Game Of Boobs” is based on. His project is a-game-of-json. He puts the JSON into some maps in the a-game-of-json.core namespace using the core Clojure function defonce. I went into that namespace, and I was unable to access that map. I couldn’t even call core functions like “+”.

Here is an example session:

REPL-y 0.3.7, nREPL 0.2.13
Clojure 1.8.0
Java HotSpot(TM) 64-Bit Server VM 9.0.1+11
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> (+ 2 3)
5
user=> (in-ns 'a-game-of-json.core)
#namespace[a-game-of-json.core]
a-game-of-json.core=> (+ 2 3)

CompilerException java.lang.RuntimeException: Unable to resolve symbol: + in this context, compiling:(/tmp/form-init15513125615002385176.clj:1:1) 
a-game-of-json.core=> (exit)
Bye for now!

This was not a problem in the “Introduction To Clojure” lesson, so I looked at that repo’s project.clj.

Here are the last two items in the project.clj file’s map:

:repl-options {:init (do
                         (use 'bakery.core)
                         (use 'introduction-to-clojure.core))
                 :skip-default-init true}

:main introduction-to-clojure.core

After some editing, I figured out that the way to get access to all the functions is to add the “:main” key/value pair. After that, everything seemed fine.

I did this with twitter-retriever, and I was able to call core functions in all the namespaces I defined. I was even able to access clj-time.local, and call a few core functions in that namespace. I used “in-ns” to make a new namespace, but I was not able to call any of the core functions. Maybe my config is a bit off.

But “user” was available to me without any problems.

You’re welcome.

Image from “Evangelia quattuor [Évangiles de Saint-Médard de Soissons] (1v-221v). Capitulare evangeliorum (223r-235v)”, a 9th century manuscript housed at the Bibliothèque nationale de France. Source gallica.bnf.fr / BnF; image assumed allowed under Fair Use.

2018-08-29 Update: Anonymous Functions and Kafka

I am still going through the tutorials on Purely Functional. It is taking longer than I thought. I might start going through Luminus at the same time as well. I think that building something while going through the lessons are a better way to learn.

In one of the lessons on Purely Functional, there is an interesting use of anonymous functions.

In version 3.17 of core.clj, we saw this in the function add:

(defn add
  ([ingredient]
   (add ingredient 1))
  ([ingredient amount]
   (cond
     (squeezed? ingredient) (add-squeezed ingredient amount)
     (scooped? ingredient)  (add-scooped ingredient amount)
     (simple? ingredient)   (add-simple ingredient amount)
     :else (error "I do not know the ingredient" ingredient))))

We see a call to “cond”. It uses the type of the ingredient to decide which function to call.

In version 3.18 of core.clj, he adds a map with anonymous functions:

(def usage {:squeezed (fn [ingredient amount]
                        (dotimes [i amount]
                          (grab ingredient)
                          (squeeze)
                          (add-to-bowl)))
            :simple (fn [ingredient amount]
                      (dotimes [i amount]
                        (grab ingredient)
                        (add-to-bowl)))
            :scooped (fn [ingredient amount]
                       (grab :cup)
                       (dotimes [i amount]
                         (scoop ingredient)
                         (add-to-bowl))
                         (release))})

In version 3.19 of core.clj, he gets the functions out of the map and sets them equal to a symbol, replacing the cond:

(defn add
  ([ingredient]
    (add ingredient 1))
  ([ingredient amount]
    (let [ingredient-type (usage-type ingredient)]
      (if (contains? usage ingredient-type)
        (let [f (get usage ingredient-type)]
          (f ingredient amount))
        (error "I do not know the ingredient" ingredient)))))

I will also look into Apache Kafka. I went to the Apache Kafka meetup recently. I think it will be important going forward.

You’re welcome.

Image from the Menologion of Basil II, an 11th century manuscript housed in the Vatican Library; image from Wikimedia, assumed allowed under Fair Use.

2018-06-03 Update

I am working through the tutorials on Purely Functional.

I got a bit hung up right away. I was trying to figure out how to reload files while working in the REPL as I updated the files. I followed this suggestion on StackOverflow by some guy named Dirk. At first, Dirk’s quirk did not work. Was Dirk a jerk? His burst was not voted first. SO at its worst? It made me so weak I could not speak. My REPL I could not tweak, putting an end to my learning streak. I do not want to sound pissed, but I do not wish to be dissed by Lisp. I am too tired for this.

Then I realized that while the files were reloaded, the new functions were not included in any aliases I used when I brought in the namespace with “require”. I could use the new functions, but I had to type out the ENTIRE namespace. O, the huge manatee! I did some googling, and I don’t think anyone mentions that.

You’re welcome.


2018-06-12_23.46.42 Update:

Just so it is easy to copy and paste, here are the commands:

(require '[clojure.tools.namespace.repl :refer [refresh]])
(refresh)

Or you could do this:

(require '[clojure.tools.namespace.repl :as repl])
(repl/refresh)

You might have a function called “refresh”, but you will probably not have a namespace “repl”.

Do this after you follow the directions in the StackOverflow answer. It is better to bring in the clojure.tools namespace in your profile than in every stinkin’ project.

Image from Bible de Moutier-Grandval, MS 10546, a 9th century manuscript housed at the British Library; image assumed allowed under Fair Use.

 

2018-05-28 Update: Clojure AI

I am still working through Clojure For the Brave and True.

I am on the exercises at the end of chapter 5, but I might skip a couple of them. I don’t do too well with these “re-implement function x” exercises. I will also start going through the videos on Purely Functional. I think he is raising the price, and my membership will renew at the end of the year.

I am looking into some Big Data/Deep Learning libraries for Clojure. This was inspired by the last Austin Clojure meetup: There were only four of us and we had a hard time thinking of topics. I tried searching for Clojure meetups around the country for topic ideas, and frankly the pickings were kind of slim.

Frankly, sometimes the technology industry in Austin and in general is kind of a drag. If you don’t want to do out-of-date Java or spend your life on the JavaScript-no-CoffeeScript-no-TypeScript-no-now-its-something-else-Script roller coaster, the pickings can be pretty slim. Sometimes I think about taking a year or so off and going though Structure And Interpretation of Computer Programs or How To Design Programs and become a smarter person (not just better with some particular language or API). The issue is that while a lot of people will say things like “we aren’t looking for people with experience in the technology we use; we just want to hire smart people”, they turn around and only hire people with experience in the technology they use.

Anyway, the consensus in the Clojure community is that Clojure needs to be a bigger player in these spaces. There is a LOT of Python in AI. Being a JVM language, Clojure will have wrappers around a lot of the Java libraries I wrote about in Thoughts On Native, GPU, Groovy, Java and Clojure (even though there was not a lot of Clojure in that post).

I know that Big Data and AI are different things. I was thinking about looking at Sparkling to work with Spark (which I hope I can do on my laptop; do you need big machines to work with Big Data libraries?). This weekend I started looking at some of the videos on the Clojure TV channel on YouTube from Clojure Conj 2017. I did not go, but there seemed to be a LOT of videos about AI/Deep Learning (yes, I am using those terms interchangeably even though a lot of people do not).

There was Deep Learning Needs Clojure by Carin Meier, author of Living Clojure. She wasted the first seven minutes on some stupid joke about Skynet, which is a lot for a thirty minute presentation. I am glad I did not pay to see that. After that it gets better. The talk was pretty general. She mentioned some Clojure libraries, Graal VM, and at about 25:00 talks about how to get into Deep Learning.

Declarative Deep Learning In Clojure by Will Hoyt talked about Deeplearning4j. He says that matrices and linear algebra pretty much IS deep learning. There is some neuroscience in this presentation. He also talks about how the Clojure code is easier to deal with than Java and builders. I do not think he ever posts a link to dl4clj, which according to the Deeplearning4j site is the official port.

The Tensors Must Flow by William Piel is about his library Guildsman, which is a new Clojure interface to Google’s TensorFlow. There are already two Clojure projects that allow access to TensorFlow (clojure-tensorflow and tensorflow-clj). They do some Java interop around the TensorFlow Java API. (You can see Google’s not-quite-Javadoc here.) He wanted something that was more idiomatic for Clojure programmers. TensorFlow is written in Python (what else?), which Google then ports to C++ and other languages. But like most Python AI libs, it seems like it is just a wrapper around CPU or GPU code.

I understood the talk when I watched it. Really, I did. From what I remember, TensorFlow uses something called gradients in its process. I think a gradient implementation is an operation in the process. bpiel says the best way to contribute to Guildsman is to actually contribute C++ gradients to TensorFlow itself. He said in the talk he wanted to be done with Guildsman before the Conj in October. It is almost June, and he is still working on it.

The last one about Deep Learning was Building Machine Learning Models with Clojure and Cortex by Joyce Xu. She talked about a Clojure library called Cortex. This library is closer to Uncomplicate, in that while it does interface with a GPU  library, it is not a wrapper around a Java library in the middle.

The traffic on the Cortex mailing list seems to have dropped off, it’s not at version 1 and there seems to be a drop-off in contributions since January.

I do wish the speakers spent a bit more time talking about the implementation details of these libraries. Hosted languages (like Java or Python) do not do a lot of their AI crunching directly. They usually call a native library to calculate on either the CPU or the GPU. And for the GPU, some can do either CUDA (in other words, NVidia) or OpenCL (every other video card). Some libraries have multiple options, like Uncomplicate or Deeplearning4j. TensorFlow can use a CPU (they have instructions on getting the JNI file here) or GPU withy NVidia only. I have not tried Guildsman, so I do not know how he handles things or if he requires an NVidia GPU. I also have no idea how Cortex handles it. Their instructions tell you to get some SDK from NVidia. Perhaps they default to a CPU if there is no NVidia GPU.

I bought my laptop used, and the one I used before this one is at least six years old. I think the older one had an Intel video card, but I could not find any SDK for that version of the video chip. I think my current laptop may also be too old. (Dealing with the Intel Math Kernel is a LOT easier than wading through their OpenCL pages.) The only reason I can think of to buy an Apple laptop is to not deal with this. It is a bit frustrating. The whole point of using a language like Java or Ruby or Python is that I do not want to deal with hardware details.

Anyway, besides all that, I still have a few ideas for a few web apps to do in Clojure.


2018-05-29_22.14.49 update:

I looked at the Cortex mailing list, and apparently you can run the tests just using the CPU:

lein with-profile cpu-only test

It would be great if they put that in the README.

You’re welcome.

Image from M.p.th.f.66, Quattuor Evangelia, a 9th century manuscript on Franconica, the online repository for the Würzburg University Library; image covered under CC BY-NC-SA 4.0  and CC BY-NC-ND 4.0; Imagemagick was used to enhance the colors, shrink the image, and rotate it.

2018-05-20 Update

I am still going through Clojure For the Brave and True. He does some pretty mind-bending things in chapter 5 when he walks you through his pegthing program.

Check this out:

(defn add-pos
  "Pegs the position and performs connections"
  [board max-pos pos]
  (let [pegged-board (assoc-in board [pos :pegged] true)]
    (reduce (fn [new-board connection-creation-fn]
              (connection-creation-fn new-board max-pos pos))
            pegged-board
            [connect-right connect-down-left connect-down-right])))

In the call to “reduce”, he is sending it a collection of functions called “connect-right”, “connect-down-left” and “connect-down-right”. Then in the function in the reduce, there is a call to “connection-creation-fn”, which is passed to the anonymous function. So he is not actually calling a function called “connection-creation-fn”; that is a placeholder for the functions in the array being passed to “reduce”.

Granted, I knew before this what “reduce” is. But in this chapter he has a lot of functions calling functions. Granted, he does say that reducing over a collection of functions is not something you will do or see very often. Even though I have been looking at Clojure for a while, and this chapter wasn’t really anything new, it was a reminder how different functional programming can be.

You’re welcome.

2018-05-14 Update

I am still going through the the code review of twitter-retriever by Eric Normand of Purely Functional. And going through Clojure For the Brave and True.  Sometimes I come home from work and I just don’t feel like doing anything.

I am seeing a lot of nice core functions in Clojure For the Brave and True. It is helping me in my quest to post examples of as much of the core functions as I can.

I am still watching the code review in bits and pieces. I guess I don’t like being criticized, even though he is very supportive (not only of members of his site, but the Clojure community in general) and not being too critical, and I have incorporated a few of the things he has said. I had to do some funky things with tweet IDs to work Twitter timelines that seemed a bit confusing and might not be idiomatic Clojure; Twitter has a guide for that here. That said, I was able to incorporate some suggestions and reduced the lines of code, and I was able to remove one of the SQL calls and use some functions to get the same result.

He did mention that a lot of the defs should be in a let block. I showed this to the Austin Clojure group, and they had the same suggestion.

I still have a lot of println statements in there. I can run this on the command line or call some of the functions in the REPL, but testing some of this stuff is a bit beyond me. I have no idea how to mock/stub database calls or external calls in Clojure.

You’re welcome.

Image from a 9th century manuscript held in the St. Vitus Cathedral in Prague; image from Wikimedia, assumed allowed under Fair Use.

2018-05-06 Update

I have watched part of the twitter retriever code review. I did make some changes based on what he said.

I am also going through the videos on Purely Functional. I am also working on Clojure For the Brave and True. I am on chapter 4.

I started changing my workouts. I wonder if that is taking energy away from Clojure. There is a lot to learn. I have been working on and off on a post about why I am interested in Clojure. I don’t think I have written a post yet about why Clojure and Lisp in general interests me.

You’re welcome.

Image from Xanten Gospels, a Carolingian manuscript from the 9th century, housed at the Royal Library of Belgium (Wikipedia page here); image from Wikipedia, assumed allowed under Fair Use.

2018-04-22 Update

I am still slowly going through Clojure For the Brave and True. I will keep going for the time being until inspiration hits.

I did try another app that uses the WordPress API. It is called WP-API v2 Java Client. I was not able to get this to work either. I thought (briefly) about making a direct SQL call to insert the post. I turned on SQL logging and decided it might be a bad idea. I have heard that many SQL experts are not impressed with WordPress’s schema. Plus I would either have to refactor (slightly) to use MySQL, or figure out how to use two connections.

Eric Normand of Purely Functional put up his code review of twitter-retriever. Take a look and let me know if it was good. He also has a channel where he talks about functional programming in general. He does these in his backyard.

You’re welcome.

Image from Harley Golden Gospels, Harley MS 2788, a 9th century manuscript housed at the British Library; image assumed allowed under Fair Use.

2018-04-08 Update

I think I am done for the time being with the Twitter-retriever.

As of now, it can get the last 3200 tweets of a user (excluding retweets) and capture any tweets going forward. It prints them out in HTML to a file with each tweet in enclosing “li” tags.

I tried to get the WordPress REST API working. I installed WordPress on my laptop, but I had a hard time getting anything to work with the POST command. GET was easy. It is the authentication that stumped me. I got a couple to work with curl, but no POST commands to work with clj-http. I might try again later, but for now I am done. It seemed to be a rabbit-hole of google searches, adding yet another plugin, editing the .htaccess file, etc, etc. For now, what I have is good enough.

I might look into an alternative to environ, but for the time being I think it will do.

I don’t know what I will work on next. I might get back to Simply Scheme. I might go back to coding examples for the Clojure API. I might go through some lessons at Purely Functional. Maybe TensorFlow (like with guildsman or clojure-tensorflow or tensorflow-clj). Maybe Luminus. As of right now, I have not decided.

You’re welcome.

Image from St. Riquier Gospels, a Carolingian manuscript from the 8th century, housed at the Bibliothèque Municipale d’Abbeville; image from Wikipedia, assumed allowed under Fair Use.

2018-04-01 Update

I am still working on the twitter retriever.

I am almost ready to start looking at the wordpress API. I think I will start a local instance so I can work with the REST API and not clutter up this site.

I also looked a bit more at the Clojure library environ. What I like about it is that it gets all the environment variables at start time, and any old time you want you just call the env function, and it works. No fuss, no getting files in every namespace. But I am not so sure if it would work in a production environment. There are a lot of pages from people saying that putting usernames and passwords in the environment is a bad idea, and I am leaning that way too. I have not been able to get my app to use environment variables in .lein-env, profiles.clj or .boot-env when I run it with “lein run”.

I also found out that (after some trial and error) that I cannot compile my app with “lein uberjar”. I use environ for my database creds, and it looks for them during the uberjar process. I was not able to use the profiles.clj or .boot-env for this either. I had to put the database variables into the environment when I compile. (So maybe there is not security risk during production after all.)  Here is what I did in bash:

export DATABASE_URL="postgres://localhost:5433/twit_retrieve_dev_db?user=twit_getter&password=this-is-twitter"
export DB_DRIVER=org.postgresql.Driver

That is not a production database, so no big deal if the creds are here. But that seemed to do the trick. I was able to use the uberjar in another window without any problems (from what I remember).

Also: Eric Normand from Purely Functional got back to me. I told him I was nowhere near done. Partially due to not spending as much time as I should, partially parsing the infamous error messages, partially due to find and trying out libraries as I get this up and running. He wants to do a code review. I think I will clean up a few things before I say yes.

Before I do, I would like to copy and paste in here a few functions for the sake of posterity:

(defn month-string-to-num [month-str]
  (case month-str
    "Jan" "01"
    "Feb" "02"
    "Mar" "03"
    "Apr" "04"
    "May" "05"
    "Jun" "06"
    "Jul" "07"
    "Aug" "08"
    "Sep" "09"
    "Oct" "10"
    "Nov" "11"
    "Dec" "12"))

(defn create-time-from-map [time-from-map]
  (let [matches (re-matches #"(\w{3})\s(\w{3})\s(\d{2})\s(\d{2}\p{Punct}\d{2}\p{Punct}\d{2})\s\+\d{4}\s(\d{4})" 
                time-from-map)
        year        (nth matches 5)
        month       (month-string-to-num (nth matches 2))
        day         (nth matches 3)
        time-of-day (nth matches 4)
            ]
        (println year,"-", month, "-", day, " ", time-of-day  "seconds since midnight")
        (println "Here it is as a string: ", (str year,"-", month, "-", day, " ", time-of-day))
(str year,"-", month, "-", day, " ", time-of-day)))

Those should parse a string with a timestamp like “Tue Feb 11 15:03:45 +0000 2014” and convert it to “2014-02-11_15:03:45”. I then found clj-time.

I also think I found a couple of function calls you can try in the REPL to interactively work on regular expressions:

(re-find (re-matcher #"(\w{3})\s(\w{3}\s\d{2})\s(\d{2}\p{Punct}\d{2}\p{Punct}\d{2})\s\+\d{4}\s(\d{4})" "Sun Dec 20 18:17:33 +0000 2009"))
(re-matches #"(\w{3})\s(\w{3})\s(\d{2})\s(\d{2}\p{Punct}\d{2}\p{Punct}\d{2})\s\+\d{4}\s(\d{4})" "Sun Dec 20 18:17:33 +0000 2009")

I know those are pretty wide, but I found it helpful.

Here is just a little let:

(let [action (:action (:options arg-map))]
     (case action
       "create-user" (do
                       (println "You want to create a user")
                       (actions/create-user user-name my-creds))
       "retrieve-tweets" (do
                           (println "You want to retrieve tweets"))
       (do
         (println "Action ", action, " not specified.")
         (println "Possible actions: create-user retrieve-tweets"))))

 

You’re welcome.

Image from Athelstan Psalter, a 9th century manuscript housed at the British Library; image assumed allowed under Fair Use.