Atoms have Properties in Maclisp. How to do the same in Clojure? - clojure

Quoting from the (1975) Maclisp Reference Manual: "Each atomic symbol has associated with it a property-list, which can be retrieved with the plist function."
A Maclisp property-list was a list of 'indicator/value' pairs. In Maclisp,
(get x y)
gets x's y-property.
(putprop x 'banana y)
sets x's y-property to banana.
I am tasked with converting a lot of old Maclisp code into Clojure. I am new to Clojure but won't be for long as this project unfolds. Before I run off and write something myself, I'm wondering if Clojure already has a "property list" feature? Or something close?
And if not, what would the assembled Clojure gods have me do to implement such a feature? Remember, every atomic symbol in Maclisp can but does not have to have a property-list. Thank you.

clojure has metadata maps associated with variables / data values:
user> (def x [1 2 3])
#'user/x
user> (reset-meta! #'x {:my-data 1})
;;=> {:my-data 1}
notice that this metadata is associated with variable, not with variable bound data
user> (-> x var meta)
{:my-data 1}
user> (-> #'x meta) ;; short form
{:my-data 1}
user> (-> x meta)
nil
otherwise you can attach it to the value itself:
user> (def x ^{:some-data 101} [1 2 3])
#'user/x
user> (meta x)
{:some-data 101}
depending on how do you want to use it.

Related

macros in clojurescript - does not expand properly

I have created a macro in clojure
(ns macro-question.map)
(defmacro lookup [key] (list get (apply hash-map (range 1 5)) key))
in the clojure repl it works as expected
$ clj
Clojure 1.9.0
user=> (require 'macro-question.map)
nil
user=> (macro-question.map/lookup 1)
2
So I create a clojurescript module like this to try to use it:
(ns macro-question.core (:require-macros [macro-question.map]))
(defn lookup1 [] (macro-question.map/lookup 1))
and when I try that in the repl, I get this
$ clj -m cljs.main --repl-env node
ClojureScript 1.10.520
cljs.user=> (require 'macro-question.core)
Execution error (ExceptionInfo) at cljs.compiler/fn (compiler.cljc:304).
failed compiling constant: clojure.core$get#3df1a1ac; clojure.core$get is not a valid ClojureScript constant.
meanwhile back in clojure, there is a clue why this might be
user=> (macroexpand '(macro-question.map/lookup 1))
(#object[clojure.core$get 0x101639ae "clojure.core$get#101639ae"] {1 2, 3 4} 1)
I can create macros that start with '( instead of (list. However, I want the map to be expanded at build time.
What is going on? And what do I have to do to get something like the following:
user=> (macroexpand '(macro-question.map/lookup 1))
(get {1 2, 3 4} 1)
or what should I be doing to use this macro in clojurescript?
(list get (apply hash-map (range 1 5)) key)
Creates a list where the first position is the function object that the var get refers to. What you actually want to return is a list with the fully qualified symbol for get as the first position. Change your macro definition to
(defmacro lookup [key]
`(get ~(apply hash-map (range 1 5)) ~key))
(macroexpand '(lookup 1))
=> (clojure.core/get {1 2, 3 4} 1)
The reference guide for the reader is helpful here https://clojure.org/reference/reader
You can just put 'get!!
(ns macro-question.map)
(defmacro lookup [key] (list 'get (apply hash-map (range 1 5)) key))
(https://stackoverflow.com/posts/58985564 is a better answer about why it happens, and a much better way to do it. This just shows a simple solution that only solves the immediate issue, that I realised right after I had asked the question)

Does the Clojure special form `def` always create a new Var?

I know that in Clojure, namespaces map symbols to Vars. So we can map the symbol x to a Var (here in the default namespace) like this:
user=> (def x 5)
#'user/x
user=> #'x
#'user/x
user=> (type #'x)
clojure.lang.Var
user=> x
5
Now if I subsequently say this
user=> (def x 3)
#'user/x
Have I rebound the symbol x to a brand new Var or have I updated the value in the same Var I created above? How would I know?
I'm thinking it's the latter because I read the sentence "Only Java fields, Vars, Refs and Agents are mutable in Clojure." in the Clojure Reference page on Vars but I'm not sure that holds as a proof.
No, def does not always create a new Var. You can confirm this using identical?:
user=> (def x 5)
#'user/x
user=> (def v #'x)
#'user/v
user=> (def x 3)
#'user/x
user=> (identical? v #'x)
true
Elogent's answer is excellent and accepted, but I just found another way to prove that a new var is not created, which might be of use:
user=> (def x 10)
#'user/x
user=> (let [y #'x] (println #y) (def x 7) (println #y))
10
7
nil
If def did create a new var, we would have seen 10 printed twice.

defrecord argument filtrering

Let's say I've defined a record like this:
(defrecord MyRecord [x y z])
And I construct it like this:
(def test (map->MyRecord {:x "1" :y "2" :z "3" :w "ikk"}))
I can do like this:
(:w test) ; Returns "ikk"
Why is :w retained? I'm thinking that since I've created a record that takes x, y and z these are the only "keys" that should be present.
Is there a good way to exclude keys that are not present as arguments in the record declaration without using select-keys?
For example:
(defrecord MyRecord1 [x y z])
(defrecord MyRecord2 [x y w])
(defprotocol MyProtocol
(do-stuff [data]))
(extend-protocol MyProtocol
MyRecord1
(do-stuff [data]
(let [data (select-keys data [:x :y :z])] ; (S1)
...))
MyRecord2
(do-stuff [data]
(let [data (select-keys data [:x :y :w])] ; (S2)
...)))
I want to avoid doing select-keys (S1, S2) manually for each record when I use MyProtocol when records are constructed using map-> with additional data (that I don't care about).
Clojure records implement IPersistentMap (as well as java.util.Map for Java interop), and behaves like a normal map - this means that you can use them wherever and in the same way you would use maps. You can look at a record as a typed map - it's a map, but you can easily do dispatch using multimethods and protocols.
This makes it very easy to start representing your data using plain maps, and then advance to a record when you need the type.
As maps, records support additional keys, but they are treated differently. With your example, (.x test) works, but (.w test) does not, since only the predefined keys become fields in the implementing Java class.
To avoid extra keys, just make your own constructor:
(defn limiting-map->MyRecord
[m]
(map->MyRecord
(select-keys m [:x :y :z])))
It is a feature of defrecord. See: http://clojure.org/datatypes Section deftype and defrecord:
defrecord provides a complete implementation of a persistent map, including:
...
extensible fields (you can assoc keys not supplied with the defrecord definition)
Via Reflection you can see, that your x,y,z params are regular attributes of the object:
user=> (>pprint (.? (map->MyRecord {:w 4})))
(#[__extmap :: (user.MyRecord) | java.lang.Object]
;...
#[x :: (user.MyRecord) | java.lang.Object]
#[y :: (user.MyRecord) | java.lang.Object]
#[z :: (user.MyRecord) | java.lang.Object])
And the additional values are stored in that __extmap:
user=> (.-__extmap (map->MyRecord {:w 4}))
{:w 4}
This means, that there is nothing left for you other than watch out on the places you want to deal with your record as a Map, since new keys can be added at any time:
user=> (let [r (->MyRecord 1 2 3) r (assoc r :w 4)] (keys r))
(:x :y :z :w)
So if you find yourself repeating code like (select-keys myr [:x :y :z]) then extract that as a helper fn.
Adding things like your own c'tors is always a good idea (e.g. if you want to have 0 for missing keys instead of nil e.g.), but this only protects you from yourself and the users following your example.

Am I using atom? wrong or there is something else....?

Basically...
=> (atom? 5)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: atom? in this context, compiling:(NO_SOURCE_PATH:1)
=> (atom? /a)
RuntimeException Invalid token: /a clojure.lang.Util.runtimeException (Util.java:156)
RuntimeException Unmatched delimiter: ) clojure.lang.Util.runtimeException (Util.java:156)
=> (atom? "hello world")
CompilerException java.lang.RuntimeException: Unable to resolve symbol: atom? in this context, compiling:(NO_SOURCE_PATH:1)
So does anyone know what's happening??
I am using Eclipse Juno 4.2, the CounterClockwise plugin.
What's called an atom in Clojure is something completely different than what's called an atom in other Lisps. In classic Lisp an atom is a single value, defined as being not null or not a cons cell (pair):
(define (atom? x)
(not (or (pair? x)
(null? x ))))
In Clojure an atom is a concurrency reference type. Atoms in Clojure can be either single-valued or collections/sequences, where updating (mutable state change) is guaranteed to happen atomically.
In Clojure there's far more reference types than the cons list in Lisp, and there's all the Java interop collection types to be reckoned with. That makes it hard to define a check on single-values.
If you do want to, the simplest check is to see if something can be counted. Looking at (source counted), it references clojure.lang.RT/count and countFrom. There, several classes / interfaces are specified, which I included in the following function:
=> (defn single-valued?
[x]
(not (or (nil? x)
(.. x getClass isArray)
(some #(instance? % x) [clojure.lang.Counted
clojure.lang.IPersistentCollection
java.util.Collection
java.util.Map]))))
=> (map single-valued? [1 "foo" \a 'x true not nil])
(true true true true true true false)
=> (map single-valued? ['(1 2 3 4)
[1 2 3 4]
{:a 1 :b 2}
#{1 2 3 4}
(seq [1 2 3 4])
(seq {:a 1 :b 2})
(seq "foo")
(int-array [1 2 3 4])
(seq [])])
(false false false false false false false false false)
Since (seq []) evaluates to nil it's not considered single-valued. Of course, java objects with multiple fields, as well as Clojure deftypes / defrecords will register as such, even though they're composite objects.
I suspect you are confusing a clojure atom with an atom in something like scheme.
In scheme an atom is a fundamental unit.
In clojure an atom is one of clojure's reference types (like ref and var) that can be updated atomically.
This fits nicely with clojure's concurrency model.
e.g.
user> (def a (atom '(1 2 3)]); create an atom with value (1 2 3)
user> #a ; look up (deference) the atoms value
(1 2 3)
user> (swap! a (fn [v] (map inc v))) ; add 1 to each element, v is the
; old value of the atom. other threads will
; see the three values in a change atomically
user> #a
(2 3 4)
user> (reset! a '(5 10 15))
user> #a
(5 10 15)
atom? is not a function.
You could use
(def x (atom 5))
(instance? clojure.lang.Atom x)
You can create atom? function like this:
(defn atom? [x]
(not (coll? x))
)
The complement function returns the opposite of any predicate passed to it as argument, so you can make a atom? with it:
(defn atom?
[x]
((complement coll?) x))
(atom? []) ;=> false
(atom? ()) ;=> false
(atom? {}) ;=> false
(atom? 4) ;=> true

Clojure caret as a symbol?

So I thought it would be a nice idea to name a function that calculates the exponential ^, but it seems like the caret actually does something special, as the Clojure REPL generates an error when evaluating '^. Googling mostly gave me this, so I was wondering what the actualy use for the caret in Clojure is.
(Also, would it be possible after all to name a function ^?)
^ is "the meta character" it tells the reader to add the symbol starting with ^ as metadata to the next symbol (provided it is something that implements IMetas)
user=> (def x ^:IamMeta [1 2 3])
#'user/x
user=> x
[1 2 3]
user=> (meta x)
{:tag :IamMeta}
user=>
You can learn a lot about how clojure works under the hood by looking at the meta of things, for instance functions:
user=> (meta foo)
{:ns #<Namespace user>,
:name foo, :file "NO_SOURCE_PATH",
:line 5, :arglists ([s])}
this is very often used for type hints
(defn foo [^String s] (.charAt s 1))
it is generally a good idea to turn on reflection warnings (set! *warn-on-reflection* true) and then add type hints until the warnings go away. without these Clojure will look up the type of the function operands at run-time, which saves you the trouble of fussing with types though at a slight cost.
PS: My next favorite reader character is the "dispatch" character #, it is well worth learning about it next :)
PPS: this is different in clojure 1.2.x vs clojure 1.3.x
in Clojure 1.2.1 metadata does not compose when you use the meta-character:
user=> (def foo ^:foo ^:bar [1 2 3])
#'user/foo
user=> (meta foo)
{:tag :foo}
and in 1.3 it "does the right thing" and also keywords are options instead of "tags":
user=> (def foo ^:foo ^:bar [1 2 3])
#'user/foo
user=> (meta foo)
{:foo true, :bar true}
It seems to me that the answer to your question is, unfortunately, no. In Clojure, you cannot name a function ^.
I tried the following in the REPL:
user=> (println \^)
^
nil
This seems to imply that you can escape the carat (^) with a backslash. However, if I try to declare a function using \^ as a name then I get an error message:
user=> (defn \^ [n e] (cond (= e 0) 1 :else (* n (\^ n (- e 1)))))
IllegalArgumentException First argument to defn must be a symbol
clojure.core/defn (core.clj:277)
The same code works with a regular text name:
user=> (defn exp [n e] (cond (= e 0) 1 :else (* n (exp n (- e 1)))))
#'user/exp
user=> (exp 3 3)
27
I would be delighted if someone with better Clojure-fu than mine could prove me wrong! :)