I'd like to be able to align the following:
(def first-thing "some text")
(def second-longer-thing (str first-thing ", yep"))
Like so:
(def first-thing "some text")
(def second-longer-thing (str first-thing ", yep"))
Can clojure-mode do this? It seems to have pretty powerful indentation abilities.
Related
I'm new to Clojure.
In Java I can do something like this extremely contrived example:
public abstract class Foo {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}
public interface Fooable {
public void sayHello(String name);
}
public class Bar extends Foo implements Fooable, Barable {
...
}
public class Baz extends Foo implements Fooable, Barable {
...
}
So, here we have two classes, implementing the Fooable interface the same way (through their abstract base class parent) and (presumably) implementing the Barable interface two different ways.
In Clojure, I can use defrecord to define types Bar and Baz, and have them implement protocols rather than interfaces (which, essentially, are what protocols are, from what I understand). I know how to do this in the most basic sense, but anything more complex is stumping me.
Given this:
(defrecord Bar [x])
(defrecord Baz [x y])
(defprotocol Foo (say-hello [this name]))
how would I recreate the abstract base class functionality above, i.e. have one protocol implemented the same way across multiple defrecord types, without duplicating code? I could of course do this, but the code repetition makes me cringe:
(extend-type Bar
Foo
(say-hello [this name] (str "Hello, " name "!")))
(extend-type Baz
Foo
(say-hello [this name] (str "Hello, " name "!")))
There has to be a cleaner way of doing this. Again, I'm new to Clojure (and Lisp in general; I'm trying to learn Common Lisp concurrently), so macros are an entirely new paradigm for me, but I thought I'd try my hand at one. Not surprisingly, it fails, and I'm not sure why:
(defmacro extend-type-list [tlist proto fmap]
(doseq
[t tlist] (list 'extend t proto fmap)))
fmap of course is a map of functions, i.e. {:say-hello (fn [item x] (str "Hello, " x "!"))} The doseq, applied to a concrete list of record types and a concrete protocol, does work. Within the macro, of course, it doesn't, macroexpand calls return nil.
So, question 1, I guess, is "what's wrong with my macro?". Question 2 is, how else can I programmatically extend protocols for types without a lot of repetitive boilerplate code?
Your macro is returning nil because doseq returns nil.
A Clojure macro should generate a new form using a combination of syntax-quotes (`), unquotes (~), and unquote-splicing (~#) reader macros.
(defmacro extend-type-list [types protocol fmap]
`(do ~#(map (fn [t]
`(extend ~t ~protocol ~fmap))
types)))
Without using a macro, you have a few options:
(defrecord Bar [x])
(defrecord Baz [x y])
Use a simple var to hold the function map:
(defprotocol Foo (say-hello [this name]))
(def base-implementation
{:say-hello (fn [this name] (str "Hello, " name "!"))})
(extend Bar
Foo
base-implementation)
(extend Baz
Foo
base-implementation)
(say-hello (Bar. 1) "Bar") ;; => "Hello, Bar!"
(say-hello (Baz. 1 2) "Baz") ;; => "Hello, Baz!"
If you move to multimethods, you can accomplish something similar with clojure.core/derive
(defmulti say-hello (fn [this name] (class this)))
(derive Bar ::base)
(derive Baz ::base)
(defmethod say-hello ::base [_ name]
(str "Hello, " name "!"))
(say-hello (Bar. 1) "Bar") ;; => "Hello, Bar!"
(say-hello (Baz. 1 2) "Baz") ;; => "Hello, Baz!"
Question 1
Macros are just normal functions that are called at compile-time rather than run-time. If you look at the definition of defmacro, it actually just defines a function with some special meta-data.
Macros return Clojure syntax, which is spliced into the code at the point of the macro call. This means that your macro should return (quoted) syntax that looks exactly like what you would have typed manually into the source file at that point.
I find that a good way to design complex macros is to first declare it with defn, tweaking it until it returns my expected output. As with all Lisp development, the REPL is your friend! This approach requires you to manually quote any parameters being passed to your proto-macro function. If you declare it as a macro then all the arguments are treated as data (e.g. variable names are passed in as symbols), but if you call it as a function it'll try to actually evaluate the arguments if you don't quote them!
If you try this out with your macro you'll see that it doesn't actually return anything! This is because you're using doseq, which is for side-effecting computations. You want to use for in order to build up the syntax to do all of the extend-type calls. You'll probably need to wrap them in a (do ) form since a macro must return a single form.
Question 2
According to the documentation, you can actually implement multiple interfaces/protocols directly within inside the defrecord macro. After your field declaration, just add the bodies of all the extend-type forms you would have declared.
(defrecord Bar [x]
Foo1
(method1 [this y] 'do-something)
Foo2
(method2 [this y z] 'do-something-else))
It looks like the two questions you asked at the bottom have been pretty comprehensively answered, but you asked something interesting in the text of the question: "how would I recreate the abstract base class functionality above, i.e. have one protocol implemented the same way across multiple defrecord types, without duplicating code?"
If we look at the documentation for datatypes, two quotes jump out:
Concrete derivation is bad
you cannot derive datatypes from concrete classes, only interfaces
Tying polymorphism to inheritance is bad
Clojure datatypes intentionally restrict some features of Java, for example concrete derivation. So I believe the answer to your question really is, "you shouldn't". Multimethods or def'ing the functionality outside the defrecord and calling into it (like DaoWen's answer) should be preferred.
But if you really, really want to do exactly what you're doing in Java, you can use gen-class to extend a class.
(gen-class :name Bar :extends Foo :implements [Fooable])
(gen-class :name Baz :extends Foo :implements [Fooable])
Note that this implementation is a bit of a hack (you can't test in the repl because gen-class only does something when compiling), and doesn't use the :gen-class key in the ns macro like you typically would if you really had to gen-class. But using gen-class at all is a bit of a hack.
If I evaluate
(def ^:macro my-defn1 #'defn)
a macro named 'my-defn1' is defined, which I can use just like 'defn'.
However, if I evaluate instead
(if true
(def ^:macro my-defn2 #'defn))
the var for 'my-defn2' doesn't have the :macro metadata set and I can't use it as a macro, even though the 'def' form is equal to the previous case.
Here is the complete code (http://cljbin.com/paste/52322ba5e4b0fa645e7f9243):
(def ^:macro my-defn1 #'defn)
(if true
(def ^:macro my-defn2 #'defn))
(println (meta #'my-defn1)) ; => contains :macro
(println (meta #'my-defn2)) ; => doesn't contain :macro!
(my-defn1 hello1 []
(println "hello 1"))
(hello1) ; => prints "hello 1"
(my-defn2 hello2 [] ; => CompilerException: Unable to resolve
(println "hello 2")) ; symbol: hello2 in this context
What makes the behaviour different?
Clojure's def cannot really be conditionally applied. The documentation for def is IMO insufficiently strong on that part. It's not just bad style, it can cause all kinds of subtle issues.
You should only use def at the top-level, or in a do or let form at the top-level. Conditionally applying def will result in the functionality being split up in something like declare and a subsequent conditional def, but not always in the way that you'd expect/like.
You might be better off using def at the top level here and then conditionally using alter-var-root. Or use (def my-var (if .. .. ..)). Think about why you'd ever want to have a global definition "disappear".
I want to switch on the class of a given object in order to encode it.
(defn encoded-msg-for [msg]
(case (class msg)
java.lang.Double (encode-double msg)
java.lang.String (encode-str msg)
java.lang.Long (encode-int msg)
java.lang.Boolean (encode-bool msg)
clojure.lang.PersistentArrayMap (encode-hash msg)
clojure.lang.PersistentVector (encode-vec msg)
nil "~"
)
)
When I call (encoded-msg-for {}), it returns No matching clause: class clojure.lang.PersistentArrayMap
What is odd is that putting the cases into a hash-map (with the classes as keys and strings as values) works perfectly well.
Also, (= (class {}) clojure.lang.PersistentArrayMap) is true. What comparison is happening here and how can I switch either on the class of the object itself or (better) something in its hierarchy?
I believe case treats the class names as literal symbols - it does not resolve them to actual classes:
>>> (case 'clojure.lang.PersistentArrayMap clojure.lang.PersistentArrayMap 1 17)
1
>>> (case clojure.lang.PersistentArrayMap clojure.lang.PersistentArrayMap 1 17)
17
This is rather unintuitive, but so it works in Clojure's case. Anyway, the idiomatic way is to use defmulti and defmethod instead of switching on type:
(defmulti encoded-msg class)
(defmethod encoded-msg java.util.Map [x] 5)
(defmethod encoded-msg java.lang.Double [x] 7)
>>> (encoded-msg {})
5
>>> (encoded-msg 2.0)
7
The dispatcher uses the isa? predicate which deals well with the comparisons of types, in particular it works well with Java inheritance.
If you don't want to use defmulti, then condp might replace case in your use case, as it properly evaluates the test-expressions. On the other hand it doesn't provide constant time dispatch.
If you are dispatching only on the class then protocols might be a nice solution, because they will enable you (or your API's client) to provide implementations for other types at a later time, here is an example:
(defprotocol Encodable
(encode [this]))
(extend-protocol Encodable
java.lang.String
(encode [this] (println "encoding string"))
clojure.lang.PersistentVector
(encode [this] (println "encoding vector")))
If you need to have finer-grained dispatch or you know extending to other types is not necessary then there might be too much boilerplate in this solution.
If you are looking for an alternative way to achieve this, take a peek at condp:
(condp = (type {})
clojure.lang.PersistentArrayMap :one
clojure.lang.PersistentVector :many
:unknown) ;; => :one
If I had a complex if statement that I did not want to overflow simply for aesthetic purposes, what would be the most kosher way to break it up since coffeescript will interpret returns as the body of the statement in this case?
if (foo is bar.data.stuff and foo isnt bar.data.otherstuff) or (not foo and not bar)
awesome sauce
else lame sauce
CoffeeScript will not interpret the next line as the body of the statement if the line ends with an operator, so this is ok:
# OK!
if a and
not
b
c()
it compiles to
if (a && !b) {
c();
}
so your if could be formatted as
# OK!
if (foo is
bar.data.stuff and
foo isnt bar.data.otherstuff) or
(not foo and not bar)
awesome sauce
else lame sauce
or any other line-breaking scheme so long as the lines end in and or or or is or == or not or some such operator
As to indentation, you can indent the non-first lines of your if so long as the body is even more indented:
# OK!
if (foo is
bar.data.stuff and
foo isnt bar.data.otherstuff) or
(not foo and not bar)
awesome sauce
else lame sauce
What you cannot do is this:
# BAD
if (foo #doesn't end on operator!
is bar.data.stuff and
foo isnt bar.data.otherstuff) or
(not foo and not bar)
awesome sauce
else lame sauce
This changes your code's meaning somewhat, but may be of some use:
return lame sauce unless foo and bar
if foo is bar.data.stuff isnt bar.data.otherstuff
awesome sauce
else
lame sauce
Note the is...isnt chain, which is legit, just as a < b < c is legit in CoffeeScript. Of course, the repetition of lame sauce is unfortunate, and you may not want to return right away. Another approach would be to use soaks to write
data = bar?.data
if foo and foo is data?.stuff isnt data?.otherstuff
awesome sauce
else
lame sauce
The if foo and is a little inelegant; you could discard it if there's no chance that foo is undefined.
Like any other language, by not having them in the first place. Give names to the different parts an treat them separately. Either by declaring predicates, or by just creating a couple of boolean vars.
bar.isBaz = -> #data.stuff != #data.otherstuff
bar.isAwsome = (foo) -> #isBaz() && #data.stuff == foo
if not bar? or bar.isAwesome foo
awesome sauce
else lame sauce
Escaping the linebreak looks most readable to me:
if (foo is bar.data.stuff and foo isnt bar.data.otherstuff) \
or (not foo and not bar)
awesome sauce
else lame sauce
When a lot of low-level boilerplate occurs you should increase level of abstract.
The best solutions are:
to use good named variables and functions
logic rules in if/else statements
One of logic rules is:
(not A and not B) == not (A or B)
The first way. Variables:
isStuff = foo is bar.data.stuff
isntOtherStuff = foo isnt bar.data.otherstuff
isStuffNotOtherStuff = isStuff and isntOtherStuff
bothFalse = not (foo or bar)
if isStuffNotOtherStuff or bothFalse
awesome sauce
else lame sauce
The main disadvantage of this method is its slowness. We will get better performance if we use and and or operators features and replace variables with functions:
C = A and B
If A is false operator and wouldnt call
C = A or B
If A is true operator or wouldnt call
The second way. Functions:
isStuff = -> foo is bar.data.stuff
isntOtherStuff = -> foo isnt bar.data.otherstuff
isStuffNotOtherStuff = -> do isStuff and do isntOtherStuff
bothFalse = -> not (foo or bar)
if do isStuffNotOtherStuff or do bothFalse
awesome sauce
else lame sauce
I am able to debug scala controller in play framework using netbeans IDE but the issue is I cannot set breakpoint when I am within a loop like:
val strs: List[String] = txt.split("\\s+").toList
for (str <- strs) {
if (str == "some")
.....
}
When I try to set breakpoint at line " if (str == " netbeans tells me: Broken breakpoint: it is not possible to stop on this line.
Most of my code is within loops, so I am stuck.
"For" expression is translated to "map":
http://www.artima.com/pins1ed/for-expressions-revisited.html#23.4
Regards