Fork me on GitHub

You can't in Spec 1. In Spec 2, you can do that for unqualified keys but not qualified keys.


(and you are using unqualified keys -- :req-un -- so you'll be happy with Spec 2 in that regard.

๐Ÿ‘ 4
Isaac Ballone01:02:47

(-> {:a 20 :30}
=> 20

(-> {:a 20 :b 30}
=> 20
I thought this was a little weird... Is there a preference? Why do they produce the same result?


-> isn't a normal function, it's a macro that automatically adds parens if they are absent


I prefer to always use parens for consistency, but part of the point of -> is to reduce character count, so


user=> (source ->)
(defmacro ->
  "Threads the expr through the forms. Inserts x as the
  second item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  second item in second form, etc."
  {:added "1.0"}
  [x & forms]
  (loop [x x, forms forms]
    (if forms
      (let [form (first forms)
            threaded (if (seq? form)
                       (with-meta `(~(first form) ~x ~@(next form)) (meta form))
                       (list form x))]
        (recur threaded (next forms)))


the magic happens in the (if (seq? form) ... (list form x))


so in the else, when it isn't in parens, it puts it in parens

Isaac Ballone01:02:29

Oh neat! So even if I don't put it in parens, it'll do it for me. I was thinking about leaving it in anyway for consistency, like you said. It just made me curious because I was surprised it was allowed


it's pretty common to see both, but it's definitely good style to pick one and stick to it, per project at least

Isaac Ballone01:02:42

Okay, thanks for your help!


Not sure if your above was just an example of using one of the threading macros, but in said example, you really dont need it:

({:a 20 :30} :a)
=> 20


gives you the same result

Isaac Ballone01:02:41

It was just an example ๐Ÿ™‚

๐Ÿ‘ 4

Parens are added only for single arg fns.


@hindol.adhya Not quite: the parens are added wherever there aren't parens. Consider (-> 42 cons) -- cons is a two-arg function but the parens are added and the result is an arity exception because it becomes (cons 42) which is invalid.


Similarly, (-> x :k) becomes (:k x) (which is valid)


Yes, I actually meant that. Don't know why I wrote it this way. It's technically incorrect.


Parens are added whenever they are omitted ๐Ÿ™‚


I mean every single thing without paren will be put in paren. For example, (-> [1 2 3] map square), map and square will be put in separate parens, which is wrong.


Although (map [1 2 3]) is legal (it returns a transducer) ๐Ÿ™‚


(it's not a useful transducer since [1 2 3] is not a reducing function)


I am just bad with examples I guess, ๐Ÿ˜‚. I keep forgetting vectors are also fns.


(-> [1 2 3] map square) => (square (map [1 2 3])) ๐Ÿ™ƒ


Question related to Datomic Ions, but I think the answers are much more about the basics so asking here. In Ions you pass a handler function to either Lambda or http entry points, and the rest of system management is abstracted away. In particular, I wasn't sure where we could hook into system startup (basically whatever the equivalent of -main is), and saw that the provided ion-starter solved these initial configuration-like steps with memoize on client but was basically hitting the database every call to check if a starter dataset was loaded. I'm looking to use larger system management libraries like component/mount/integrant but I'm not sure where to call component/start . What are best practices here, and what are some patterns I could use? Can I rely on the idempotency of start in these libraries and basically call it on every request? I was thinking of just closing over an atom to return a handler that does a simple in-memory check like so:

(defn run-once [handler]
  (let [started (atom false)]
    (fn [& args]
      (if @started
        (apply handler args)
          (println "system start functions called here")
          (reset! started true)
          (apply handler args))))))
Are there better/simpler solutions with delay or memoize? Am I missing something really basic?


Hi I'm very new with clojurescript I download a website template and put it in the resource directory and now after lein trampoline run -m figwheel.main -b dev -r I can see the template in the browser but the repl won't work anymore, however, the only change is in the resources I also don't get any warning or exception Do you have any idea about the possible reasons? (The figwheel logs stop just before print clojurescript version)

Aviv Kotek12:02:29

silly Q but is there any way to convert KEY to INT. :1 -> 1 instead of converting to string?


Itโ€™s a bit of a loaded footgun but:

(-> :1 name Integer/parseInt)

๐Ÿ‘ 4
Aviv Kotek13:02:12

Yea I want without the string convertion

AJ Jaro13:02:49

@USPQF75AS It looks like itโ€™s stored as a String based on this source below, which means probably that the idea from @ben is the best

Aviv Kotek13:02:26

Yea that's what I thought, k thanks


Hello. I have the following macro, which works

(defmacro make-fn [name args body]
  `(fn ~name ~args ~body))


I can execute this from the repl as follows


(make-fn add [x y] (+ x y))


However, if I invoke make-fn from a function, the spec fails while macroexpanding fn


user=> (defn fn-maker [name args body] (make-fn name args body))
Syntax error macroexpanding clojure.core/fn at (form-init14866211691412444997.clj:1:33).
args - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
args - failed: (or (nil? %) (sequential? %)) at: [:fn-tail :arity-n] spec: :clojure.core.specs.alpha/params+body


And this is while defining, not while calling.


It seems like fn, understandably, enforces its args to be a vector


Just trying to understand this a bit better. So the spec is invoked at defn time?

Alex Miller (Clojure team)14:02:20

it's validated during macroexpansion

Alex Miller (Clojure team)14:02:49

which is actually what it says there "syntax error macroexpanding clojure.core/fn"


Right. I am curious about fn being a macro. I know the same to be true of let as well. Both are listed as special forms which expand to fn* and let*

Alex Miller (Clojure team)14:02:44

you should probably treat that as implementation details


Someone I know is using instaparse to write a language and they are writing a bunch of macros which expands their language to runnable Clojure code

Alex Miller (Clojure team)14:02:36

you can turn off spec macro checking

Alex Miller (Clojure team)14:02:44

(but anyone would need to do that to use this)

Alex Miller (Clojure team)14:02:13

or you could tweak that macro definition to be a vector


Oh thatโ€™s a good idea

Alex Miller (Clojure team)14:02:40

(defmacro make-fn [name args body]
  `(fn ~name [~@args] ~@body))

Alex Miller (Clojure team)14:02:08

something like that (the body not being a splice quote was a bug), but depends a bit on what you're trying to do


No, this works. Thanks

Michael J Dorian21:02:39

Is spec a correctly sized hammer for this nail? All the simulated objects in my game have their state in a map, including function identifiers, and I need my code to yell at me if a new map is missing critical keys or if the functions it identifies choke on it's data when run. I could just do a series of checks, but if this is the correct use case for spec, I'll use that so I can get an understanding of what everyone's so excited about!

Alex Miller (Clojure team)21:02:57

seems like a possible match

Michael J Dorian21:02:51

Cool, I'll give it a go then simple_smile