Fork me on GitHub
#beginners
<
2019-08-15
>
markgdawson05:08:54

I'm slightly confused by namespace, specifically ns-all. I put a dependency on my project.clj for lein, I would expect ns-all to show some namespace corresponding to the dependency, but it doesn't? Is ns-all (and dir) the correct way to know what namespace/functions are available to me? Many thanks.

markgdawson05:08:53

I think I mean all-ns actually! :)

ashu05:08:22

Hi, I am new to clojure, can someone suggest me link/blogs to learn how to write tests in clojure.

andy.fingerhut06:08:58

@markgdawson ns-all shows the namespaces that are currently loaded, usually via require, in your current session. It does not show what is available. For that the best way is to look at the documentation for the package for what it recommends require'ing, or if the source is available, examine that for the namespaces available.

markgdawson06:08:51

Thanks @andy.fingerhut, that's very useful. On a side note, are you able to enlighten me on what the :require line in project.clj actually is? Is this a label for the package on some central repository? Or does it have any relationship to the source code as it is on disk? I often that it's hard (or impossible?) to predict the likely namespaces from the :require line, which seems to be an odd inconsistency to me....

markgdawson06:08:39

Maybe it's just that I'm new to clojure and there's a lot to take in for a beginner compared to some other languages I've used! 🙂

andy.fingerhut06:08:21

I am not sure what you mean by the :require line in project.clj, as I don't recall seeing one that has that.

andy.fingerhut07:08:08

To find out what libraries exist and are useful, ask places like here for what kind of functionality you are looking for, or there are web pages with some lists of useful libraries. (I will find a link to one in a minute here)

andy.fingerhut07:08:52

To find out what namespaces are in a library, again, look at the README for the library to see if it recommends require'ing one (or several), and what functions or macros are intended to be useful to you.

andy.fingerhut07:08:18

https://www.clojure-toolbox.com is one list of Clojure libraries, by category

markgdawson07:08:48

Thanks @andy.fingerhut, it's great to have support as someone starting out. I looked into the source code, and found the ns headers and that worked. Quite simple when you know how 🙂

markgdawson07:08:09

Another silly question (sorry!). The brave clojure book describes namespaces as labelled shelves with functions in them. It seems a very simple mental model, does this mean that in theory two libraries (if we're not careful) could be unwittingly dumping functions into the same namespace? I've tested it and it seems (to me as a beginner) to behave just like that.

andy.fingerhut07:08:07

Yes, two libraries could easily include the same namespace names. People tend to try to avoid that by picking a short prefix that is unique, and naming other namespaces as suffixes of those.

andy.fingerhut07:08:20

Just as two Java, Python, Perl, etc. libraries could use the same names for packages/namespaces/whatever-the-language-calls-them, and that would not be useful to do.

markgdawson07:08:41

Sure, OK. Thanks again @andy.fingerhut 🙂

andy.fingerhut07:08:08

you are welcome

dharrigan12:08:46

Let's say I have an if expression that returns true. In the true part, I want to eval a series of functions that write into different tables in a db, one after the other. They don't eval to anything (well, I suppose, they eval to nil. Would a do form be sufficient, or is there a better "wrapper"?

herald13:08:11

This is the only way inside an if branch. Do note that there are many functions that create an implicit do, like when, let and defn. In those cases you can do side-effects without adding a do.

dharrigan15:08:44

thanks! 🙂

dharrigan12:08:09

something like this

dharrigan12:08:12

(if (some-truthy-evaluation)
  (do
    (write-into-table-1)
    (write-into-table-2)
    (write-into-table-3))
 (log/info "oh nooooos"))

manutter5112:08:25

Nope, nothing better. That’s one of the main things do is designed for.

manutter5112:08:29

The only caveat would be to think about whether, in context, it would make sense to pull those writes out into a update-tables function so you can do

(if (something?)
  (update-tables)
  (log/info "Whoops"))

manutter5112:08:15

Sometimes that makes for more readable/maintainable code, because the function name serves as a kind of “executable documentation” for what you’re doing.

chepprey12:08:26

(update-tables!) don't forget the "I'm mutating stuff" bang 🙂

manutter5112:08:32

Heh, I don’t actually follow that convention very often.

Alex Miller (Clojure team)12:08:02

and a side koan to ponder - why does using do in an if mean side effects must be involved?

chepprey12:08:06

Ok - noob here, I'll bite. Since do only returns the value of the last expression in the list, if the other expressions are pure functions with no side effects, there's little point to them being there. This would seem true regardless of if the do is in an if or not.

manutter5112:08:10

That’s what I’d say too.

Alex Miller (Clojure team)13:08:20

right, I just always think that's an interesting observation to come across, esp if you're not coming from fp

➕ 4
chepprey13:08:23

should be named do! 😉 (ok not really, side-effects are not necessarily mutation)

markgdawson14:08:20

Silly question....in what cases are side-effects not some kind of mutation?

chepprey14:08:03

not silly at all, I re-considered my statement several times after typing it

chepprey14:08:21

so purely on concept, and ignoring any terms + any implications that might come along with certain terms,

chepprey14:08:48

the "mutation" i was referring to was more about the style of "immutable values" that Clojure has. {:last "Doe" :first "Jon" :age 40}

chepprey14:08:07

That map can't be mutated in Clojure. You can stick the map into an atom, then swap! updated values into the atom. So that's the typical kind of "mutation" that can happen in Clojure, and that's the kind of mutation that the bang ! is used to warn against ------ at least this was my impression. Caveat: I'm a Clojure noob.

chepprey14:08:44

So yes, sticking some (prn ...) statements in a do technically causes side effects, and side effects by definition mutate something in the universe. But they don't mutate Clojure values.

markgdawson14:08:36

Yeah, I assumed that was the implication. I'm not arguing with what you said, just thought it was an interesting statement. Cavear: also a noob.

Crispin13:08:26

I read the exclamation mark as "do not use inside an STM transaction (like inside swap!)", not that it has side effects. If it has side effects, but is safe to use inside a swap! (like an idempotent operation) I leave the bang off.

✔️ 4
Crispin13:08:29

is there any docs on what the bang convention actually means?

bronsa13:08:30

swap! isn't a STM transaction

bronsa13:08:37

the bang convention isn't really a thing, most people use it to indicate side-effects but there's no strict rule

Crispin13:08:28

are atoms not considered part of the STM?

bronsa14:08:11

only agents and refs take part in STM transactions

Alex Miller (Clojure team)14:08:53

even agents are more "aware of" than "involved in" transactions, it's primarily refs

💯 4
Ashley Smith17:08:06

Hey! So I asked in re-frame but maybe its not typically a re-frame question. Does anyone have any idea on how to take the user to a different page without using href links? So I have an event that retrieves data from a server, and I want to send the user to the page the data corresponds with.

(rf/reg-event-fx
  :post-submission-success
  (fn [_ [_ response]]
    (let [postid (:new-post-id response)]
      (if (pos? postid)
        (do
          (println "Submitted post: " postid)
          ;; @TODO: Send user to /posts/<id>
          {:dispatch 
            [:new-notification
              [ "Post submission successful!"
                ""
                "is-success"
                "fa-file-check"]]})
        ...
I'm using re-frame-routing to handle changing the app's appearance when the URL changes, but now I want to force navigate the user when postid is valid, and take them to the post they just submitted. Any ideas? Using reagent and reframe 🙂

noisesmith17:08:41

a common thing is to use https://google.github.io/closure-library/api/goog.History.html to manage navigation and history

noisesmith17:08:55

if you are using cljs, all of goog is available by default

Ashley Smith17:08:08

yeah I've seen a lot of goog in my error messages

Ashley Smith17:08:18

I can imagine this means going forwards and backwards through your history

noisesmith17:08:34

and also sending the user to a new URI under the same parent

noisesmith17:08:49

(while also adding the item to your history etc.)

Ashley Smith17:08:52

is that new history

Ashley Smith17:08:07

as in, adding to your new history list and as a side effect taking you to the new item?

noisesmith17:08:27

I'm realizing we attached the history object to the page to capture navigation (so that the back / forward buttons would work) that wasn't what did the actual navigations

noisesmith17:08:14

@ashley correcting myself: the way you make the navigation happen is to mutate window.location.href

noisesmith17:08:46

if you initialize a goog.History object you can do other nice things so your app doesn't seem broken to the user

Ashley Smith17:08:14

ah I see! Well allow me to correct myself too, I'm using re-frame-routing but thats built on top of bidi, so I'm not looking to see if bidi offers anything similar to what you're talking abotu

noisesmith17:08:31

bidi watches window.location.href

Ashley Smith17:08:35

I was under the impression a router was about matching routes to pages and it wasn't able to control the URL bar itself

noisesmith17:08:36

changing it triggers bidi

Ashley Smith17:08:04

does bidi / any router change the URL bar? Is it the job of the router?

noisesmith17:08:17

no, the url bar makes bidi run

Ashley Smith17:08:18

I'm still kind of learning the roles of these packages and their alternatives, still learning the ecosystem

Ashley Smith17:08:34

okay, so what I'm looking to do is most likely not a function of bidi

noisesmith17:08:54

the big picture is that your code (or a user, by typing or pushing a button) can rewrite the href in the window bar, bidi is automatically called when that happens, it uses its router to pick some js to run (which is code you wrote, you can make it do whatever to reflect the new location)

noisesmith17:08:24

if only the part after an optional # is changed, this all happens without needing to load anything from the server, the html spec says that part of the URL is only for things local to the page view

Ashley Smith17:08:15

okay gotcha, so how would oyu go about mutating window.location.href, is it trivial or does it require another package?

noisesmith17:08:30

if anything before # changes, you can override it in hacky ways, but otherwise you are loading a new page from the server, and all js runs from your main again

noisesmith17:08:09

@ashley this is probably the best description of the API - it's doable without any other libs

noisesmith17:08:38

it's an object that's basically a string, you overwrite it and it makes the browser navigate

Ashley Smith17:08:38

okay, I'll do some searching as to how I actually do this

Ashley Smith17:08:49

I need to learn how to do this with clojurescript though 🙂

Ashley Smith17:08:20

im browsing /js to see if its in there somewhere

noisesmith17:08:23

the interop to adapt those examples should be simple

noisesmith17:08:13

if you haven't learned interop yet, it's a great time to start :D

try-not-to-cry 4
Ashley Smith17:08:50

yeah, I think you're correct

noisesmith17:08:14

interop only seems hard if you haven't used it IMHO, I'd make an exception for concrete inheritance in clj, but that doesn't apply in cljs world

noisesmith17:08:46

you can use simple classes that are nearly data (like Date) to get a feel for it

Ashley Smith18:08:37

@noisesmith this is very easy and I can't believe something so powerful has only just come to my knowledge, thank you!

noisesmith18:08:55

haha, glad it's working out

noisesmith18:08:26

it opens up a lot of possibility to be able to use all of js / js libs - though in some rare cases you really do want a proper wrapper

noisesmith18:08:38

in my experience people use wrappers more than it's really needed

Ashley Smith18:08:10

I guess the only thing I can ask, is that my app switches pages instantaniously when clicking href links, but because this function is changing the URL bar the webpage seems to reload. I imagine there's not much I can do about this but I guess I'd ask haha

noisesmith18:08:21

hmm... - if you adapt your app to use the "fragment" for all navigation (only changing past the optional #), then it doesn't need any reload, it just triggers your router

noisesmith18:08:39

but there could be multiple things going on, really depends on how you app works

Ashley Smith18:08:27

yeah - I'm not using jump links as I want my URL bar to look like a normal website. I think with reframe routing the term pushy-init allows for the changing of the URL bar without reloading

Ashley Smith18:08:57

to be honest its only a minor inconvenience in this case but it would be nice to have that consistent, no load time feel 🙂

noisesmith18:08:04

the one cljs app I really developed in anger we did everything via fragment routing, so I can't provide any more info personally

Ashley Smith18:08:41

(defn- pushy-init
  [routes]
  (fn [_]
    (let [history (pushy/pushy #(re-frame/dispatch [:router/set-route %])
                               #(bidi/match-route routes %))]

      (listen-for-navigation! history)

      (pushy/start! history)

      (re-frame/dispatch [:router/initialized]))))
I found this inside the sourcecode for @oconn’s re-frame routing if this helps?

Ashley Smith18:08:52

Yeah don't worry thank you for your help with everything else!

oconn18:08:49

@ashley https://github.com/oconn/re-frame-routing/blob/master/src/re_frame_routing/events.cljs#L69 I think this event will help you with what you’re looking to do (navigating from effects / events)

Ashley Smith18:08:12

I'm afraid I don't quite understand what the function does from the source code does, but do I just :dispatch [:router/nav-to "/post/whatever"]

oconn18:08:05

yep - or you can use the effect {:db ... :nav-to "/post/whatever"}

oconn18:08:09

both will work

Ashley Smith18:08:10

Thank you!! I wish I had gone through the source code earlier

Ashley Smith18:08:18

I was going through bidi but didn't realise you had a nice solution

Libor Danek20:08:39

SOLVED. command in tutorial is

boot serve -d target
but
boot serve -d target wait
must be used. --- Hello. I'm going through modern-cljs tutorial no. 2 https://github.com/magomimmo/modern-cljs/blob/master/doc/second-edition/tutorial-02.md Jetty stops right after it is started without my interference. Has anyone experienced this behavior? What am I doing wrong? I'm new to all, java, clojure and cljs so except solution, I'm also interested in how would one debug this.
$ boot serve -d target
2019-08-15 22:43:57.585:INFO::clojure-agent-send-off-pool-0: Logging initialized @6570ms
2019-08-15 22:43:57.622:INFO:oejs.Server:clojure-agent-send-off-pool-0: jetty-9.2.10.v20150310
2019-08-15 22:43:57.644:INFO:oejs.ServerConnector:clojure-agent-send-off-pool-0: Started ServerConnector@11f4bbdb{HTTP/1.1}{0.0.0.0:3000}
2019-08-15 22:43:57.645:INFO:oejs.Server:clojure-agent-send-off-pool-0: Started @6630ms
Started Jetty on 
Stopping Jetty
2019-08-15 22:43:57.660:INFO:oejs.ServerConnector:main: Stopped ServerConnector@11f4bbdb{HTTP/1.1}{0.0.0.0:3000}
My build.boot follows. Please let me know which files / logs / cmd outputs could be helpful, so I can provide.
(set-env!
 :source-paths #{"src/cljs"}
 :resource-paths #{"html"}

 :dependencies '[[org.clojure/clojure "1.7.0"]
                 [adzerk/boot-cljs "1.7.228-2"]
                 [javax.xml.bind/jaxb-api "2.3.1"]
                 [pandeiro/boot-http "0.8.2"]
                 [org.clojure/tools.nrepl "0.2.12"]])

;; makes cljs task visible to the boot command
(require '[adzerk.boot-cljs :refer [cljs]]
         '[pandeiro.boot-http :refer [serve]])
$ boot -V
#
#Thu Aug 15 22:51:06 CEST 2019
BOOT_VERSION=2.8.3
BOOT_CLOJURE_VERSION=1.7.0
BOOT_CLOJURE_NAME=org.clojure/clojure
$ java --version
openjdk 12.0.2 2019-07-16
OpenJDK Runtime Environment (build 12.0.2+10)
OpenJDK 64-Bit Server VM (build 12.0.2+10, mixed mode)
edit: format