I was checking documentation about do
in the cheat sheet and found that it executes all the function one -by-one, but clojure is smart enough to handle execution one by one right!, what is the purpose of do
then?
@popeyepwr Consider if
. It can only accept a single expression for "then" and a single expression for "else". What would you do if you wanted to print a message in both arms and also return a value?
yeah, I am able to connect the dots now, if we have if
and we need multiple statement to be executed then we use do
There's a few other places where something expect a single expression, but if you want to pass in more than one you can wrap it in a do
, but if
is one of the main one.
Hello! How do I write a spec definition to say ::lrow
must be larger or equal to ::row
?
Currently have (s/def ::lrow (s/and nat-int? #(<= % max-rows) #(<= % ::row)))
and
(s/def ::coord
(s/keys :req-un [::row ::col]
:opt-un [::sheet ::lrow ::lcol]))
you have to keep in mind, spec is to some degree not oriented around specing maps
when you define ::lrow
you are specing what ::lrow
should mean in every map everywhere in your program
you are not defining it in relation to other things it may be aggregated with, because the set of things it may be aggregated with is open
if you have a constraint based on the relationship between things in an aggregate (::cord) you spec it on the aggregate
I think I get what you mean, but is there an example of how I can do this? Understand the idea but not the syntax
(s/def ::coord
(s/and
(s/keys :req-un [::row ::col]
:opt-un [::sheet ::lrow ::lcol])
(<= ::row ::lrow)
(<= ::col ::lcol)))
Think you mean something like this? but not sure how i can refer it correctly::row and ::lrow are keywords
<= takes numbers
<= returns true or false, s/and takes specs / predicates
You can use proper clojure functions in specs
Including anonymous functions. So you can have something like (s/and (s/keys ...) (fn [m] "do some operation on the map"))
Apologies, I'm on the phone. The point of the function in this case is to describe a property, not do any arbitrary operation, sorry if the above was confusing.
As a more simple example, you can definitely say (s/def ::int (s/and ::number integer?))
So is there a way to say that one must be greater than the other then? :o
Yes. Disregard specs for a moment.
If you had a map, how would you check whether the value of key :k1
is greater than the value of :k2
?
(when #(> :k1 :k2)
do-something))
?i have defined a variable (def abc {:a "b" :c "d" :e "f"})
why ({:keys ["a" "b" "c"]} abc)
returning nil
the :keys destructuring needs symbols, not strings. So try ({:keys [a b c]} abc)
#(> (:k1 %) (:k2 %))
% is your map
because that's what the function expects as input, it needs the whole map to check this property
So how would you rewrite the spec now?
that is assuming you want to destructure. Just calling it like that is creating a new map with the key :keys
and value [a b c]
associated with that key. Then that map is used to lookup if anything in that map is associated with abc
. To which the answer is no, since the only key is :keys
.
Hence you get nil.
({:keys [a c e]} abc ) - this gave unable to resolve symbol
#(<= (::row %) (::lrow %))
I see I see!
destructuring is make sence only in some context such as let
βs binding form and argument declaration for function
like this
(let [{:keys [a c e]} abc]
;; you can use a, c, and e here
)
Thanks π
yeah got it !!! thanks @d.wetzel86 @delaguardo
I was doing this exercise from the braveandtrue book, the aim is to create a macro that will create a number of function that get a specific key from a map eg. c-int is defined as (defn c-int [map] (:intelligence map)) etc.. and the macro should be used like this (create-attribute-fs c-int :intelligence c-str :strength c-dex :dexterity) my solution looks like this
(defmacro create-attribute-fs
([] nil)
([fn-name key & nks]
`(conj (create-attribute-fs ~@nks)
(defn ~fn-name [m#] (~key m#)))))
I was not sure it would work because I thought I needed a do
as a the first element of the list bit it does work, why?I figured that it's because the elements of the list created by conj
are evaluated
π I just did and finisch that one . Which chapter was that ? @antbbn
Chapter 8
aha, I see it
I did it something else
feel free to post your solution π
key is for you the function and nks the arguments
I could but then you would not learn anything
but I begin with :
(defmacro defattrs
[& assignments]
what can you use and have we learned to iterate trough a collection ? @antbbn
map?
yep
and I want to give you a hint I also get. Look at partition
yeah I would imagine that mapping over the partition works too, i was just curious about the need for a (do)
could maybe also work instead of a map. I do not know
@d.wetzel86 do you know if we need a do
?
in my solution where I use a map I do not use a do
@antbbn
yeah me neither
you will need a do
for that macro. Since you are effectively returning a piece of code from a macro, and you want to do multiple things in that piece of code (any number of defn
in this case), those need to be in a do
.
??? we did not use a do
but a map
We did splice unquote the map into a (do ...` in the end. π
@antbbn then I apolize for this error of saying I did not use a do
but my macro does define all the functions it's supposed to
that's why I was confused
could be, there are more ways to solve a problem
@antbbn what does your macro look like atm?
like here https://clojurians.slack.com/archives/C053AK3F9/p1610448385038400
okay, your macro works, but I don't know if you intended that behaviour. Let's look at a call like this:
(create-attribute-fs c-int :intelligence c-str :strength)
That macro call expands to the following code (cleared up for better readability)
(conj
(conj
nil
(defn c-str [m] (:strength m)))
(defn c-int [m] (:intelligence m)))
So it will evaluate the defn
forms, and conj the vars that they return into a sequence. Then that sequence of vars is returned.
(create-attribute-fs c-int :intelligence c-str :strength)
=> (#'user/c-int #'user/c-str)
A question I have is if the resulting functions work with your character map from Chapter 5?
So does (c-int character)
work?
yep
but returning the sequence of functions might not be the intended behaviour
It's not a bad thing either though, so you can just roll with it π
ehehe all right π
Edit: solved π
Hello, I have a question around repl set up in intellij with the cursive plugin.
I used the lein new app app_name
command, and can run the app from my terminal. I havenβt changed anything at all in the scaffolded app.
When I try to run the repl, I get
Error: Could not find or load main class clojure.main
Caused by: java.lang.ClassNotFoundException: clojure.main
and all the function names are highlighted as not resolved. Any idea what might be causing that?
Probably best asked in the #cursive channel.
What I always do when creating a new leiningen project is create it at the command line like you did, and then in IntelliJ, select File -> New -> Project From Existing Sources. That registers the project as a Leiningen project and loads all the dependencies, etc. If you didnβt do that, that might be why itβs giving you problems.
Do you have clojure in project.clj :dependencies? Ie.:
[org.clojure/clojure "1.10.1"]
short answer: yes
long answer - here is my project.clj
(defproject comparer "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "<http://example.com/FIXME>"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "<https://www.eclipse.org/legal/epl-2.0/>"}
:dependencies [[org.clojure/clojure "1.10.1"]]
:main ^:skip-aot comparer.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all
:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})
I will try that now!
actually, just looking at the file tab, there was an option to add it as leiningen project, and that did the trick!
thank you!
for the future, you can right click on project.clj
or deps.edn
files and import it
if I want to do web developement in clojure as soon as i finished the brave book, can I then better make a leiningen or a clojure-clj project ?
oke
My ideea was to start very small. A app with 3 routes and some json parsing and hiccup
and no problem to start this app very small. As I look at it Luminus seems to be overkill for this app
I personally just use clj and shadow-cljs. But getting familiar with leinigen is beneficial. Also for web dev have a look at: https://luminusweb.com/ And consider reading: https://www.amazon.com/Web-Development-Clojure-Build-Bulletproof/dp/1680500821
clj; back or front -end, or both?
Here's a small (server-side) web app that uses the Clojure CLI and deps.edn
: https://github.com/seancorfield/usermanager-example -- I would suggest making sure you understand how this works and that you can modify and enhance it yourself before trying to work with Luminus (or the Web Development book). Luminus is a lot of moving parts and uses a lot of different libraries. It's a huge step up from what you're currently working with in Brave&True.
When I evaluate a line referencing a function, after changes were made to that function, those changes aren't being reflected when evaluating the line. Evaluating that function and then evaluating the line referencing it shows the changes. Why is that?
can you be a bit more specific what "changes were made to that function" means?
Yeah, so say I have a function that adds 2
to a provided number, then I realize it should be adding 3
. I'll change that number to 3
inside of the function, evaluate a line referencing that function, and it'll still add 2
.
But if I evaluate the function and then evaluate the reference, that change will be reflected.
right. you need to reevaluate the function
until you reevaluate it with 3, the function exists but with the value of 2.
Cool, thank you! Just taking some getting used to on my part.
If it helps: Clojure is always compiled, even in the REPL, and compilation happens as you evaluate each top-level form. It's a good practice to get into to instinctively "eval top-level block" after every change you make to any function.
Funnily enough, I just gave a talk to the London Clojurians about this very topic... recording to be available soon!
Awesome, that does help! I'll keep an eye out for the recording.