Fork me on GitHub
#beginners
<
2021-05-20
>
joeljuca00:05:56

@rob370 did you check Exercism already? It’s both a community and command-line app that provides a learning experience along with a community-powered mentorship program

Rob Haisfield00:05:44

Oooh @joelwallis good recommendation

✌️ 4
joeljuca00:05:22

Exercism is awesome! I’ve even contributed to the JavaScript and the [now dead] ECMAScript track in the past. It’s really a master piece of self-learning platforms. I’ve been on it for JS, Elixir, Erlang, Python, Ruby, and other languages. Lots of fun is mostly guaranteed! 😆

pithyless07:05:09

@joelwallis there is an #exercism channel where people contributing to the Clojure track hang out

👍 4
Rob Haisfield01:05:33

How can I make this code return strings rather than individual characters?

Michael Stokley01:05:53

what should the output look like?

Michael Stokley01:05:54

is this right?

(let [car-names ["audi" "bmw" "fiat" "haval"]
      car-models ["" ["x1" "i3"] "500" ""]]
  (map vector car-names car-models))
;; => (["audi" ""] ["bmw" ["x1" "i3"]] ["fiat" "500"] ["haval" ""])

Michael Stokley01:05:13

but i would adjust car-models to be [[] ["x1" "i3"] ["500"] []], if possible

Michael Stokley01:05:32

which gets you

(let [car-names ["audi" "bmw" "fiat" "haval"]
      car-models [[] ["x1" "i3"] ["500"] []]] ;; empty list instead of empty string
  (map vector car-names car-models))
;; => (["audi" []] ["bmw" ["x1" "i3"]] ["fiat" ["500"]] ["haval" []])

Michael Stokley01:05:58

or even

(let [car-names ["audi" "bmw" "fiat" "haval"]
      car-models [[] ["x1" "i3"] ["500"] []]] ;; empty list instead of empty string
  (->> (map vector car-names car-models)
       (into {})))
;; => {"audi" [], "bmw" ["x1" "i3"], "fiat" ["500"], "haval" []}

Rob Haisfield02:05:24

This is the desired output ; desired output of the function = “Audi, BMW X1, BMW i3, Fiat 500, Haval”

Rob Haisfield02:05:46

I’m going to try the empty lists

Michael Stokley13:05:21

(let [car-names ["audi" "bmw" "fiat" "haval"]
      car-models ["" ["x1" "i3"] "500" ""]]
  (->> (map (fn [car-name car-model]
              (cond
                (= "" car-model) [car-name]
                (coll? car-model) (map (fn [model] [car-name model]) car-model)
                :else [car-name car-model]))
            car-names car-models)
       flatten))
;; => ("audi" "bmw" "x1" "bmw" "i3" "fiat" "500" "haval")

Ivan Koz14:05:34

map would fit better for that case

(def cars {"Audi" []
           "BMW" ["X1" "i3"]
           "Fiat" ["500"]
           "Haval" []})
           
=> #'user/cars
(for [brand (keys cars) 
      model (or (seq (cars brand)) [""])]
  (str brand (when (seq model) " ") model))
=> ("Audi" "BMW X1" "BMW i3" "Fiat 500" "Haval")

noisesmith15:05:22

alternative to looking up the keys is grabbiing keys and vals together:

(for [[brand models] cars
      model (or (seq models) [nil])
      :let [model-str (if model
                          (str " " model)
                          "")]]
   (str brand model-str))
(also used let in an attempt to aid readability)

Jacob Rosenzweig03:05:56

Is a threading macro basically equivalent to ocaml piping?

sova-soars-the-sora14:05:59

Yes I think so. Note that Clojure has thread-first -> and thread-thru ->>

noisesmith15:05:09

@U02313K7TSL the important difference is that OCaml piping is semantic, and clojure threading macros (as all macros) are syntactic - a pipe creates a series of applications, a threading macro does a code rewrite to be more concrete, OCaml piping can't do this:

user=> (->> (+ a b) (let [a 23 b 19]))
42
it also doesn't have this commonly seen newb trap error:
user=> (macroexpand '(-> y (fn [x] (+ x x))))
(fn* y ([x] (+ x x)))

noisesmith15:05:59

lists of symbols that are not yet compiled is a lower level, more flexible, and more error prone domain compared to the operatioins the pipes do the macro rearranges symbols in lists, the pipe operator does higher order application and value capture

noisesmith15:05:45

I think it's good style to use the clojure threading macros as if they were semantic, but you can't really use them fluently without understanding they are syntactic

Luciano Laratelli03:05:01

hi! is this the appropriate channel for newbie cljs questions, or should that be in #clojurescript? 🙂

dpsutton03:05:38

Here’s fine.

valerauko15:05:36

anyone knows if doing the tonsky-way formatting in atom is possible? (https://tonsky.me/blog/clojurefmt/ ) i tried tweaking the paredit plugin's regex but no luck...

Juλian (he/him)09:05:29

Off topic: That site has an awesome dark mode!

👍 2
MikeE15:05:13

hello! i have a question regarding structuring a part of an application I’m working on. It may not actually be a beginner question but Clojure is new to me so feel free to point me to another channel 🙂 I’m developing a REST API and have a need to run a worker process/thread long polling SQS for messages continuously and I wasn’t sure where to put something like that in my codebase nor how to kick off the process so it’s not blocking anything. Is there any code samples possibly or ideas on how to structure this and what part of the stdlib may help with this. FWIW I’m starting project this as a monolith. I have a lot of experience with building micro services but this is a solo project and micro services don’t feel appropriate at this stage of development.

MikeE15:05:16

Also if this question is too complex to answer in Slack I’m happy to post on clojureverse

noisesmith15:05:59

@mike741 the answer that's usually good enough for your first draft is to use future , which propagates bindings for dynamic vars, starts your code in a new thread (in an expandable thread pool) and returns a handle you can use to check exit or error status, or even cancel it if it calls cancellable methods like Thread/sleep periodically.

Darin Douglass15:05:59

^ in general it depends on what you're wanting to do, but that suggestion is a good starting point

noisesmith15:05:53

user=> (def f (future (loop [] (println "looping") (Thread/sleep 1000) (recur))))
#'user/f
user=> looping
looping
looping
looping
looping
looping
looping
looping
looping
looping
(future-cancel f)
true

noisesmith15:05:24

the threading mixes up the program output and my input here, but it shouldn't be too hard to figure out

noisesmith15:05:17

do note that future-cancel is opt in - you need to call something that respects that it should abort if the current thread is cancelled (the jvm has code that isn't cancellation safe, so the method that actually no questions asked kills a thread isn't safe to use)

MikeE16:05:10

ok cool thank you for that pointer.. that seems pretty straightforward.. I’ll check that out.. Much appreciated!

noisesmith17:05:36

for more advanced usages there are libraries like claypoole or the built in java executor service

valerauko17:05:12

personally i find manifold very easy to use

👍 2
MikeE17:05:45

my use case is pretty simple.. poll SQS for messages, on receipt of message(s) loop through them and do a database update.

MikeE17:05:34

I just wasn’t sure which part of the stdlib I should start with to get a thread running out to the side continuously in order to support polling the queue. thanks again all!

piyer20:05:43

How do I turn on the sql debugging with seancorfield/next-jdbc ?

seancorfield21:05:32

@munichlinux What do you mean by “sql debugging”?

piyer21:05:00

I am trying to log the sql statements.

seancorfield23:05:38

There’s currently nothing built into the library for that. For execute! and execute-one! you could write a wrapper that logged the sql-params vector.

seancorfield23:05:01

In theory you could write something like next.jdbc.default-options that lets you wrap a connectable in something that would log the sql-params prior to calling the implementation.

seancorfield23:05:23

There are some interesting edge cases: what should you do for a prepare call? It doesn’t actually run the query, it just builds a PreparedStatement, and in general you can’t get the actual SQL back out of those in a generic JDBC way. What about plan which doesn’t do anything until it is reduced? What about sensitive data being passed in as parameters?

seancorfield23:05:36

Logging result sets is even more fraught since they can be arbitrarily large — and for plan they are not really exposed since the whole point is to reduce over the ResultSet, iterating across it without even producing Clojure data structures.

piyer00:05:05

yup, I wrote a macro with logging.

seancorfield00:05:58

If you want to see what a variant of default options would be for logging:

(! 1574)-> clojure -M:rebel:test:dev/repl
Downloading: com/bhauman/rebel-readline/maven-metadata.xml from clojars
Selected port 61666 for the Socket REPL...
Starting Rebel Readline as the REPL...
[Rebel readline] Type :repl/help for online help info
dev=> (require '[next.jdbc :as jdbc] :reload-all)
nil
dev=> (def db-spec {:dbtype "h2:mem" :dbname "example"})
#'dev/db-spec
dev=> (def ds (jdbc/get-datasource db-spec))
#'dev/ds
dev=> (def lds (jdbc/with-logging ds println))
#'dev/lds
dev=> (jdbc/execute-one! lds ["create table foo (name varchar(32))"])
execute-one! [create table foo (name varchar(32))]
#:next.jdbc{:update-count 0}
dev=> (require '[next.jdbc.sql :as sql])
nil
dev=> (sql/insert! lds :foo {:name "Sean"})
execute-one! [INSERT INTO foo (name) VALUES (?) Sean]
nil
dev=> (sql/insert! lds :foo {:name "Piyer"})
execute-one! [INSERT INTO foo (name) VALUES (?) Piyer]
nil
dev=> (sql/find-by-keys lds :foo {:name "Piyer"})
execute! [SELECT * FROM foo WHERE name = ? Piyer]
[#:FOO{:NAME "Piyer"}]
dev=> 
https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc/sql_logging.clj

seancorfield00:05:27

That’s available on develop as an experiment, if you want to try it out via :git/url.

piyer05:05:56

nice! does that log via log4j2 or simple stdout?

seancorfield05:05:33

@munichlinux You supply the logging function(s) so it can do whatever you want.

seancorfield05:05:51

(I’ve updated it a bit since the post above — see my note in #sql)