Fork me on GitHub
#beginners
<
2018-03-19
>
runswithd6s01:03:57

has anyone tried to stand up the re-frame/example/simple application?

runswithd6s01:03:57

I seem to be running into "Figwheel Server: Resource not found". Whereas the todomvc example works fine

runswithd6s01:03:25

nevermind... I had to go to a specific HTML file to load the app. tl;dr: RTFM

Michael Fiano02:03:03

What is the correct way to pull in a new dependency using only the REPL instead of adding it to my project.clj and restarting lein repl?

Dormo02:03:14

I don't think you can.

seancorfield02:03:35

@mfiano You might consider switching to Boot since it supports that out of the box.

seancorfield02:03:26

You can just say (merge-env! :dependencies '[[my-new/library "RELEASE"]]) and it will find and load the latest version of that library into your running REPL.

Michael Fiano02:03:13

Interesting. I tried boot but I didn't really understand how to use it

Michael Fiano02:03:43

To be honest, I'm using Emacs/CIDER integration

seancorfield02:03:29

Happy to help you with that -- we switched from Leiningen to Boot at work two years ago and we've been really happy. You can use Emacs/CIDER with Boot just fine.

Michael Fiano02:03:51

What changes do I have to make? I actually have boot in my $PATH already

seancorfield02:03:35

It'll depend on what's in your project.clj file but you basically just convert it to build.boot and that's it.

Michael Fiano02:03:13

How does CIDER know to use lein or boot when I type ,' to start a REPL?

seancorfield02:03:30

(set-env! :dependencies '[[...]]) is probably all you'll need if you're working with a simple project.clj. CIDER looks for both project.clj and build.boot

seancorfield02:03:55

So if it sees project.clj, it'll use lein. If it doesn't, but it sees build.boot, it'll use boot.

Michael Fiano02:03:07

Ok so lein has precedence.

seancorfield02:03:23

Yeah, I believe so. I haven't used CIDER for years tho'.

seancorfield02:03:50

In Atom/ProtoREPL, you can configure which has precedence. You probably can in CIDER too.

Michael Fiano02:03:38

I just read "brave and true" and "joy of" this week, and this is actually the very first bit of programming I'm doing, so I really don't know what is preferred to use. The lein vs boot thing was very confusing to me about which to use, but I'd like to get a nice setup going if boot is what is preferred

Michael Fiano02:03:01

very first bit of Clojure programming that is...I am an old Common Lisp user πŸ™‚

seancorfield02:03:59

Leiningen has been around longer but I think Boot is a better build tool these days.

Michael Fiano02:03:43

One thing I didn't like about lein was the chore it is to make a custom template.

seancorfield02:03:01

I wrote a bunch of blog posts about our move from lein to boot in 2016 http://seancorfield.github.io/blog/archives/

seancorfield02:03:20

Boot relies on the same template machinery so you won't find it any easier, sorry. Boot new can run all of the Leiningen templates, as well as any new Boot templates, but the machinery is pretty much the same.

Michael Fiano02:03:29

I read some of your blog posts before it seems. How do I go about getting rid of (-main) as you mentioned is possible?

seancorfield02:03:36

Not sure what you mean. We have -main, we just don't use AOT.

Michael Fiano02:03:21

Oh I was just referring to this sentence which might have been read a bit out of context: There’s no need to create a -main function – the Boot tasks can call directly into our code.

seancorfield02:03:22

Oh, right. Yeah, for a lot of "task-based" stuff we just write a deftask in build.boot that does a require of the namespace in our code and then resolve the function we want to call, and then calls it.

Michael Fiano02:03:39

Ok, so if I were to switch to Boot, I am not sure how to translate the :main and :profiles lines that lein generated for me. I'm still very new and don't even know what they do, but I assume :main is setting the starting package for CIDER to automatically switch to when it starts a REPL.

seancorfield02:03:44

For example

(deftask sql-migration
  "Run SQL migrations to bring database up to desired level."
  []
  (comp (migration-context)
        (with-pass-thru fs
          (require '[worldsingles.data.migration :as dm])
          ((resolve 'dm/sql-migration)))))

seancorfield02:03:55

:main specifies the namespace that contains -main for when you run your program. I didn't know CIDER paid any attention to that.

seancorfield02:03:28

:profiles generally would become tasks in your build.boot file that create the environment that the profiles contain.

seancorfield02:03:44

How complex is your project.clj file?

Michael Fiano02:03:23

I literally just started. It looks like:

clojure
(defproject mfiano/crawler "0.1.0-SNAPSHOT"
  :description "TODO"
  :url "TODO"
  :license {:name "MIT"
            :url ""}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [clojure.java-time "0.3.1"]]
  :main ^:skip-aot mfiano.crawler.core
  :profiles {:uberjar {:aot :all}})

seancorfield02:03:28

OK, so your :profiles doesn't do much.

seancorfield02:03:17

Tell you what, in a separate folder, run boot -d boot/new new -t app -n mfiano/crawler and look at the build.boot file it creates.

Michael Fiano02:03:09

Ok looking at it.

seancorfield02:03:17

You should just be able to copy that into your current project, then edit the :dependencies line to specify Clojure 1.8.0 (instead of the latest RELEASE) and add [clojure.java-time "0.3.1"] and you'll be all set.

Michael Fiano02:03:42

I'll try that...

seancorfield02:03:49

boot build will create the uberjar for you, boot run will run the program. If you rename your project.clj file, CIDER jack-in should "just work".

Michael Fiano02:03:09

It seems to take less time to start a REPL anyway

seancorfield02:03:09

Oh, just noticed, it'll assume your main namespace is mfiano.crawler -- you'll need to update that to mfiano.crawler.core

seancorfield02:03:22

Yes, Boot only runs one JVM. Lein runs two.

Michael Fiano02:03:49

One sec...let me edit my deps and see if this works

seancorfield02:03:00

(and the downside of that is it uses whichever version of Clojure is specified in ~/.boot/boot.properties by default, not what's in build.boot)

seancorfield02:03:07

(which is a bit confusing)

Michael Fiano02:03:12

Oh let me see what I have

Michael Fiano02:03:11

Ah, boot doesn't have the package magic

seancorfield02:03:47

"package magic"?

Michael Fiano02:03:56

CIDER starts a REPL in the boot.user ns, whereas with lein it started in the mfiano.crawler.core ns

seancorfield03:03:18

Ah, ok. I never worry about that since I never type into the REPL anyway.

Michael Fiano03:03:28

Sorry, namespace, not package. CL terminology habits

seancorfield03:03:37

I just eval forms from the editor -- and that switches the REPL to the right ns anyway.

Michael Fiano03:03:30

I eval'd a form and it stays in boot.user

seancorfield03:03:24

Hmm, ProtoREPL switches the REPL to the ns of the file you're eval'ing forms from -- I assumed CIDER would do the same.

Michael Fiano03:03:43

I think CIDER paid attention to the :main section to autostart in a ns

seancorfield03:03:06

I'm sure there's a CIDER hot key to switch ns

Michael Fiano03:03:36

But, is there an equivalent boot thing like :main. Maybe it'll do the same thing?

seancorfield03:03:54

Don't know. You'd have to read the CIDER docs for that.

Michael Fiano03:03:51

Oh, ok. I was asking what the build.boot equivalent of project.clj's :main section would be.

seancorfield03:03:52

Hmm, I bet lein repl switches to the :main ns? So maybe you could do it via the Boot repl task. Let me check.

seancorfield03:03:55

Well, you can use boot repl -n my.ns but that doesn't help you with CIDER.

Michael Fiano03:03:19

Yeah lein repl starts me in my own ns too

seancorfield03:03:27

OK, so maybe it's not CIDER behavior causing that.

Michael Fiano03:03:53

Nope appears not. I'll read the boot docs. Thanks for all your help.

seancorfield03:03:32

Edit your build.boot and add this

(ns-unmap *ns* 'repl)
(deftask repl
  []
  (boot.task.built-in/repl :init-ns 'mfiano.crawler.core))

seancorfield03:03:56

(took me a while to make it work!)

Michael Fiano03:03:12

Right, but CIDER is not going to call that task I don't think?

seancorfield03:03:26

Yeah, it should. That's how it starts a REPL.

Michael Fiano03:03:34

Oh, nice. I'll try that

seancorfield03:03:56

It may try to pass some options tho'... not sure... so that may need to be updated with those too...

seancorfield03:03:16

It probably uses -s to start a server... let me check the docs...

Michael Fiano03:03:34

That errors when I call cider-jack-in

seancorfield03:03:14

Yeah, pretty sure it needs the -s option. Try this:

(ns-unmap *ns* 'repl)
(deftask repl
  [s server bool "Start REPL server only"]
  (boot.task.built-in/repl :init-ns 'mfiano.crawler :server server))

seancorfield03:03:07

If CIDER passes extra options, those would need to be added too. Oh wait, I'm being an idiot here! It would be simpler just to set a default task option for repl! Hang on...

seancorfield03:03:54

Yeah, that was all way too complex! Sorry. in the task-options! call, just add this:

repl {:init-ns 'mfiano.crawler.core}
(I added it just above the jar defaults).

seancorfield03:03:14

(and remove that ns-unmap call and the deftask repl)

Michael Fiano03:03:59

Great it works!

seancorfield03:03:11

So that (`task-options!` for repl) is the Boot equivalent of lein repl reusing the :main setting.

seancorfield03:03:39

(but in Boot, the initial REPL namespace and the AOT main ns are configured separately)

seancorfield03:03:50

Sorry for the wild goose chase there!

Michael Fiano03:03:54

That's ok. I appreciate this, thanks. This is going to save me a lot of time, as I develop on a really modern machine and on an under-powered netbook on the go, and this decrease in startup time due to 1 JVM instance is going to be very nice.

Michael Fiano03:03:21

I have to wait about a minute for lein on my netbook...it's been horrible.

Michael Fiano03:03:08

@seancorfield I do get some warnings on the command line with boot.

Michael Fiano03:03:24

A few of the following. Are these harmless? Classpath conflict: org.clojure/clojure version 1.8.0 already loaded, NOT loading version 1.10.0-alpha4

seancorfield04:03:49

Sorry, I was off feeding the cats. That warning says that the version Boot originally loaded was 1.8.0 (from your ~/.boot/boot.properties file I expect) but something in you build.boot is trying to load 1.10.0-alpha4 (probably as "RELEASE").

seancorfield04:03:25

So it's harmless but important if you actually want to be using a version other than 1.8.0 @mfiano

seancorfield04:03:14

I generally work with the latest release of Clojure myself. I have

BOOT_CLOJURE_VERSION=1.10.0-alpha4
in my boot.properties file.

seancorfield04:03:57

(and then I just specify "RELEASE" in most of the build.boot files for org.clojure/clojure)

seancorfield04:03:00

If you want Boot to use a specific version, you can set the environment variable for the the command:

BOOT_CLOJURE_VERSION=1.9.0 boot repl
if you wanted 1.9.0 (and you're on Mac or Linux -- no idea how to specify an env var for a single command in Windows)

Michael Fiano04:03:08

@seancorfield Thank you. Is it possible to sync the build file and properties to use the same Clojure version?

seancorfield05:03:41

@mfiano Good question... Let me try something...

seancorfield05:03:16

So, yes, since build.boot is "just" Clojure code, you can use (clojure-version) to get the current Clojure version as a string in the Build file. You just need to be a bit more careful about what is quoted in the expression

(set-env! :resource-paths #{"resources" "src"}
          :source-paths   #{"test"}
          :dependencies   [['org.clojure/clojure (clojure-version)]
                           ['adzerk/boot-test "RELEASE" :scope "test"]])

Michael Fiano05:03:08

That makes sense

seancorfield05:03:32

I moved the ' from outside the vector-of-vectors to the inside, quoting just the symbols that I don't want evaluated.

Michael Fiano05:03:59

could probably just backtick it and use [email protected] for the version?

Michael Fiano05:03:51

I might not have that reader macro correct for unquote...haven't actually done that in this lisp yet πŸ™‚

seancorfield05:03:52

We actually build our dependency vectors dynamically at work. Our build.boot reads a version.properties file to get the "pinned" versions of libraries we use and then we process all the dependency vectors to replace what's in the build.boot file with the actual versions we want to use.

seancorfield05:03:27

I wouldn't use backtick or ~ here -- that's overkill. You just want to quote the symbol names to prevent evaluation.

Michael Fiano05:03:11

I mean instead of adding a quote on the first element of every dependency's vector, couldn't you just quote the whole outer vector and unquote the one expression you need to eval?

seancorfield05:03:49

If you still wanted to quote everything except the Clojure part you could do

(conj '[[adzerk/boot-test "RELEASE" :scope "test"]
        [other deps ... ]]
       ['org.clojure/clojure (clojure-version)])

seancorfield05:03:58

Since it's "just code".

seancorfield05:03:10

> I mean instead of adding a quote on the first element of every dependency's vector, couldn't you just quote the whole outer vector and unquote the one expression you need to eval? (edited) Ah, gotcha. Yes, you could have

`[[org.clojure/clojure ~(clojure-version)]
 [adzerk/boot-test "RELEASE" :scope "test"]]

Michael Fiano05:03:55

Oh I just saw you typed the same thing.

Michael Fiano05:03:59

Thanks πŸ™‚

seancorfield05:03:23

I think we'll see a shift toward using deps.edn and clj in the near future.

seancorfield05:03:55

And either standalone uberjar build tools or add-ons for Lein/Boot (I maintain boot-tools-deps for that purpose).

Michael Fiano05:03:31

I am liking boot better so far though

seancorfield05:03:42

I've already converted the Contrib projects I maintain from using Leiningen (as a dev/test convenience) to just using clj and Cognitect's test-runner.

seancorfield05:03:46

Running tests is as simple as clj -A:test:runner:1.8 or clj -A:test:runner:master to test against Clojure 1.8.0 or against master-SNAPSHOT

xtreak2909:03:05

How does conj without an input collection work in a transducer here : (transduce (comp (map inc)) conj (range 5)) ; [1 2 3 4 5]

xtreak2910:03:39

Thanks. Never knew this and just looked at the source.

Umar Daraz12:03:46

Hi, I m super new to clojurescript and having trouble using semantic ui components in reagent hiccup syntax I m able to use semantic ui components where html tags are expected by using πŸ˜† But when I try to pass a semantic component to another semantic component as props (in code it is :trigger props), I m getting undefined error in console. I have spent like hours in this problem, but not able to find solution. thanks for the help.

roosta13:03:35

What semantic lib are you using? I've only used soda-ash, which uses react semantic ui, but in cases where a semantic component expects another component you'd have to pass the raw js function, and not an adapted reagent component

roosta13:03:34

something like this:

(:require [reagent.interop :refer [$]])
...
[:> modal {:trigger ($ js/semanticUIReact :Button)}

roosta13:03:39

yeah, ok then my approach should possibly work

Umar Daraz13:03:11

and how can I pass props to :Button component, like callback etc.

Umar Daraz13:03:02

haven't heard about soda-ash before, I will give it a try

roosta13:03:44

ah, I'm not sure. Maybe its possible to reactify a regular reagent component to pass props. Or some simpler approach. I haven't actually had to pass props to such a component before

roosta13:03:50

Soda-ash is a nice wrapper. I've seen several people asking questions about semantic in #reagent , maybe worth asking there too

Umar Daraz13:03:19

Sure I will use that channel, It is my first time with slack and clojure community. So it will take some getting used to But my first interaction with clojure community is awesome You people are super helpful Thanks buddy

mikerod18:03:55

I’d definitely say you’ll get good answers for reagent sorts of questions like this in the #reagent channel too

roosta13:03:38

np πŸ™‚

roosta13:03:39

@umardaraz4747 Seems there is an example on the soda-ash readme that does what you're trying to do

[sa/Modal {:trigger (reagent/as-element [sa/Button "Show Modal"])}]
I'm sure the same approach would work outside soda-ash too

Umar Daraz15:03:11

@roosta I tried the solution and it worked like a charm πŸ™‚

jonathan15:03:23

Can someone tell me what's wrong with my usage of tree-seq?

(defn get-children-seq
  [node]
  (tree-seq
    (fn has-children? [n] (pos-int? (count (:children n))))
    (:children node)
    node))
(get-children-seq
  {:something "A"
   :children [{
               :something "B"
               :children []}]})
IllegalArgumentException Key must be integer  clojure.lang.APersistentVector.invoke (APersistentVector.java:294)

sundarj15:03:56

@jonjanisch the second arg to tree-seq has to be a function. (try just :children)

jonathan15:03:12

ah, i see thanks

jonathan15:03:50

yeah that worked. I guess tree-seq isn't spec'ed πŸ˜„

sundarj15:03:41

the docstring does say it's a function though:

=> (doc tree-seq)
-------------------------
clojure.core/tree-seq
([branch? children root])
  Returns a lazy sequence of the nodes in a tree, via a depth-first walk.
   branch? must be a fn of one arg that returns true if passed a node
   that can have children (but may not).  children must be a fn of one
   arg that returns a sequence of the children. Will only be called on
   nodes for which branch? returns true. Root is the root node of the
  tree.
nil

jonathan15:03:10

yeah, it's my fault. I was looking at an example on stackoverflow and didn't notice the second arg was an anonymous function (using #)

jonathan15:03:45

should have looked at the docstring. The error message had me scratching my head a bit πŸ™‚

sundarj15:03:04

ah yeah, that anonymous fn syntax can be easy to miss πŸ˜›

sundarj15:03:37

as for the error, it sure is rather obtuse; though for future reference, you get that error when calling a vector as a function with a non-integer argument

sundarj15:03:24

=> ([1] 0)
1
=> ([1] :x)

java.lang.IllegalArgumentException: Key must be integer

jonathan17:03:42

i see, thanks!

sundarj15:03:24

yeah most of the core verbs aren't specced (yet)

skukade15:03:35

how do i define my own predicate in clojure?

skukade15:03:25

got it.. with "?" πŸ™‚

sundarj15:03:33

the '?' is simply a naming convention, it has no meaning in the language itself

manutter5116:03:58

"predicate" is just a name for a function that returns a boolean (true or false) result

manutter5116:03:30

or in many cases non-nil (for "true") or nil (for "false")

manutter5116:03:56

so you can just define your own predicate that returns a true/false (non-nil/nil) result

seancorfield16:03:19

@manutter51 If a function ends in ?, it's considered more idiomatic for it to specifically return either true or false /cc @saurabhkukade

seancorfield16:03:52

Lots of functions can be used as predicates without being purely Boolean-returning tho'.

manutter5116:03:03

@seancorfield That's right, I remember that now that you mention it. Thanks!

oVerde17:03:47

Someone here had any experience with the Telegram client API ?

oVerde17:03:02

(not the bot one)

vex18:03:00

how to get everything with function get-in. usually it gets some part of what is required, but I want to get everything with this function

ghadi19:03:52

can you give an example of what you have and would like to extract @poiga? get-in pulls data from one path down a datastructure

noisesmith19:03:05

or doesn't change the input, you can use merge to replicate what or does though

noisesmith19:03:47

(merge {:carved false :features [] :distance -1} cell) - bind that in a let block shadowing the value of cell

noisesmith19:03:39

you can also move the keys destructure into the let block to have that part work correctly (after the merge)

noisesmith19:03:12

putting the keys destructure on the same line as the merge is valid, but just adds noise - it doesn't make faster code or anything

vex19:03:59

@ghadi I have re-frame subscription which uses get-in . But I just want to get full db.

Michael Fiano20:03:27

Makes sense. Thanks

ackerleytng23:03:57

I have

(defmacro domain
  [name & body]
  `{:attrs {:name (str '~name)}})
, which allows me to do (domain foo) to get {:attrs {:name "foo"}}. How should I write the macro such that when i do (domain (str 11)), I get {:attrs {:name "11"}} instead of {:attrs {:name "(str 11)"}}? Or is there no way to do that without a conditional in the macro?

sundarj23:03:14

@ackerleytng in one case you're passing a symbol, and wanting it to be coerced to a string. in the other you're passing a list, and wanting it to be evaluated. how do you envision this happening without a conditional?

sundarj23:03:20

your logic is conditional, therefore you need a conditional πŸ˜›

ackerleytng23:03:19

i thought if the macro could be written such that evaluation is done before the macro is applied, sort of like functions

ackerleytng23:03:55

if i had (defn foo [bar] (+ bar 1)), and i called it with (foo (+ 2 3))

ackerleytng23:03:08

clojure will actually do (foo 5), right?

sundarj23:03:30

macros are run and expanded at compile time, so the only thing they have available to them is the unevaluated data you pass to it. to have something be evaluated, you have to return it from the macro

ackerleytng23:03:45

but i can manipulate the data passed in to the macro?

ackerleytng23:03:41

like

(defmacro domain
  [name & body]
  `{:attrs {:name (str (first ~name))}})

(domain [1 2])

mfikes23:03:37

You can also call eval during macroexpansion

ackerleytng23:03:52

oh but

(macroexpand-1 '(domain [1 2]))
; {:attrs {:name (clojure.core/str (clojure.core/first [1 2]))}}

sundarj23:03:31

yeah, correct. but consider the following:

(def x 2)
(foo x)
if foo is a function, x will be evaluated and foo will be passed 2. however if foo is a macro, foo will simply be passed the raw symbol x

ackerleytng23:03:30

does this mean that the process of macroexpansion only "pastes" [1 2] where ~name was?

sundarj23:03:21

creating data from a literal like a, [1 2], or {:a 2} also happens at compile time, which is why macros can 'see' them

sundarj23:03:38

boot.user=> {(inc 1) 2 (inc 1) 2}

boot.user=>      java.lang.IllegalArgumentException: Duplicate key: (inc 1)
clojure.lang.LispReader$ReaderException: java.lang.IllegalArgumentException: Duplicate key: (inc 1)

sundarj23:03:08

see the (inc 1) is treated as the literal list

ackerleytng23:03:41

@mfikes thanks, how would i do that?

mfikes23:03:31

@ackerleytng You can execute any code you'd like in order to produce the form you want to return. Here is one that involves calling eval:

(defmacro domain
  [name & body]
  (let [x (str (eval name))]
    `{:attrs {:name ~x}}))

sundarj23:03:49

~x in a macro evaluates the x at compile time

ackerleytng23:03:50

this doesn't seem right, because if x were evaluated at compile time, macroexpand would have thrown that error already?

ackerleytng23:03:57

so since it's only thrown when (foo a) is run, then x is sort of just pasted in, but the result, a, is evaluated at runtime. did I get it right?

sundarj23:03:09

look at the let. it's evaluated (it's already 2 after expansion). the same applies to the x. it's just that it's evaluated to the plain symbol a, since that's what x is

sundarj23:03:59

like so:

boot.user=> (def y 'x)
#'boot.user/y
boot.user=> `~y
x

sundarj23:03:39

and then at runtime (after expansion):

boot.user=> (eval `~y)

             java.lang.RuntimeException: Unable to resolve symbol: x in this context
clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to resolve symbol: x in this context, compiling:(null:1:1)

sundarj23:03:52

boot.user=> (defmacro foo [x] (prn (type x)))
#'boot.user/foo                              
boot.user=> (foo a)
clojure.lang.Symbol
nil 

ackerleytng23:03:34

so where does the evaluation stop?

ackerleytng23:03:50

i mean, usually a symbol will be evaluated to find the value

ackerleytng23:03:55

but in a macro that doesn't happen?

ackerleytng23:03:14

as in, in the let, it expands x until it gets 2

sundarj23:03:18

it does, it's just that a macros arguments aren't evaluated

sundarj23:03:17

that's how macros like when and if-not work πŸ™‚

ackerleytng23:03:48

i will think about it again after work

sundarj23:03:08

boot.user=> (defmacro foo [x] `[~x ~(let [x 2] x)])
#'boot.user/foo
boot.user=> (macroexpand '(foo a))
[a 2]
boot.user=> (foo a)

             java.lang.RuntimeException: Unable to resolve symbol: a in this context
clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(null:1:1)

mfikes23:03:55

It is a fairly common pattern to do some processing on the forms passed prior to generating the form to pass back to the compiler.

mfikes23:03:24

Look at the source for let as an example of that pattern.

ackerleytng23:03:50

this doesn't seem right, because if x were evaluated at compile time, macroexpand would have thrown that error already?

ackerleytng23:03:57

so since it's only thrown when (foo a) is run, then x is sort of just pasted in, but the result, a, is evaluated at runtime. did I get it right?