Background
I've written a hack for Emacs that lets me send a Clojure form from an editor buffer to a REPL buffer. It's working fine, except that if the two buffers are in different namespaces the copied text doesn't usually make sense, or, worse, it might make sense but have a different meaning to that in the editor buffer.
I want to transform the text so that it makes sense in the REPL buffer.
A Solution in Common Lisp
In Common Lisp, I could do this using the following function:
;; Common Lisp
(defun translate-text-between-packages (text from-package to-package)
(let* ((*package* from-package)
(form (read-from-string text))
(*package* to-package))
(with-output-to-string (*standard-output*)
(pprint form))))
And a sample use:
;; Common Lisp
(make-package 'editor-package)
(make-package 'repl-package)
(defvar repl-package::a)
(translate-text-between-packages "(+ repl-package::a b)"
(find-package 'editor-package)
(find-package 'repl-package))
;; => "(+ A EDITOR-PACKAGE::B)"
The package name qualifications in the input string and the output string are different—exactly what's needed to solve the problem of copying and pasting text between packages.
(BTW, there's stuff about how to run the translation code in the Common Lisp process and move stuff between the Emacs world and the Common Lisp world, but I'm ok with that and I don't particularly want to get into it here.)
A Non-Solution in Clojure
Here's a direct translation into Clojure:
;; Clojure
(defn translate-text-between-namespaces [text from-ns to-ns]
(let [*ns* from-ns
form (read-string text)
*ns* to-ns]
(with-out-str
(clojure.pprint/pprint form))))
And a sample use:
;; Clojure
(create-ns 'editor-ns)
(create-ns 'repl-ns)
(translate-text-between-namespaces "(+ repl-ns/a b)"
(find-ns 'editor-ns)
(find-ns 'repl-ns))
;; => "(+ repl-ns/a b)"
So the translation function in Clojure has done nothing. That's because symbols and packages/namespaces in Common Lisp and Clojure work differently.
In Common Lisp symbols belong to a package and the determination of a symbol's package happens at read time.
In Clojure, for good reasons, symbols do not belong to a namespace and the determination of a symbol's namespace happens at evaluation time.
Can This Be Done in Clojure?
So, finally, my question: Can I convert Clojure code from one namespace to another?
I don't understand your use case, but here is a way to transform symbols from one namespace to another.
(require 'clojure.walk 'clojure.pprint)
(defn ns-trans-form [ns1 ns2 form]
(clojure.walk/prewalk
(fn [f] (if ((every-pred symbol? #(= (namespace %) ns1)) f)
(symbol ns2 (name f))
f))
form))
(defn ns-trans-text [ns1 ns2 text]
(with-out-str
(->> text
read-string
(ns-trans-form ns1 ns2)
clojure.pprint/pprint)))
(print (ns-trans-text "editor-ns" "repl-ns" "(+ editor-ns/a b)" ))
;=> (+ repl-ns/a b)
So, editor-ns/a was transformed to repl-ns/a.
(Answering my own question...)
Given that it's not easy to refer to a namespace's non-public vars from outside the namespace, there's no simple way to do this.
Perhaps a hack is possible, based on the idea at http://christophermaier.name/blog/2011/04/30/not-so-private-clojure-functions. That would involve walking the form and creating new symbols that resolve to new vars that have the same value as vars referred to in the original form. Perhaps I'll investigate this further sometime, but not right now.
Related
So, this is what I want to do
(def body `(prn sth))
(defn f [sth] body)
(f "hello")
; can it prn hello here?
Is this possible?
If you want to "take a data structure and embed it in code to be executed", then you can do something like this.
You will want to tweak the body to embed like this:
(def body `(prn ~'sth))
that is, prefixing the sth local variable with ~' so that it will not be namespaced. Then you need a macro that will embed the code for you:
(defmacro insert-body [body]
(eval body))
Using this macro inside the f function to embed the body and putting things together, you get this code:
(defmacro insert-body [body]
(eval body))
(def body `(prn ~'sth))
(defn f [sth] (insert-body body))
You can now call f with an argument and it will work as expected:
> (f "hello")
"hello"
nil
The function macroexpand comes in handy to test that the macro does what it is supposed to be doing:
(macroexpand `(insert-body body))
;; => (clojure.core/prn sth)
But it is not clear to me what you are trying to accomplish or what you would gain from writing your code this way. Whatever you want to accomplish eventually, there is most likely a better way to accomplish it than what I suggest here. I am just providing a specific answer to your question, nothing more, nothing less. So if you clarify your question and give more details, it will also be possible to provide a better answer that addresses you actual problem.
Here's some code I wrote, using clojure.core.match, which performs a pretty common programmng task. A function takes some "commands" (or "objects", "records", or whatever you prefer to call them), has to do something different with each type, and has to destructure them to figure out exactly what to do, and different command types might have to be destructured differently:
(defn action->edits [g action]
"Returns vector of edits needed to perform action in graph g."
(match action
[:boost from to]
[[:add-edge from to 1.0]]
[:retract from to]
[[:remove-edge from to]]
[:normalize from to] ; a change has just been made to from->to
(map (fn [that] [:remove-edge from that])
(successors-except g from to))
[:recip-normalize to from] ; a change has just been made to from->to
[]
[:reduce-to-unofficial from to competitor]
[[:remove-edge from to] (make-competitive-edge from competitor]))
I'm mostly imitating the way people commonly use the pmatch macro in Scheme. I'd like to know what's the idiomatic way to do this in Clojure.
Here's what I like about the above code:
It's very readable.
It was effortless to write.
Here's what I don't like:
Accessing the from and to fields from anywhere but inside a match macro is extremely unreadable and error-prone. For example, to extract the from element from most of the action vectors, you write (action 1). That code will break if I ever add a new action, and it breaks right now on :recip-normalize.
The code generated by match is inefficient: it searches by repeatedly throwing and catching exceptions. It doesn't just generate a big nested if.
I experimented a little with representing the commands as maps, but it seemed to get verbose, and the name of the command doesn't stand out well, greatly reducing readability:
(match action
{:action :boost :from from :to to}
[{:edit :add-edge :from from :to to :weight 1.0}]
{:action :retract :from from :to to}
[{:edit :remove-edge :from from :to to}]
. . .)
Probably future versions of match will generate better code, but the poor code generated now (and lack of support for records) suggests that in Clojure, people have been handling this kind of thing happily for years without match. So how do you do this kind of thing in Clojure?
I would utilize clojure's build-in destructuring facilities, since I do not see a requirement for core.match here - but I might be missing something.
For example:
(defn action->edits [g [action from to]]
(condp = action
:boost "boosting"
:retract "retracting"
:normalize-ksp-style (recur g [:boost from to])
nil))
(action->edits 2 [:normalize-ksp-style 1 2])
;=> "boosting"
I've been trying to get a simple reg-ex working in Clojure to test a string for some SQL reserved words (select, from, where etc.) but just can't get it to work:
(defn areserved? [c]
(re-find #"select|from|where|order by|group by" c))
(I split a string by spaces then go over all the words)
Help would be greatly appreciated,
Thanks!
EDIT: My first goal (after only reading some examples and basic Clojure materials) is to parse a string and return for each part of it (i.e. words) what "job" they have in the statement (a reserved word, a string etc.).
What I have so far:
(use '[clojure.string :only (join split)])
(defn isdigit? [c]
(re-find #"[0-9]" c))
(defn isletter? [c]
(re-find #"[a-zA-Z]" c))
(defn issymbol? [c]
(re-find #"[\(\)\[\]!\.+-><=\?*]" c))
(defn isstring? [c]
(re-find #"[\"']" c))
(defn areserved? [c]
(if (re-find #"select|from|where|order by|group by" c)
true
false))
(defn what-is [token]
(let [c (subs token 0 1)]
(cond
(isletter? c) :word
(areserved? c) :reserved
(isdigit? c) :number
(issymbol? c) :symbol
(isstring? c) :string)))
(defn checkr [token]
{:token token
:type (what-is token)})
(defn goparse [sql-str]
(map checkr (.split sql-str " ")))
Thanks for all the help guys! it's great to see so much support for such a relatively new language (at least for me :) )
I'm not entirely sure what you want exactly, but here's a couple of variations to coerce your first regex match to a boolean:
(defn areserved? [c]
(string?
(re-find #"select|from|where|order by|group by"c)))
(defn areserved? [c]
(if (re-find #"select|from|where|order by|group by"c)
true
false))
UPDATE in response to question edit:
Thanks for posting more code. Unfortunately there are a number of issues here that we could
try to address by patching your existing code in a simplistic and naïve fashion, but it will
only get you so far, before you hit the next problem with this single iteration approach.
#alex is correct, that your areserved? method will fail to match order by if you have already
split your string by white space. That said, a simple fix is to treat order and by as separate keywords (which they are, even though they always appear together).
The next issue is that the areserved? function will match keywords in a string, but you are dispatching it against a character in the what-is function. You nearly always get a match in your cond for isletter?, so you will everything is marked as a 'word'.
All in all, it looks like you are trying to do too much work in a single application of map.
I'm not sure if you are just doing this for fun to play with Clojure (which is admirable - keep going!), in which case, maybe it doesn't matter if you press on with this simple parsing approach... you'll definitely learn something; but if you would like to take it further and parse SQL more successfully, then I would suggest that you may find it helpful to to read a little on Lexing, Parsing and building Abstract Syntax Trees (AST).
Brian Carper has written about using the Java parser generator "ANTLR" from Clojure - it's a few years old, but might be worth looking at.
You also might be able to get some transferrable ideas from this chapter from the F# programming book on lexing and parsing SQL.
I'm learning Clojure and as an exercise I wanted to write something like the unix "comm" command.
To do this, I read the contents of each file into a set, then use difference/intersection to show exclusive/common files.
After a lot of repl-time I came up with something like this for the set creation part:
(def contents (ref #{}))
(doseq [line (read-lines "/tmp/a.txt")]
(dosync (ref-set contents (conj #contents line))))
(I'm using duck-streams/read-lines to seq the contents of the file).
This is my first stab at any kind of functional programming or lisp/Clojure. For instance, I couldn't understand why, when I did a conj on the set, the set was still empty. This lead me to learning about refs.
Is there a better Clojure/functional way to do this? By using ref-set, am I just twisting the code to a non-functional mindset or is my code along the lines of how it should be done?
Is there a a library that already does this? This seems like a relatively ordinary thing to want to do but I couldn't find anything like it.
Clojure 1.3:
user> (require '[clojure.java [io :as io]])
nil
user> (line-seq (io/reader "foo.txt"))
("foo" "bar" "baz")
user> (into #{} (line-seq (io/reader "foo.txt")))
#{"foo" "bar" "baz"}
line-seq gives you a lazy sequence where each item in the sequence is a line in the file.
into dumps it all into a set. To do what you were trying to do (add each item one by one into a set), rather than doseq and refs, you could do:
user> (reduce conj #{} (line-seq (io/reader "foo.txt")))
#{"foo" "bar" "baz"}
Note that the Unix comm compares two sorted files, which is likely a more efficient way to compare files than doing set intersection.
Edit: Dave Ray is right, to avoid leaking open file handles it's better to do this:
user> (with-open [f (io/reader "foo.txt")]
(into #{} (line-seq f)))
#{"foo" "bar" "baz"}
I always read with slurp and after that split with re-seq due to my needs.
I am very very new to clojure. The zip utility looks interesting but I cant seem to use it.
I tried
;; ZIP
(:use "zip")
(def data '[[a * b] + [c * d]])
(def dz (zip/vector-zip data))
But I am getting
java.lang.Exception: No such namespace: zip
How do yo use external libraries?
You may be confusing two different ways to import code. You can do it this way:
user> (use 'clojure.zip)
Or while you're declaring a namespace in a source file:
(ns foo
(:use clojure.zip))
The second version is a macro that is expanded into the first.
Outside of (ns), doing (:use "zip") is going to treat :use as a function and call it with "zip" as its parameter (i.e. try to use the string "zip" as a collection and look up the key :use in it), which does nothing.
clojure.zip has some functions whose names clash with things in clojure.core though, so you either have to do something like this:
user> (use '(clojure [zip :rename {next next-zip replace replace-zip remove remove-zip}]))
Or preferably this:
user> (require '(clojure [zip :as zip]))
With the latter you can refer to functions like (zip/vector-zip data) as you wish.
See the documentation for require and refer and the page talking about libs.
I don't know much about clojure, but this little ditty seems to work:
(require '[clojure.zip :as zip])
(def t '(:a (:b :d) (:c :e :f)))
(def z (zip/zipper rest rest cons t))
(zip/node z)