Fork me on GitHub
#beginners
<
2018-03-06
>
duminda02:03:49

In a project using compojure-api, I need to expose a POST endpoint taking some unsigned long params:

(POST "/update" []
      :return Boolean
      :body-params
      [size-identifier :- Byte
       firmware-version :- String
       packet-size :- Long    ;Should be unsigned long
       arr :- Long  ;Should be unsigned long
How to do this?

seancorfield04:03:07

@duminda The JVM doesn't have unsigned long, only signed long.

seancorfield04:03:42

(but if you're careful with math you can simulate unsigned long)

awerner3204:03:19

is there ever a reason that changing maps to a defrecord would make the program slower? in clojurescript in particular

seancorfield05:03:09

@awerner32 Hard to say without more information (and I don't know enough about cljs to even hazard a guess -- maybe ask in #clojurescript if no-one comments here). I would say that there's no need to use records unless you want to dispatch on types (via protocols, for example). I hardly ever use records.

seancorfield05:03:03

(Interestingly, Alex Miller recently commented that although his Clojure Applied book leans heavily on records, he is using them much more sparingly now -- I think he credited clojure.spec for that change but I can't be sure)

awerner3205:03:03

interesting. I have pretty tight loops comparing point data in some other data container and I have a branch where I moved that all to records expecting it to help because of smarter field dispatch (?) but I find the record-based version to be almost 50% slower

awerner3205:03:40

with :optimizations :advanced

seancorfield05:03:48

The fastest way to access record field (in Clojure at least, may be the same in cljs) is just to directly use their name -- but that presupposes you're writing functions within the record that implement a protocol...

seancorfield05:03:28

(defrecord [a b c]
  SomeProtocol
  (some-fn [this x y z] (do-stuff a b x y)))

seancorfield05:03:59

If you're accessing the record from outside, I guess you're doing (.-a some-record)?

seancorfield05:03:22

(and hoping that's faster than (:a some-map)?)

stardiviner06:03:22

When I try to require clj-tagsoup with (require '[pl.danieljanus.tagsoup :as html]) I got error:

duminda06:03:27

Do you guys notice that lein run migrate is very slow?

$ time lein run migrate
...
real	0m43.549s
user	1m41.920s
sys	0m3.251s
Just a simple create table script. Startup is very slow.

seancorfield06:03:51

@stardiviner That means the syntax in that file is not valid -- I suspect tagsoup is bringing in an old clojure.data.xml version...

stardiviner06:03:36

@seancorfield Am I able to manually fix this locally?

seancorfield06:03:29

Yup, you can specify a more up-to-date clojure.data.xml in your project.clj file

seancorfield06:03:36

What version of tagsoup are you using?

seancorfield06:03:46

@stardiviner Looks like you should be able to solve that problem by adding [org.clojure/data.xml "0.0.8"] to your :dependencies in project.clj

seancorfield06:03:15

Unfortunately clj-tagsoup brings in a lot of old dependencies.

seancorfield06:03:52

@duminda I'm curious, if you run it again, is it faster the second time? (I wonder if part of the time spent was downloading dependencies)

duminda06:03:42

@seancorfield: I’ve ran multiple times now. Both migrate and rollback take 45+secs.

seancorfield06:03:16

Maybe report it to whichever project those come from?

duminda06:03:46

Ok, will do.

seancorfield06:03:53

(I've no idea which plugin you're using to get those tasks)

seancorfield06:03:35

It's worth bearing in mind that Leiningen starts two JVMs so using it for scripting tasks is going to carry quite an overhead.

seancorfield06:03:47

@stardiviner Did that help?

stardiviner06:03:21

Still downloading, very slow network. @seancorfield

duminda06:03:22

@seancorfield: Right now I have three lein processes running (Using luminus - lein new luminus myapp +swagger +re-frame +mysql): 1. lein figwheel 2. lein repl then (start) - Running server. Or, lein run 3. lein garden auto And lein run migrate is after all that.

seancorfield06:03:19

All those JVMs... 🙂

duminda06:03:24

Perhaps a noob mistake, but what I understood was that I have to run lein figwheel, then run server, and perhaps run garden task afterwards. Is there a way to get all these processes into a single JVM?

seancorfield06:03:32

@duminda Just noticed your timezone. My wife's currently in that timezone 🙂

seancorfield06:03:07

@duminda I've no idea about Luminus or any of the cljs stuff, sorry.

duminda06:03:32

@seancorfield: No problem. At least now I know I have a problem. 🙂

seancorfield06:03:32

I use Boot and when developing, I start the Component for each of my apps, all inside a single JVM.

stardiviner06:03:21

@seancorfield Now it works. Thanks. Is there some new libraries like clj-tagsoup suggested?

seancorfield06:03:15

No idea. But you can nearly always override old dependencies by specifying more up-to-date versions directly in project.clj. Sometimes you also need to add :exclusions as well.

seancorfield06:03:03

For clj-tagsoup it's probably a good idea to add :exclusions [org.clojure/clojure] since it otherwise uses a version range [1.2.0,] which can cause problems.

seancorfield06:03:26

It looks like there's been some recent activity on clj-tagsoup but no new release yet https://github.com/nathell/clj-tagsoup/commits/master

seancorfield06:03:40

So I think there will likely be a new release at some point soon maybe...

seancorfield06:03:56

@stardiviner A lot of the time, Clojure is so stable and backward compatible that old libraries just continue to work for years. Clojure 1.9 tightened up the syntax checking of a number of forms and that highlighted a lot of "bad syntax" in older libraries that just happened to get past the compiler in earlier versions of Clojure. Most library maintainers have been pretty quick to release new versions with fixes.

seancorfield06:03:50

@duminda So where are you based, if you don't mind me asking? My wife was in Wuhan this weekend and has just taken off from Shanghai, headed back home to San Francisco.

stardiviner06:03:16

I see. After I mastered Clojure more better, I will try to contribute on things like this. Thanks for your help.

duminda06:03:34

@seancorfield: I’m in Singapore. Not citizen, just working.

seancorfield06:03:52

@duminda Oh cool. Somewhere I'd love to visit one day.

seancorfield06:03:41

@stardiviner The whole Maven/Java versions thing with libraries is a giant pain when you're learning Clojure. But you get used to using lein deps :tree to figure out what's going wrong and how to fix stuff.

stardiviner06:03:26

@seancorfield This is really a great helpful tip.

gklijs06:03:55

I find lein deps :tree a whole lot better then any other tool I tried for java to resolve them. Especially the suggestions are nice.

seancorfield06:03:42

And adding :pedantic? :abort to flush out those nasty version ranges!

seancorfield06:03:23

(in Boot there's a show task -- boot show -d is the equivalent to lein deps :tree and boot show -p is equivalent to Leiningen's pedantic mode)

seancorfield07:03:50

Ah, Europe is just coming online I see @gklijs! That's good because it's bedtime here...

gklijs07:03:15

@seancorfield yes, the Netherlands, where i live and work, but not to get to far off-topic.

seancorfield07:03:22

(it just means I can go to sleep and know that there are still folks active here to answer questions! 🙂 )

stardiviner08:03:31

@seancorfield Have not found :pedantic? :abort in lein deps --help. And execute lein deps :tree :pedantic? :abort report error Unknown deps command :tree.

seancorfield16:03:34

@stardiviner You put :pedantic? :abort in your project.clj file. See lein sample for all the things you can do in a project.clj file.

magra08:03:48

Hi, can anybody point me in a direction what this errormessage means or where it comes from?

error in process filter: user-error: Some namespaces are in a bad state: error "class java.lang.String is not a function, but it’s used as such" in inserate.api.mutations 

serge08:03:13

What is the analogue for React/Redux for cljs?

joelsanchez08:03:26

React wrappers include Om, Reagent and Rum; re-frame is very Redux-like

serge08:03:24

Thank you

joelsanchez08:03:29

it would be more correct to say that Redux is very re-frame-like, as re-frame predates Redux

magra08:03:44

Ahh. I think I found it. A String literal in function position. I am used to getting "can not be cast to IFn" with a line number when I do that.

duminda09:03:04

In a project created with luminus template, I have the following code in a file:

(ns ppserver.db.core
  (:require
    [clj-time.jdbc]
    [clojure.java.jdbc :as jdbc]
    [conman.core :as conman]
    [ppserver.config :refer [env]]
    [mount.core :refer [defstate]])
  (:import [java.sql
            BatchUpdateException
            PreparedStatement]))

(defstate ^:dynamic *db*
  :start (conman/connect! {:jdbc-url (env :database-url)})
  :stop (conman/disconnect! *db*))

(conman/bind-connection *db* "sql/queries.sql")
It seems that if the sql/queries.sql file has problems, I can’t even get a repl by doing lein repl:
Exception in thread "main" java.lang.ExceptionInInitializerError
	at clojure.main.<clinit>(main.java:20)
Caused by: clojure.lang.ExceptionInfo: Missing HugSQL Header of :name, :name-, :snip, or :snip-
...
Is this a problem? I’m unsure because this code is generated by the template. Isn’t having code like (conman/bind-connection *db* "sql/queries.sql") at top level a bad idea?

rende1112:03:53

Hello! Which library should use for "naming routes", like this example: https://github.com/montoux/namedroutes? (This looks old and unsupported)

rende1119:03:57

Looks good, but I already use Compojure, bidi - is alternative for it, not addition

edwaraco13:03:19

@duminda how is your sql/queries.sql file?

duminda13:03:02

There was some problems in it. I fixed them and now it’s fine.

tkjone15:03:11

I just want to confirm my understanding of ~/.m2/repository/* (which is where all of the clojure dependencies on our local machines are stored to my knowledge). Whenever we install a CLJ/S dep, in any project we work on our local machines, it is added to this directory, correct? This works because if we were to venture into any of the packages in ~/.m2/repository/*, lets say ~/.m2/repository/cljfmt/cljfmt/* it will have further directories named after the versions you have locally - for example I have 0.5.3 0.5.7 dirs. So in a way, this is like a global node_modules directory. The main difference is that ~/.m2/repository/* is storing everything under directories named after the specific version, so unlike node_modules we do not have to have an .m2 dir inside each of our clojure projects. Would this outline be correct?

russ76716:03:31

@tkjone I'm certainly no expert on either Maven or Node but what you have outlined is in line with my understanding.

seancorfield16:03:41

> Isn’t having code like (conman/bind-connection *db* "sql/queries.sql") at top level a bad idea? @duminda Yes, having side-effecting global state in top-level expressions in a namespace is a very bad idea, in my opinion.

duminda17:03:50

@seancorfield: I get so confused with all these stuff. I gave up trying to figure stuff out and am now just trying to get something working (whatever that works). Fixing the sql/queries.sql solved my problem for the time being. I don’t know what will happen when I do lein uberjar though. That’s a problem for another time I guess :man-shrugging:

derpocious17:03:58

hey all, I'm wondering why this code isn't printing "here I am!"

derpocious17:03:06

(ns klipse.app
  (:require-macros
    [cljs.core.async.macros :refer [go]]))

(go
  (println "ok")
  (let [ok (<! (timeout 1000))]
  (println "here i am!")))

joelsanchez17:03:18

(ns klipse.app
  (:require
    [cljs.core.async :refer [timeout]])
  (:require-macros
    [cljs.core.async.macros :refer [go]]))

derpocious17:03:05

huzzah, thanks!

derpocious17:03:17

weird that it didn't give me an error for forgetting it.

joelsanchez17:03:28

in recent versions of cljs you can just refer go directly FYI

dpsutton17:03:39

wow. opening up the console on that page has a lot of errors

derpocious17:03:49

haha yes lots of errors.

derpocious17:03:57

I don't think they are all from me though 🙂

derpocious17:03:54

thanks @joelsanchez I think I need it in klipse though

derpocious17:03:09

but it seems that I don't need to require <!

duminda17:03:43

Been using clojure/clojurescript continuously for close to two weeks now, I find myself wasting more than the usual amount browsing and reading and comparing this and that. Do I use Om/re-frame/reagent/… Which routing library to use? lein or boot? It’s just option paralysis (for me at least). I don’t want so many options - at least not so early. I almost want some authority figure to guarantee: “Here, go use this and this and that to build a web app and you will be fine”. Right now I’m trying to get my head around the language (being a newb) + trying to learn and use some libraries + trying to evaluate all those libraries against each other because there’s so many + trying to grapple with idiosyncrasies of the env + trying to write code that does what I need it to do. Perhaps I’m too dumb for this. Apologies for the rant, don’t mean to diss clojure in any way as I’m just new and perhaps not going at this the correct way. I just wish the learning curve was easier. Any comments are very welcome.

radomski17:03:02

Lest conceptual load? I would recommend reagent and lein

joelsanchez17:03:27

re-frame builds on reagent so that list doesn't really make sense, Om Now is dead and Om Next is unstable, so just use re-frame with reagent

sundarj17:03:41

you're missing Fulcro 🙂

joelsanchez17:03:31

there, solved 😛

radomski17:03:59

you will find superior tooling and some nicer documentation with re-frame

duminda17:03:52

@radomski, @joelsanchez: Actually, I confess I am quite happy with re-frame.

radomski17:03:39

If you're already using re-frame it is safe to stick with it.

lee.justin.m17:03:17

@duminda you’re not too dumb for this. i was in the same boat not too long ago and had the same reaction. the reality is that cljs tooling is rough in many places and there is no big player like facebook pushing a singular vision forward.

seancorfield17:03:32

I think it's unfortunate that a lot of Clojure beginners tend to want to learn full-stack so they try to learn cljs + clj at the same time, which means a slew of libraries and tooling and the JVM ecosystem on the back end... and that's a HUGE amount to try to learn at once.

seancorfield17:03:15

I think it's much better to learn Clojure first, on its own, then pick up ClojureScript once your comfortable since then you'll only need to be learning the tooling, not the syntax/language.

anantpaatra17:03:16

@duminda this is the current state of how Clojure receives newcomers. I'm one myself. I'm a hard learner and even though I've been exposed to Clojure for a really good time now, I still struggle severely with tooling and figuring how things fit together.

seancorfield17:03:50

The cljs ecosystem is pretty user-hostile, even tho' it has improved dramatically over the last few years.

seancorfield17:03:07

And that web development book based on Luminus is not a good "first book" for learning the stack (in my opinion). Clojure for the Brave and True, Living Clojure -- those are better ways in, as far as I can tell.

anantpaatra17:03:59

@duminda I have found that authoritative person though, and his stuff helped and are helping me beyond words. Unfortunately he has to make a living of it so it's mostly paid material (although there are many freebies in his website) but he is the sweetest person and extremely engaged in trying to soften the barrier of entry. Check it out if you haven't: https://purelyfunctional.tv

duminda18:03:11

Actually, I was planning to try membership next week!

anantpaatra18:03:25

I can't recommend it enough. Really. Each contact with him is a lesson in humbleness and he really tries to help each step of the way.

seancorfield18:03:59

Eric is awesome -- love his videos!

lee.justin.m18:03:49

the great frustration for me is that the language has not been particularly difficult for me to learn. a few oddities here and there, but mostly pretty straightforward. the frustration for me has been things like: how do i get a repl working and why are there 6 different pieces of software involved to make it work. how do i get an editor working. how do i do normal web dev stuff like install an npm library and get it to work.

duminda18:03:45

Exactly! The language itself is beautiful from the little I have tried so far.

drewverlee18:03:33

Are you still having repl issues?

lee.justin.m18:03:07

oh yea i gave up on using a repl under my setup. i just rely on print statements and hot reload. if i need to mess with a library i either use planck or i use boot -d. but an actual functional cljs nrepl connection to atom has never worked

drewverlee19:03:10

Did you try protorepl?

drewverlee19:03:37

I probably won’t be able to help with a more complex project. As i understand, sometimes hot code reloading can get tricky at points with larger codebases

lee.justin.m19:03:53

yes that’s what i use. but protorepl does something weird with its nrepl conneciton. thheller tried to explain it to me but it was over my head.

drewverlee19:03:56

My guess is ones with lots of stateful components, hense libs like compjure.

lee.justin.m19:03:19

i use shadow-cljs because i need something sane for npm modules

drewverlee19:03:29

Ah, ok. Then i probably can’t be of any help 🙂

lee.justin.m19:03:31

i don’t use a clojure backend. it’s just cljs frontend stuff

lee.justin.m19:03:01

but to be fair, protorepl doesn’t work well with figwheel projects either

lee.justin.m19:03:20

you can send forms to eval, but none of the other cool stuff like doc strings, autocompletion, etc. works

lee.justin.m19:03:44

CIDER was just as clunky and nonfunctional

drewverlee19:03:56

Right, I haven’t had much luck with understanding how to utilize figwheel unless it was part of a packaged solution.

drewverlee19:03:08

So your not alone in that struggle.

drewverlee19:03:40

Luckly, the maintainer of figwheel is getting some funded to work on it.

drewverlee19:03:56

So i would expect improvements in the next couple months.

lee.justin.m19:03:09

that’s great

lee.justin.m19:03:48

That’s pretty cool, though I still find stuff like this insanely complicated I am now recommending that you forgo using lein figwheel and instead run lein repl and then launch figwheel from the Clojure nREPL session. This will allow you to reuse all of your project.clj's nrepl configuration.

duminda23:03:42

@lee.justin.m : I tried atom + proto reply first, had the same sort of trouble you are having, and then switched to cursive. Cursive works better for me so far.

lee.justin.m23:03:20

@duminda with clojurescript or with clojure?

duminda23:03:01

I do lein figwheel and then connect to nrepl port with cursive. After that, it’s quite easy to work with.

anantpaatra18:03:10

@lee.justin.m exactly how I feel.

lee.justin.m18:03:47

i actually find it easier to read the source code of big complex libraries and figure out what’s going on, for example, as compared to javascript, where i still can’t follow what people are doing sometimes even though i’m 10 times more experienced in that langauge

duminda18:03:18

Thanks for the comments guys. Appreciate it. I will try http://purelyfunctional.tv guides and perhaps “Clojure for the brave and true” (which seems to be aptly titled)

jonjanisch19:03:44

Since we’re talking about beginner woes, I’ll add my own 🙂 Prefixing symbols with a single quote often confuses me (like with the various forms of require). For example, the main difference between the functions below is one returns a seq of functions in a namespace while the other prints them. I might even expect dir to be implemented using dir-fn. Yet one is prefixed with single quote while the other isn’t. As a beginner this looks like an inconsistency in the API.

(clojure.repl/dir clojure.string)
(clojure.repl/dir-fn 'clojure.string)
I find myself really dependent on clojuredocs as I cannot really determine what a function expects from the docstring alone (e.g. these are both documented as taking a namespace as an argument). I realize it’s likely related to dir being a macro while dir-fn is a function and I need to really go back and re-learn macros 🙂

noisesmith19:03:18

whose api? the main difference here is macros vs. functions - macros can decide whether to evaluate args, so as a convenience make it so you don't need to quote certain args

noisesmith19:03:33

what on the surface might look inconsistent is actually the sign of a very consistent underlying structure - instead of a huge number of special case syntaxes, we have forms with arguments, some being functions (all args evaluated before use) and soem macros (processed before usage)

jonjanisch19:03:17

yeah, I'm sure once I have a better mastery of macros it all makes sense

sundarj20:03:19

you need not be a master :) the only thing you need to know is that macros receive their arguments unevaluated (as data); functions receive them fully evaluated. quoting is for opting out of evaluation, which naturally isn't needed for macros since their arguments are received unevaluated anyway

sundarj20:03:35

also keep in mind that 'a is not the same as a unevaluated. it expands to (quote a), which is what a macro would receive

sundarj20:03:49

=> (defmacro foo [x] (prn x))
#'boot.user/foo
=> (foo a) 
a                   
nil                 
=> (foo 'a)
(quote a)           
nil                 

noisesmith19:03:41

in the specific case of dir / dir-fn, dir-fn exists in order to be an implementation detail for dir

lee.justin.m19:03:34

is there a convention for documenting which arguments get evaluated and which do not in macros?

noisesmith19:03:46

sadly, not that I know of

noisesmith19:03:12

but the doc string for the macro should make it clear what each argument is for (your milage may vary in practice)

lee.justin.m19:03:50

I was thinking instead of writing Prints a sorted directory of public vars in a namespace maybe it could say Prints a sorted directory of public vars in 'nsname but I don’t know if that works generally

jonjanisch19:03:50

To be fair one takes a parameter named nsname while the other is named ns

russ76719:03:31

@noisesmith So here is a kind of rule of thumb if you are working in a REPL: If you are dealing with an ordinary function, you can always evaluate the function "name". So if you try to evaluate inc you will get something that looks like #object[clojure.core$inc 0x21ab2448 "[email protected]"]. If you are dealing with a macro you or something built in like 'if', then you can't evaluate it: You will get an error message like 'Can't take value of a macro:'. Functions always evaluate their args, macros and those other built-in things like 'if' may or may not. Or you can look at the docstring, but this is way easier to do than it is to talk about.

noisesmith19:03:27

@russ767: I assume you meant to tag jonathan there

russ76719:03:47

Sorry @noisesmith. You know me, I just start talking to anyone happens to be standing there.

noisesmith19:03:03

haha sure thing

ben.borders20:03:27

In go blocks, if a statement throws an exception, can a statement that is declared further down the go block ever be executed? and if so, is there a way around this?

ben.borders20:03:18

example:

(go
	(try
		(let [_ (/ 1 0)]
		  (println "blah"))
	    (catch whatever, please dont print)
	))
I don’t want blah printed here

noisesmith20:03:01

between channel input and output, you can expect code inside go to follow relatively normal rules

noisesmith20:03:23

everything between two channel usages gets put into a function in the go state machine

noisesmith20:03:11

but a try/catch across multiple channel operations... I wouldn't expect it to work in a sensible way

ben.borders20:03:46

hmm, yes inside the go block will be takes and puts to different channels

ben.borders20:03:07

(try
+        (let [{:keys [facts
+                      receipt-handle]} (async/<! chan)
+              information       (core/urpdates facts)]
+          (async/onto-chan chan information)
+          (sqs/delete-message {:receipt-handle receipt-handle :queue-url queue-url}))
+        (catch Exception e "blah"))))

ben.borders20:03:22

so for exmaple i only want to delete the sqs message when no exception E is thrown

ben.borders20:03:41

i should be alright then?

noisesmith20:03:16

interacting with sqs inside a go block is bad - io and such

noisesmith20:03:46

and in terms of code quality, isolating the api interaction / io from channel ops will lead to better code also

noisesmith20:03:31

@ben.borders since this is #beginners, in case you are not familiar, core.async go blocks use a small fixed number of threads, and blocking operations like IO inside go blocks can lock up all async operations in the entire app

noisesmith20:03:01

there's clojure.core.async/thread for launching things that might block, it returns a channel you can read from to get proper async behavior

ben.borders20:03:49

ahh that makes a lot of sense

noisesmith20:03:51

regarding the exception handling, I'm not sure how a try/catch surrounding multiple channel ops would even work, maybe someone else can offer advice

greg31620:03:55

simplest usage with deferred values is something like:

(->
  (d/future
    (call-api! {:foo true}))
  (d/chain
    (fn [result]
      (prn result)
      (handle result))
    ; do more things when the above function returns...
    ,,,)
  (d/catch BadRequestException
    (fn [ex]
      (log/error ex "Bad API call!")))
  ; can chain more things, catch more errors, etc.
  ,,,)

greg31620:03:16

there’s an async stream abstraction that works with core-async too

noisesmith21:03:22

@greg316 that's an excellent point

ben.borders21:03:19

thanks for the information!

ben.borders21:03:36

fwiw for other readers: here is a good read on async and blocking io http://martintrojer.github.io/clojure/2013/07/07/coreasync-and-blocking-io

senorflor21:03:22

What resources (besides this Slack, obviously ❤️) are people finding most useful on their journey to using/grokking Clojure, and also what are folks’ current biggest obstacles/difficulties/known unknowns? This can be tools, blogs, videos, books, libraries, people… anything really!

val_waeselynck22:03:18

1- books, and the REPL 2- discoverability of tools, and the REPL

seancorfield22:03:30

Back when I was getting started, I'd say: 1- the REPL (and a pile of books) and 2- translating error messages back to my code, and finding decent documentation, esp. about "the best choice" for various libraries.

seancorfield22:03:03

(the latter really hasn't improved at all, IMO -- Clojure remains incredibly unopinionated about libraries)

anantpaatra23:03:18

That the official resources are unopinionated is the hardest thing to me. At every corner a new library is indicated or even taken for granted. Newcommers need consistency across learning materials, especially people without a solid programming background or without a background at all. Also, Clojure libraries tend to be robust and opinionated about dependencies and stack, so the lack of a main structure for the beginner to compare the alternatives to can drain ones energy like a succubus.

seancorfield23:03:53

I think it's fair to say that while Clojure is a perfectly good language for web development, that's definitely not its focus and -- combined with the mindset of preferring composable libraries over frameworks -- that means that for beginners trying to learn Clojure for web development it's an extremely hard path to follow. For all that I'm sure Luminus is a fine "web framework", I think it would be better for a web development book to start with the basic libraries and build up to where a framework might make sense.

senorflor23:03:44

Thanks all! FYI I asked a similar question in #clojure on the theory I’d get a complementary perspective, and there’s some great contributions from people over there too: https://clojurians.slack.com/archives/C03S1KBA2/p1520373219000385, https://clojurians.slack.com/archives/C03S1KBA2/p1520375631000432

anantpaatra00:03:38

I agree with your sentiment about how Web Development should be introduced to newcomers @seancorfield, but the thing is that a lot of us are coming to Clojure/Clojurescript in search of a heaven of elegance and simplicity for Web Development. This is what the language itself gives us, but the ecosystem is really off. Too many people coming to the ecosystem and getting scared not by the language (as one would assume since it is a "lisp") as said earlier, but by the tooling, the libraries and how they connect.

tkjone01:03:11

@seancorfield Your comment on clojure not being focused on web development. How do you mean? I understand that the original purpose of clojure would align to this, but given that we have CLJS now and all the efforts being made there, would we not feel that clojure is more and more embracing the wed development narrative?

duminda01:03:07

@: I was asking a same sort of question yesterday. Add to this rant ides + repl woes which I didn’t include in the original post. https://clojurians.slack.com/archives/C053AK3F9/p1520357383000137

seancorfield01:03:47

@tkjone ClojureScript is focused on the browser (and the tooling is far from beginner-friendly, even today) and there's still no "official" way to build front ends -- just lots of choices -- and Clojure on the server has no focus on web development.

seancorfield01:03:50

"a heaven of elegance and simplicity for Web Development" -- there's no such thing (yet).

seancorfield01:03:18

Folks keep bringing up Rails and praising its opinionated batteries-included but it's still essentially server-side web development -- and that's what I'm comparing Clojure to. If your definition of "Web Development" is full-stack (I hate that phrase) and you're looking for SPA on the front and APIs or web sockets on the back then ClojureScript/Clojure still don't have a solution. Cognitect does a lot of server-side stuff and I haven't seen much focus from them on client-side, and certainly very little on tooling or ecosystem -- that's what I'm saying.

seancorfield01:03:19

And I don't want to dismiss the huge amount of work they've put in to Clojure (and ClojureScript) and Datomic -- but, so far, it's pretty much been all about what they've needed internally for their projects that has "leaked out" as things like Pedestal etc.

seancorfield01:03:56

And as y'all are seeing: Clojure/ClojureScript is not easy when it comes to web development -- and the simplicity of the language is swamped by the complexity of modern web development. Hence, my suggestion that trying to learn Clojure(Script) in the context of a full-stack web application is going to be substantially harder than trying to learn Clojure in a purely server-side context.

lee.justin.m02:03:47

@seancorfield even setting “full-stack” development aside, I think it is totally possible to go clojurescript first if you have a json endpoint to program your spa against. the real shame is that clojurescript is so very close to being great at web development. it really isn’t far off at all: most of the actual hard stuff already works. that’s what’s super frustrating is that it seems like we’re wiffing the easy stuff

duminda02:03:05

I for one, would not have opportunity to get into any Clojure if it wasn’t through web dev (due to various reasons)

tkjone02:03:27

I see. I agree with Sean's assessment if your are trying to approach it from a rails, server-side perspective. But then again, even Rails is having a hard time being Rails. To further this thought, I believe that even the web development world and languages known for web development are all being swamped by complexity. The tooling for everything front end is also insanely complex to the point where people regularly apologize because web development has become so large. Which is one of the reasons I finally came to clojure. But to the original point, and like nakiya said, web development might be, depending on your experience with programming, the most accessible way to get into the language. I have no real thoughts on the best way myself. I read a lot and practiced. All in all, it depends on where you are in your programming journey - early, beginnner, intermediate or advanced - each of these levels is going to change the way the language should be taught. But yes, if I was teaching someone how to clojure from the early learner perspective, I would think of organizing a curriculum like "Learn Python The Hardway"

lee.justin.m02:03:04

create-react-app shows that with effort you can kill most of the incidental complexity by using sensible defaults and providing an opinion as to the minimum number of things possible and get people building their project in no time. things that every website must solve have a solution there: installing npm modules, development vs production mode, proxying to an api, minification and bundling, code maps so that the debugger works. that stuff just works if you use CRA. shadow-cljs solves many of these problems, which is why i often steer people towards it, though really i have no business giving advice 🙂

tkjone13:03:27

On the flip side, that is the most sensible option for people who have decided to use React. There is still a conversation before the one about create-react-app - which front end framework should I use? Which I think is more paralyzing and where I think most people get lost in the clojure ecosystem. But I am 100% on board with create-react-app and I would likely steer people there if they decided React was what they want to try