Can someone help me understand why quote as a function is useful, why it exists, how often it's used?
Alright, so what is this:
"hello"
If you don't know programming, you'd tell me it's the word hello surrounded by double quotes.
If you know programming you'd tell me it's a String whose value is hello
But in actuality it's just a bunch of characters, they take on a meaning based on some contextual semantics.
In the context of English reading, it is the word hello surrounded by double quotes.
In the context of Clojure reading, it is a Unicode String whose value is hello. That's because characters surrounded by double quotes in Clojure mean that they represent a Unicode String.
From this point on, lets remain in the realm of Clojure reading, so with that in mind, what is this:
:hello
It is an unqualified Keyword whose name is hello. That's because characters starting with a colon mean that they represent a Keyword.
So what is this:
hello
This is a Symbol whose name is hello. That's because characters not prefixed or surrounded by anything special mean that they represent a Symbol.
What is this:
("hello" "John")
This is a List with two Strings as elements. You know this because anything wrapped between open/close parenthesis represent a List in the context of reading Clojure.
Now what is this:
(hello "John")
This is a List where the first element is a Symbol and the second a String.
Now you might ask me, but isn't hello
in this case a function?
Well, no it is not, because I'm the context of reading Clojure, as we saw, this is a list of two elements, the first one is read as a Symbol, and the second as a String.
But after you read source text in Clojure, you get back what you read. So after reading the text (hello "John"
you now have a List of Symbol and String, this is no longer text, you hold now in memory an instance of a List object whose first element is an instance of a Symbol object and second element is an instance of a String object.
Once you have an instance of a List, you can ask Clojure to evaluate this List as code. Now we are in the context of Clojure evaluation, and as you see, Clojure can evaluate a data-structure like a List, that's why people say in Clojure "code is data(structure)". Because code is what can be evaluated, and in Clojure that can be, among other things, a List data-structure.
So if I were to represent this List in text once again, I would write (in the Clojure language):
(hello "John")
Now, after reading this and getting the List of Symbol and String, if I call eval
on it, which Clojure will do automatically when given a source file to run, or when sending a piece of source to the REPL (where it will Read and then Eval), well what Clojure evaluation semantics will do is that they'll take the first element of any list and look it up to see if there is a function of that name and then replace it by that function, where the rest of the elements in the list will become the arguments to it.
So when evaluating the List:
(hello "John")
Clojure will actually execute the hello function, now let's pretend the hello function is this:
(defn hello [name] (println "Hello " name))
That will return nil
. So after evaluating our list, we get nil
back.
Okay, but now we have a problem, because we read a List, but as soon as Clojure evaluates it, it'll become a function (or macro) call. So what if we want the List itself?
Imagine we had:
(def sum [numbers]
(reduce + numbers))
And so I wanted to write:
(sum (1 2 3))
See the issue? When we read this, we see that we have a two element List where first element is a Symbol (sum) and second element another List. That nested List contains three Numbers.
But now when Clojure evaluates this List, it'll consider sum
a function, which is correct, and pass it as an argument the nested Lists, and prior to function executing, their arguments are also evaluated, so now we have a List being evaluated again, and Clojure will consider 1
to be a function and it'll call it with 2 and 3 as arguments, this will fail obviously.
The problem is that since Lists represent function or macro calls when evaluated, how do you evaluate something to get a List back? And not have it be treated as a function or macro?
That's where quote
comes into the picture. You tell the Clojure compiler, the following form, please skip evaluating it, treat it simply for what it was read as.
So now you can do:
(sum (quote (1 2 3)))
And Clojure will evaluate this where it'll consider sum
a function, but this time it'll see that (1 2 3)
is quoted, and so it won't evaluate it, it'll just take the List as-is and pass it to sum as the argument.
It's a little meta, because of the homoiconicity, it makes it so that the textual representation for a List is the same as that for a function call, which means that you need a way to tell the compiler when it should be treated as a List or when it should be treated as a function call, and quote let's you do that, by saying don't evaluate what's quoted, treat them as-is the same as they were read.Now because it's annoying to wrap the List in a List calling the quote special form, Clojure also has a reader macro character for it, and so everything that is prefixed by '
is read as if it was wrapped in a List calling quote.
The only useful example I've found so far is when you want to print an expression out, like for debugging or something like that
The single quote '
is just a macro reader that expands to quote
. And these are what make "code as data" possible. Manipulating source code via macros wouldn't be possible without quoting.
A lot of the usage of quoting is in macros where you're building and manipulating forms that are not going to be evaled "yet" (macroexpansion time).
https://www.braveclojure.com/writing-macros/
it would be possible - just very inconvenient
(defmacro tedious-infix
[& forms]
(cons (symbol "do")
(for [[x op y] forms]
(list op x y))))
user=> (tedious-infix (1 + 1))
2
(that's massively simplified in that it doesn't break down the x and y for subforms etc.)
I think what makes "code as data" is demonstrated well with (eval (list + 1 2))
And think about what (list + 1 2)
returns and how evaling a list can really do anything at all. if you're familiar with C# or Java, consider how weird it would be to eval(IEnumerable<Object>)
or the like. And then recognize that (list + 1 2)
is using just regular clojure functions and a regular clojure datastructure
(eval (list (quote let) (vector 'a 1) (list + (quote a) 2)))
ok so apparently atom
is not a special form, and the source code for it doesn't seem to use special forms directly, but I'm guessing it uses let
or var
somehow if you dig deep enough, is there a simple way to see this in a repl?
You should read this: https://clojure.org/reference/special_forms
I actually recommend you the read each section of the reference in order, it's pretty short, but it explains a lot of things really clearly.
@lukewallace1990 one thing you might be confused about is that clojure is not an interpreter - value storage is not implemented on top of let and var, it's done via java viirtual machine bytecode instructions
It does use the new
special form. An instance of the clojure.lang.Atom
class will be created in this case.
@lukewallace1990 Can you see it in the REPL? Yes, via the source
function:
user=> (source atom)
(defn atom
"Creates and returns an Atom with an initial value of x and zero or
more options (in any order):
:meta metadata-map
:validator validate-fn
If metadata-map is supplied, it will become the metadata on the
atom. validate-fn must be nil or a side-effect-free fn of one
argument, which will be passed the intended new state on any state
change. If the new state is unacceptable, the validate-fn should
return false or throw an exception."
{:added "1.0"
:static true}
([x] (new clojure.lang.Atom x))
([x & options] (setup-reference (atom x) options)))
nil
Take a look at https://cljdoc.org/d/com.github.seancorfield/honeysql/2.0.0-rc3/doc/getting-started where you can use a DSL that involves either keywords or symbols: some people like the symbol-based version because they don't have to type :
everywhere -- but you need '
so that the symbol names are treated as names and not looked up for their values.
I am trying to use the defroutes macro to convert this:
(defn home-routes-backup
(routes
(GET "/" _
(-> "public/index.html"
io/resource
io/input-stream
response
(assoc :headers {"Content-Type" "text/html; charset=utf-8"})))
(resources "/")))
To this:
(defroutes home-routes
(GET "/" [] (content-type (resource-response "index.html" {:root "public"}) "text/html"))
(resources "/"))
But i get a "java.lang.NullPointerException: Response map is nil" when i try to load up the localhost. Any ideas what could be the issue?? I tried to follow the tips from this thread https://stackoverflow.com/questions/7729628/serve-index-html-at-by-default-in-compojure
These are the imports for clarity sake:
[compojure.route :refer [resources]]
[ring.util.response :refer [redirect response file-response resource-response content-type]]
is it possible to use java source when compiling clojure jars to be used by java app? https://github.com/bigos/JavaApplication3/blob/master/src/clojure/responder/src/jac/responder.clj
ideally I would not only have the type of the object but also call it's methods
my build system is clj deps.edn
looks like it can not be done
https://stackoverflow.com/questions/63376869/compiling-java-sources-with-clojure-deps-edn
😞
Try adding the wrap-resource middleware https://ring-clojure.github.io/ring/ring.middleware.resource.html.
The second argument to it being “public” if your files are in the resources/public folder
the new tools.build library for deps.edn projects is nearly out and will support this
Started watching that last night. The intro is hilarious. And whoever did the captions explaining the missing music really nailed it
thx :)
it was better with the music though :)
i imagine. was glad to see it. looks amazing. thanks as always for your thoughtful work 🙂
is there a way to set environment variables? more specific: can I have clojure load environment variables from an .env file, at least for when I use the repl?
btw not being able to manage env vars at runtime is a portability issue, and via the right OS system calls it can be done, you just give up on the one very popular OS where this can't be done
also the 12 factor thing of providing config via environment makes stealing credentials very easy one linux (the OS almost everyone actually uses in prod) - just slurp /proc/$PID/environ
and you get the whole thing
or better yet, the shortcut /proc/self/environ
since the kernel knows your PID
Even if you change the env vars through the OS it won't reflect in Java, and System.getenv will return only what was there at startup, because Java caches the environment on startup I believe. I saw some ways to change the JVM env cache, but it's not an official API, so depending on your JVM and its version it might not work. I think other langs, when you execute setenv it doesn't actually set the environment vars of the parent, but simply changes the one of the current process, that bit I'm not sure why Java doesn't allow it.
it's exactly the "set the vars of the current process" bit that isn't portable though
if you need a child to have a different env, ProcessBuilder supports that, but that happens in a full new process not the current vm
maybe I should drop the environment variable idea and use config files instead... outpace/config looks promising for that purpose
Some ClojureScript
(defn get-prices-and-balances
[client return]
(let [prices-c (chan 1 (map #(assoc {} :prices %)))
balances-c (chan 1 (map #(assoc {} :balances %)))
combined-c (merge [prices-c balances-c])]
(b/get-all-prices client prices-c)
(b/get-balances client balances-c)
(go-loop [prices nil
balances nil]
(if (some nil? [prices balances])
(let [result (<! combined-c)]
(cond
(:prices result) (recur (:prices result) balances)
(:balances result) (recur prices (:balances result))))
(>! return
(assoc {} :prices prices :balances balances))))))
The question I have is. Is there another way to fire off 2 async calls (get data from 2 resources on the internet in this case) and manage the data returned. I know I can do better in this code with putting the values on a map, then taking them from the map and putting it in the recur and all the way at the end put it on a map again.
The thing is also, both the b/get-all-prices
and b/get-balances
functions internally return promises that I put on core.async chancels but now I'm starting to get the idea it would be better to just leave those as promises and use something like (<p! (.all js/Promise #js [b/get-all-prices b/get-balances]))
and I get the same effect (parallel calles).
I also made something with pipeline-async in the past but ended up with quite some code as well. I have the idea I'm missing something.Or use into instead of merge
Or don't combine the channels at all and loop around alt
what benefit do you get from all of this rather than
(defn ps-and-bs [client]
(go
(let [p (b/get-all-prices client)
b (b/get-balances client)]
{:prices (<! p-c )
:balances (<! b-c)})))
Return a channel rather than taking in a channel. you can pipe from one to the other though if you want. No adding things into maps and then taking out and then adding back into a mapThis is gold, I did miss that a go block returns a channel with the result, and yes I can just wait for both to resolve. This is exactly what I wanted. I just thought I needed all that other code to get this effect. Thanks a bunch!!
Yeah just fire the events before waiting on them achieves what you want
I read, I think in a book form Alex Miller it would be more flexible to accept channels instead of creating and returning them.
Thats why I did that. But then again, this is not some library but just my own code so yeah, this is easier anyway
Also check out https://github.com/juxt/aero. Also hand rolling config accessors is pretty straight forward. 1. Read from edn. 2. Store it in an atom. 3. Have functions accessors that when called get the values from the atom
Thanks for the reply as well @hiredman
thanks
You cannot set environment variables once a process has started (at all). You can set system properties in the JVM however, so properties are a bit more flexible than environment variables in that respect. But using an external config file is probably a better approach.
The environment variables as the name implies are managed by the "environment". So what you need to do is set them in your environment before launching your REPL, and then you can read them from the REPL.
If you use lein to start your REPL, I've used: https://github.com/athos/lein-with-env-vars before to have lein define some environment variables to set.
You can also have a look at: https://github.com/cdimascio/dotenv-java which attempts to replicate Ruby's dotenv. But like Sean said, JVM has immutable-ish env variables, so you can't set them once the program is running. The dotenv-java bypasses this by having you use its own API to get env variables. What I'd recommend though is to use System/getProperty to get the your .env config, and use dotenv-java with its systemProperties option. In general, Java libs that look for env variables have logic to fallback to properties as well (or vice versa).
Now, I personally strongly disagree with the 12 factor app idea of using env variables for config. In my experience, it creates a mess. I recommend going with configuration for your app. And what I do is that I have my configuration be defined per-environment. Now the only environment variable I have is the name of the environment I'm in. I then use that to load the right config. Imagine: default.edn johnny-desktop.edn bob-desktop.edn build-server.edn ci.edn beta.edn prod-eu.edn prod-na.edn And then an env variable: APP_ENVIRONMENT=beta
That way, configuration is still in code, you have it versioned controlled, can roll it back, know exactly what the config was at a point in time, etc
Here's a nice library that implements something similar to what I'm recommending: https://github.com/levand/immuconf