Fork me on GitHub
#beginners
<
2018-02-21
>
Pedro00:02:01

Hey guys, could somebody help me with the following code?

(reduce #(let [requesting-agent ((:agents structured) (%2 "agent_id"))
                     assigned-task (dequeue requesting-agent (:jobs %1))]
                 (-> %1
                 (fn [data] (update data :jobs (fn [x] (remove (fn [j] (= (j "id") (get-in assigned-task ["job_assigned" "job_id"]))) x))))
                 (fn [x] (update x :assigned conj assigned-task))))
              {:agents (:agents structured)
               :jobs (:open-jobs structured)
               :assigned []}
              (:pending-requests structured))
this is my first time trying to use ->. I'm trying to both remove from :jobs and insert into :assigned. What I'm doing wrong?

sundarj00:02:24

you can see what macros expand to using macroexpand-1 and macroexpand (the latter throws an error in this case):

user=> (macroexpand-1 '(-> 2 (fn [x] x)))
(fn 2 [x] x)
user=> (macroexpand-1 '(-> 2 ((fn [x] x))))
((fn [x] x) 2)

sundarj00:02:18

the threading macros do a blind data transform, they don't know anything about what you're passing in

sundarj00:02:05

user=> (-> [x 2] (let (inc x)))
3
user=> (macroexpand-1 '(-> [x 2] (let (inc x))))
(let [x 2] (inc x))

sundarj00:02:20

also see (doc ->)

Pedro00:02:31

this just went way over my head 😂

sundarj00:02:44

ah ok, if you don't know how macros work read this: https://aphyr.com/posts/305-clojure-from-the-ground-up-macros (the rest of the posts in the series are great too)

sundarj01:02:56

basically (-> x f g) blindly rewrites that code into (g (f x)) at compile time, which is then evaluated at run time

Pedro01:02:15

yeah, that's what I expected, but I'm not sure why my code doesn't work

sundarj01:02:14

because, say, (-> 2 (fn [x] x)) turns into (fn 2 [x] x), which is invalid

Pedro01:02:06

then how would I go about chaining updates?

sundarj01:02:46

in your case, since you're defining the functions in-line, you can wrap them in an extra pair of parens

sundarj01:02:54

user=> (macroexpand-1 '(-> x (fn [x] (inc x))))
(fn x [x] (inc x))
user=> (let [x 3] (-> x (fn [x] (inc x))))
#object[user$eval150$x__151 0x3a022576 "[email protected]"]
user=> (macroexpand-1 '(-> x ((fn [x] (inc x)))))
((fn [x] (inc x)) x)
user=> (let [x 3] (-> x ((fn [x] (inc x)))))
4

Pedro01:02:57

I guess taking of the whole fn thing and just using update?

Pedro01:02:18

oh god, it worked!

Pedro01:02:21

thank you!

sundarj01:02:32

no problem 🙂

sundarj01:02:25

does it make sense?

Pedro01:02:57

somewhat, but I think just removing fn and leaving only the update made more sense

sundarj01:02:51

ah, right, yeah - i didn't actually look at your code in too much detail. glad you worked it out 😛

sundarj01:02:09

you are correct in that those functions aren't needed since it's a blind data transform, and indeed it makes more sense to remove them. sorry i should've seen that

sundarj01:02:22

better that you did though 😁

Pedro01:02:15

could you hive me a hand with something else?

sundarj01:02:45

shoot 🙂

Pedro01:02:04

I have the following:

(first (into
                                   (filter #(can-execute-work (% "type") (requesting-agent "primary_skillset")) jobs)
                                   (filter #(can-execute-work (% "type") (requesting-agent "secondary_skillset")) jobs))

Pedro01:02:18

I need the filtered resolts from primary_skillset to show up first if any is found. But in case there's both a valid primary and secondary the secondary is showing up first

Pedro01:02:25

is there a better way of doing this?

sundarj01:02:57

so, here is what into does behind the scenes (these are equivalent):

user=> (into [] [1 2 3])
[1 2 3]
user=> (reduce conj [] [1 2 3])
[1 2 3]

Pedro01:02:33

just to put in other words, the requesting agent should give priority for jobs of types in it's primary skillset, but if none is found it tries to find some job that matches it's secondary skillset

sundarj01:02:42

and here is how conj behaves, first for a vector, and then for a list:

user=> (conj [1] 2)
[1 2]
user=> (conj '(1) 2)
(2 1)

sundarj01:02:36

filter returns a list, so conj (and thus into) will prepend items to it:

user=> (conj (filter odd? [1 2 3 4 5]) 6)
(6 1 3 5)

Pedro01:02:53

right, I was thinking that it might have to do with the way the collection adds to it

Pedro01:02:00

oh, it's a list?

Pedro01:02:00

is it always a list? the documentation I found just says it's a lazy sequence

sundarj01:02:08

yeah, as you can see:

user=> (filter odd? [1 2 3 4 5])
(1 3 5)
(it's a little bit more complicated than that, but what filter returns looks and works like a list for this purpose)

sundarj01:02:34

yeah lazy sequences are list-like

Pedro01:02:07

that's important to remember

Pedro01:02:28

so the simple solution is just to flip the calls, right?

sundarj01:02:39

that will mean the primaries are conjed onto the secondaries, so will show up at the beginning, yeah

Pedro01:02:10

alright!!!

sundarj01:02:22

note that you don't really need to remember that lazy seqs are list-like: they're printed like lists, so that's how you know they work like lists

Pedro01:02:23

with that I think I'm finished!

sundarj01:02:42

here is what the clojure website has to say on sequences: https://clojure.org/reference/sequences

sundarj01:02:51

user=> (seqable? [1 2 3])
true
user=> (seqable? {:a 2})
true
user=> (seqable? "hello")
true
user=> (seq [1 2 3])
(1 2 3)
user=> (seq {:a 2})
([:a 2])
user=> (seq "hello")
(\h \e \l \l \o)

sundarj01:02:15

map/filter etc end up calling seq on their arguments, turning them into a sequence, and return a sequence

sundarj01:02:53

all of the 'sequence functions' in Clojure do the same

sundarj01:02:17

and all take their collection argument last

Pedro01:02:43

thank you, i'll take a look at it

steven kent01:02:19

If you have this [[["this is a file" " this is the second sentence"] ("." ".")] [["i am trying to capitalize the first word of every sentence, using Clojure"] (".")] [["will this work" " will this work"] ("?" "!")] [["i hope i can get this to work"] (".")]] where you have the sentences in a vector and the associated punctuation in a list, how do you join them up?

andy.fingerhut01:02:52

Try this in a REPL:

(map str ["foo" "bar"] ["?" "!"])

steven kent01:02:03

@andy.fingerhut cool, it's working so far

steven kent01:02:49

@andy.fingerhut if I try that on one of the components it works but when i try to map str against the entire thing it doesn't work

steven kent01:02:58

i guess that will take a little more work

steven kent01:02:36

I got this to work

(defn more-massaging [coll]
  (loop [coll coll, result []]
    (if (empty? coll) result
        (let [[f & r] coll]
        (recur r (conj result (apply map str f)))))))

andy.fingerhut01:02:26

Cool. Yeah, the expression I gave was a hint to get you started, not necessarily what you needed for the whole problem.

steven kent01:02:59

@andy.fingerhut i'm down with that. Thanks!!

noisesmith01:02:51

@stevenpkent as a future suggestion, every loop that consumes the first item of a collection and recurs with the rest can be rewritten as a simpler map, reduce, or mapcat

noisesmith01:02:24

not needed if your code works to your satisfaction, but if you start to wonder "how would I eliminate 3/4 of this code while preserving the correct result"

steven kent01:02:55

@noisesmith definitely down with that also. yeah, I'm a few weeks into Clojure. Still pretty basic knowledge

steven kent01:02:47

@noisesmith i think right now i'm writing the code so that i clearly understand it and will refactor into better code in an "evolutionary" manner

noisesmith01:02:24

peregrine.circle=> (def input '[[["this is a file" " this is the second sentence"] ("." ".")] [["i am trying to capitalize the first word of every sentence, using Clojure"] (".")] [["will this work" " will this work"] ("?" "!")] [["i hope i can get this to work"] (".")]])
#'peregrine.circle/input
peregrine.circle=> (map (partial apply map str) input)
(("this is a file." " this is the second sentence.") ("i am trying to capitalize the first word of every sentence, using Clojure.") ("will this work?" " will this work!") ("i hope i can get this to work."))

noisesmith01:02:12

this turns everything but (apply map str) from your original, and replaces it with map

noisesmith01:02:43

because that loop you wrote was actually map in disguise (except it wasn't lazy)

sreeram03:02:12

Could any one help me with clojure spec function "map-of", i'm very new to this kinda validation, some how figured out that i can validate my data if i dump it into hashmap using map-of and been trying to use it but no result so far, I'm basically trying to weed out of the records with bad fields. Any help would be helpful, anyways its pretty cool to find slack just for clojure developers.

mfikes03:02:43

@devi.sreeram51 If you are trying to look at the types of fields, say for example validating a map like {:name "Jane" :age 35} where you want a string name and integer age, then s/keys will be useful, where s/map-of handles a different use case were you want to indicate that all keys have a type and all values have a type.

sreeram03:02:16

@mfikes Thanks for quick response. I least bothered about keys, actually i have list of vectors like (["jane" 35] ["sam" 26]), trying to validate their types

sreeram03:02:32

was naively trying to use conditional statements since i'm from java background, realized its a blunder and trying to do it clojure style

mfikes03:02:50

@devi.sreeram51 Cool. Without fully understanding your use case, the example you gave could be validated via something like (s/coll-of (s/cat :name string? :age number?))

sreeram04:02:43

it was part of sample exercise our team is working on, one of the exercise is to validate the data in vectors to their types, there is no key value pair ( i was thinking about keeping everything in map before i knew how map-of is used), its just list of vectors with some bad records ( wrong data type) which needs to be removed. Example (["jane" 35 true] ["sam" -26 false] ["ram" 25 faaa])

sreeram04:02:16

Here the result is supposed to be (["jane" 35 true]), thanks for suggestions and helping me out, i really appreciate it.

seancorfield04:02:53

@devi.sreeram51 If you're starting with a collection of tuples, that's probably the shape of the spec you should use... s/coll-of and s/tuple

seancorfield04:02:48

Sounds like the case above would be (s/coll-of (s/tuple string? pos-int? boolean?)) perhaps?

sreeram04:02:54

I think i'm getting hang of it, thank you very much

sreeram04:02:23

that was very helpful, should be give enough to get to what i need

Karol Wójcik09:02:47

Hello! I got snippet of code which uses bunch of reducing functions. Here is an example https://repl.it/@KarolWojcik/OutlyingOpenFan . The problem is that the program iterates 3 times instead of 1 time. Is it possible to iterate only once and apply those 3 transformations on the sequence? If yes how can I achieve it? Thanks on advance for the help.

Karol Wójcik13:02:17

@schmee Thank you very much 🙂 Really appreciate that 🙂

schmee13:02:37

glad that it helped you! 🙂

schmee09:02:05

this uses a thing called transducers, you can read more about them here: https://clojure.org/reference/transducers

schmee09:02:34

I also recommend watching the introductory talk about them: https://www.youtube.com/watch?v=6mTbuzafcII

gonzalo10:02:35

Hello, people! Good morning. I'm trying to get into Clojure after coming from Rails, Elixir, and C#, but I'm having troubles with some tasks that I'm pretty sure should be easy. The main one is using databases. Using jdbc is easy enough and it works, but it "breaks" for me when doing table joins in a belongs to relationship, break meaning here that I don't actually know how to get all the separate rows from a table join and then offer them in a nested structure that would be valid json. Is there an easy way of doing this, or am I thinking this problem the wrong way?

hawari10:02:38

Hi @me1017, can you help with showing what you've already done? Is it the trouble of querying the database or processing the rows after successfully querying the database?

gonzalo10:02:02

Sure, let me cleanup the code first.

gonzalo10:02:40

(defn query []
  (jdbc/query db-spec ["select cities.*, theaters.* from cities join theaters on cities.id = theaters.city_id where city_id = 5"])
  )
Let's imagine this query. We have a list of cities, and theaters in those cities. Using Elixir and Ecto I could model this as two entities, establish a has_many relationship between cities and theaters and then I'd get an array of maps, where every map would be one city, and one of the fields would be theaters, being another array with the corresponding theaters. This is the part that I don't know how to do using Clojure.

Zsuark10:02:50

Does anyone here know how to specify a username/password to use with Clarango? The clarango docs are strangely quiet on this topic.

firthh10:02:14

@me1017 the clojure JDBC stuff doesn’t aim to replace an ORM like Ecto or ActiveRecord, if you want that transformation to occur you need to do it yourself, it should just be a simple pure function, and you can use :row-fn to ensure that transformation occurs on each row. If you want a full ORM there is Korma - http://sqlkorma.com/ but I can’t comment on if it will solve your problem as I’ve never used it in anger

gonzalo10:02:30

Yeah, I'm not looking for an ORM. Sorry, I think I'm not explaining myself correctly 😛. After that query, I have a list of rows, but being unexperienced in Clojure, I have no idea how to transform that list into the other struct (that could be used, for example, to serialize it into JSON).

firthh10:02:50

The great thing about FP is that you shouldn’t be thinking about how to transform that list. Think about how to transform a single row - it should come back as a simple Clojure Map (with string keys?) that you can then use get or destructuring to get values out and restructure

gonzalo10:02:50

But I cannot use just one row for this, can I? The result of a join would be one row per theater in this case. IE, if we have a city with id 5, and two theaters with city_id = 5, the join would return two rows.

hawari10:02:05

In that case (if I understand correctly), you can use group-by after the query part:

(->> (jdbc/query db-spec ...)
     (group-by :city-id))
It will transform a vector of map
[{:city-id 5 :theater-id 6 :theater-name "Theater A"}
 {:city-id 5 :theater-id 7 :theater-name "Theater B"}]
Into a map with the group-by function as the key, and the map as the value:
{5 [{:city-id 5 :theater-id 6 :theater-name "Theater A"}
    {:city-id 5 :theater-id 7 :theater-name "Theater B"}]}
Is this what you need?

gonzalo10:02:46

Uh, so simple. Let me experiment for a while, I think I might be able to make it work.

gonzalo10:02:53

Thank you very much.

gonzalo11:02:32

Even better: (group-by #(select-keys % [:id :name :state_id])) makes the key for the map the whole city data, and the value the theater data, so I can encode it easier. Thank you, guys.

mfikes13:02:52

@me1017 An alternative that may be worth considering is (group-by (juxt :id :name :state_id)) The composite key will be a vector, but using juxt is appealing in this scenario.

gonzalo13:02:17

Thanks, I'll check that option too.

mfikes13:02:51

One nice thing about juxt when processing a sequence of maps is that it composes very nicely with sort-by, as in (sort-by (juxt :id :name :state_id) rows)

gonzalo13:02:40

Interesting, though in this case I would just order from the database.

gonzalo13:02:01

I'll keep experimenting. Thanks everyone for your help, now this is a great community to get started on a new lang!

mbjarland13:02:29

as we don't have a channel for code golfing I'll ask this here. I find that trying to express a solution in the least possible number of characters often teaches me new aspects of the language and I come out understanding more. Granted, least number of characters is a bad singular metric, but I've learnt a lot in other languages playing around with terser ways of expressing things. With that preamble, I cooked up a solution to the caesar cipher problem at rosettacode and came up with the following:

(defn encode [k s]
  (let [f #(take 26 (drop %3 (cycle (range (int %1) (inc (int %2))))))
        a #(map char (concat (f \a \z %) (f \A \Z %)))]
    (apply str (replace (zipmap (a 0) (a k)) s))))

(defn decode [k s]
  (encode (- 26 k) s)

(encode 12 "The Quick Brown Fox jumped over the lazy dog")
=> "Ftq Cguow Ndaiz Raj vgybqp ahqd ftq xmlk pas"

(decode 12 (encode 12 "The Quick Brown Fox jumped over the lazy dog"))
=> "The Quick Brown Fox jumped over the lazy dog"
essentially we are using replace to replace from keys (\a \b \c ...) to values (\a+k \b+k \c+k ...) (pseudo code) in a given string and thus rotating the letters in the given argument string by k. Is there a terser way of creating the map returned by zipmap above...I still get the feeling that there is a better way to express this

mfikes15:02:51

@mbjarland I don't think this is necessarily terser, but I find it easier to read and it also works in ClojureScript:

(defn encode [k s]
  (let [alpha "abcdefghijklmnopqrstuvwxyz"
        form-map (fn [chars] 
                   (zipmap chars (drop k (cycle chars))))]
    (string/escape s (merge (form-map alpha) 
                       (form-map (string/upper-case alpha))))))

Will16:02:27

@seancorfield I’m not seeing a parse function in the clojure.java-time library

seancorfield17:02:03

@josmith2016 I asked the library author about this -- it's not exposed because the way to use custom formats it to supply them to the date construction functions directly, which I didn't realize you could do: (jt/local-date-time "yyyy-MM-dd HH:mm:ss.SSS" "2018-02-19 18:20:25.355")

Will17:02:50

That makes sense thanks 🙂

seancorfield17:02:28

@josmith2016 It's in java-time.format:

boot.user=> (require 'java-time.format)
nil
boot.user=> (doc java-time.format/parse)
-------------------------
java-time.format/parse
([fmt o] [fmt o opts])
nil
boot.user=> 

seancorfield17:02:52

(not sure why it doesn't import it into the main namespace)

Will17:02:04

Thank you

Will17:02:07

How would I convert a list of java.util.LinkedHashMaps to a clojure vector of maps?

noisesmith17:02:39

(into [] (map (partial into {})) l) will do it

bj19:02:03

style question:

(defprotocol Fly
  "A simple protocol for flying"
  (fly1 [this] "Method to fly")
  (fly2 [this] "Method to fly"))

(defrecord Bird [species]
  Fly
  (fly1 [this] (str (:species this) " flies..."))
  (fly2 [this] (str species " flies as well")))
In the Bird implementation, any reason to prefer accessing species the way it's done in fly1 vs fly2?

ghadi19:02:00

Fly2 is clearer and negligibly faster

seancorfield20:02:36

When I asked that in the past, Alex recommended direct access to record members as the idiomatic choice (and the fastest). I was a bit surprised but I've done it that way ever since 🙂

Will21:02:21

If I have a vector of maps like this: [{:a 1 :b 2} {:a 3 :b 4}], How would I run an operation like the increment function on each property in each map?

noisesmith21:02:16

(map (partial into {} (map #(update % 1 inc))) c)

noisesmith21:02:15

(ins)user=> (def c [{:a 1 :b 2} {:a 3 :b 4}])
#'user/c
(ins)user=> (map (partial into {} (map #(update % 1 inc))) c)
({:a 2, :b 3} {:a 4, :b 5})

schmee21:02:30

@josmith2016 with Specter: (transform [ALL MAP-VALS] inc your-vec-of-maps)

Will21:02:20

Thanks @schmee and @noisesmith I’ll try those out

drewverlee21:02:34

I’m familiar with options to leverage java in clojure, what about the reverse. If i wanted to use a clojure lib in java what does that look like?

noisesmith22:02:37

@drewverlee this guide is pretty simple and it’s what I followed to use clojure from java https://clojure.org/reference/java_interop#_calling_clojure_from_java

noisesmith22:02:40

looks like everything it introduces is documented in @alexmiller’s like above

alexmiller22:02:17

yes, I forgot put that there too :)

drewverlee22:02:34

I look everywhere for equivalent libs in java that i know about in clojure and can’t find them. Maybe its time to see if i can’t get some clojure into our codebase i guess!

alexmiller22:02:50

I’d try to minimize the contact surface - working through the Java API works but is certainly a lot less fun than using Clojure directly

mathpunk22:02:53

I'm looking for readings that will help me understand the differences and similarities among: - developing cljs for node with figwheel - developing cljs for node with lumo - developing in planck (which i believe is NOT transpiling to js?) - developing cljs for the browser - developing cljs with the new clj command line tools and, how to develop something in cljs when you don't want to decide yet whether it's server-side or for the browser. Any related readings would be appreciated. (I have a hunch that one of my problems is I don't quite understand "the classpath" and how it, as an abstraction, is realized for the Java and JavaScript hosts)

mathpunk22:02:09

I'm making progress on my actual project, but when I have a problem I'm basically trying random things until I ask #clojurescript and I could be doing better than that

justinlee22:02:03

@mathpunk I don’t think you’ll find material written the way you framed it. I think you’ve got a few different things to resolve: (1) do you want to write server-side code or single-page-application style client-side code. (2) how do you want to interact with the repl (3) if you write client-side code, what kind of hot reloading do you want (4) do you really need self-hosted cljs

mathpunk22:02:51

@lee.justin.m You're already helping me, by framing what the issues the different platforms address

justinlee22:02:20

first things first: why are you trying to run on self-hosted cljs? because that’s going to be a pain and i’m not sure what the benefit is.

mathpunk22:02:32

that's easy: it was the first thing I tried that I successfully required a library from npm. It might be equally easy in other ways; but my first problem was, "I want to stop waffling and pick something to start working on my actual problem"

mathpunk22:02:14

btw, I'm surprised that choosing the platform first is so important --- there's 'business logic' i want to write into libraries, and let the user choose where it operates. If you'd like more context, I'm working on procedurally generated text, and I can imagine I might want to use the library to make a little node server that generates it, but also a web page where a human can mess with grammars

noisesmith22:02:23

@mathpunk I’d say the easiest way to understand all of those is start with the official clojurescript docs, the other things you describe are all wrappers or replacements for cljs.jar and IMHO if you want to understand all of them just using cljs.jar directly makes the other things easier to understand

noisesmith22:02:34

and as a minor point, planck does compile to js, it’s just a self-hosting alternative for OS provided js runtimes

noisesmith22:02:54

so it’s replicating some subset of what cljs.jar does, in js instead of java

mathpunk22:02:22

Okay. I've gathered that "everyone" uses these wrappers instead of the cljs.jar in actual development so I wasn't quite sure what I'd get out of it. I will read it as a base

noisesmith22:02:27

you can literally run cljs.jar with java. get a clojure repl, and output js from cljs forms

noisesmith22:02:11

@mathpunk yes everyone uses the tools and skips the basics, but if you want to understand the nuances between all the various tools, knowing the basics first actually makes that simpler

noisesmith22:02:53

and the cljs.jar api isn’t actually so low level or terrible - it’s just a level or two of automation that the tools add

mathpunk22:02:57

great. I didn't really start getting into Clojure until CIDER got to the point that, provided I had a project.clj, jacking in Just Worked

dpsutton22:02:28

I agree. There should be a simple and standard way to get up and running

mathpunk22:02:28

I think that clj and deps.edn are a sign that the complexity is going to start decreasing soon

mathpunk22:02:48

but since soon isn't now I'm still working out what I should be learning / developing in

eggsyntax14:02:06

I think we're pretty much already there, at least for simple stuff. I can just call clj from the command line, and have a REPL in one second, or pass it a file to run that. See the latest "getting started" instructions at https://clojure.org/guides/getting_started

eggsyntax14:02:24

I'm still using lein for my main project, because the project is already heavily invested in it. But for my next project, I'm gonna at least take a shot at using the approach you can see here: deps.edn and a Makefile. Very clean, very simple. https://github.com/robert-stuttaford/bridge

dpsutton14:02:43

its very easy to get a repl up with that. but i'm a big fan of repl integration with my editor which is less settled with this tool

eggsyntax14:02:27

Oh, absolutely. Although @U064J0EFR’s new rebel-readline may make that less critical 🙂. Having a CLI REPL that acts a lot like an editor would go an awful long way... But I just meant in terms of getting started.

eggsyntax14:02:48

And I have to admit I hadn't read all of the original discussion until just now, and I see I'm off at a bit of a tangent from what y'all were actually discussing 😊

dpsutton14:02:37

no biggie. it's just discussion 🙂 and interesting points are always welcome

noisesmith22:02:29

yeah - but even then, if you eg. want to deploy to a server, knowing how to run clojure via java and a jar makes everything much simpler

mathpunk22:02:32

I have a much greater understanding of the language than I do about all the sausage-making gear that is happening to make it function

mathpunk22:02:49

thanks for the leads y'all!

noisesmith22:02:56

IMHO the tooling surrounding clojure itself isn’t nearly as elegant and well designed

justinlee22:02:09

also @mathpunk just follow up on your comment that you are surprised you need to choose the platform now, note that there is a big difference with doing something like procedurally generated text, which sounds like a pure data transformation, and crafting a webpage that will demo that library.

mathpunk22:02:55

Fascinating. Maybe it's because I'm a mathematician first, software guy second, but I think of my library as "uses strings and rules to make strings" and it just doesn't seem important what kind of runtime the strings are on

mathpunk22:02:55

so, if the webpage is demoing it, I'm imagining it as requiring the library, and putting buttons and text fields around it, exposing the vocabulary so you can change it, that kind of thing

justinlee22:02:14

That’s basically what I’m saying.

justinlee22:02:59

The only thing is if you start to include libraries from JavaScript you’re going to be tied to that platform unsurprisingly.

mathpunk23:02:07

does that mean, there's a choice to make early that supports the library being portable and non-choosy about which runtime?

mathpunk23:02:45

you're saying, I think, that the two JavaScripts, browser and node, are different enough that once I rely on one, I've chosen my runtime already?

justinlee23:02:58

And there are some differences between clj and cljs but it’s possible to write portable code without too much pain I understand.

justinlee23:02:28

No I’m talking about what libraries you use

justinlee23:02:41

Like something from npm for example

justinlee23:02:59

If you are just writing straight clojure you can make it portable so it’ll run on cljs browser or node or clj on the jvm

mathpunk23:02:26

yeah ok, i see

justinlee23:02:40

Taking a look back at this conversation, I think maybe you have already made the decisions between clojurescript (which runs on a js environment) vs clojure (which runs on jvm).

mathpunk23:02:31

that's right --- I've been happy writing Clojure much of the time, but there's this JS library I wanted to experiment with

mfikes23:02:59

Yeah, in your string processing library, you ideally would have no code that is platform dependent. Some “pure” libraries end up being broadly reusable across Clojure and ClojureScript.

mfikes23:02:16

Ahh, yeah, you are using an npm dep, forgot.

justinlee23:02:50

Okay sorry I didn’t get you earlier. If you want to use an NPM module, the problem here is that the different tools do that in very different ways.

justinlee23:02:08

You code won’t depend on that, but your development environment will for sure.

mathpunk23:02:22

yeah when I have my data science hat I generally want to rely on Java, but when I have my art coder hat on, I want to play with some of the zanier JS libraries out there

justinlee23:02:21

I personally, use shadow-cljs, which replaces most of lein and figwheel. I like it because I just use npm to install things and then you import using syntax that is similar to JS. It had node support, but I haven’t used it.

mathpunk23:02:31

Like I know that there's cljc, but I don't know a lot about what makes node and browser js different, other than there are some modules available in node like fs and such

justinlee23:02:59

cljc is about portability between clojure and clojurescript.

mathpunk23:02:01

shadow-cljs has been completely off my radar until this week when I was choosing

mathpunk23:02:05

yeah I know

mathpunk23:02:17

I mean, the equivalent for portability between nodejs and js

justinlee23:02:58

i mean except for dependency management it will be portable

justinlee23:02:19

node has some libraries that don’t work on the browser (e.g., files and processes)

justinlee23:02:29

but the language is the same

justinlee22:02:07

for the latter you need to make some architectural decisions right up front. from what you’ve said so far, sounds like you probably want a luminus type server-side template to work with, which will be good because i have found the clojure tooling to be more solid