Fork me on GitHub
#beginners
<
2019-10-09
>
tvalerio02:10:47

Hey! I’m reading Programming Clojure and there’s this snippet: “Clojure is high signal, low noise…” I’m having a hard time to understand this. Where can I be familiar with these terms? What’s the definition of a high signal and low noise language?

hiredman02:10:24

It is a figure of speech meaning the amount of code it takes to perform some action is very small with very little outside what is minimally required

hiredman02:10:21

It is an analogy to https://en.m.wikipedia.org/wiki/Signal-to-noise_ratio where for example in Java to print something out, the "signal" might be System.out.println("foo") and the "noise" would be the class and method around it

tvalerio02:10:16

Oh, I get it. Thanks a lot @hiredman

zav03:10:44

anyone have an upsert function for mysql on their shelf by any chance?

seancorfield03:10:12

@mzavarella It's going to depend on how you identify your primary key, but what I've seen is to run an update! based on the PK and if that returns zero rows updated, do an insert! with the PK assoc'd into the data -- but you really need to wrap the insert! in try/`catch` in case another thread also attempts the insert (and this is assuming you have a unique constraint on your PK so a second insert will fail), and if the insert fails, because of that constraint, you need to retry the upsert.

zav03:10:43

awesome, thanks Sean!

zav03:10:00

Didn't even think to use try/`catch`

seancorfield03:10:52

Reading around, it looks like you might be able to just do INSERT ... ON DUPLICATE KEY UPDATE but I'm not sure how that would work with auto-generated PKs...

seancorfield03:10:39

(non-standard SQL caveats, not available in all versions of MySQL caveats, etc, etc)

seancorfield03:10:48

If you have non-primary (keys) only, you can still do the update-or-insert approach (assuming auto-gen'd PK).

zugnush03:10:05

Anyone played with claypoole? I have a pool of processes doing http document uploads. They're all using a token stored in an atom, when the token expires I'd really like one Thread to know that it has the job of updating the token, I guess it won't do any really harm for them all to update the atom but it just doesn't seem right, should I bracket the token check/update in some other way?

seancorfield03:10:10

@zugnush Perhaps you could push the check/update into an agent so that piece of work would be single threaded?

zugnush03:10:27

thanks Sean, I think might be what I need, I had it in my head that i needed to check the thread id somehow but that sounds much better. Ta. Loving jdbc-next btw.

seancorfield03:10:05

Cool. Best channel to ask Qs about next.jdbc is #sql to make sure I see it. Always happy to hear feedback on it (and on its docs).

fappy05:10:21

Quick spec question…

(require '[clojure.spec.alpha :as s])
(require '[clojure.test.check.generators :as gen])
(s/def ::non-map #(not (map? %)))
(gen/generate (s/gen ::non-map))
… results in an interesting error in my environment:
1. Unhandled clojure.lang.ExceptionInfo
   Spec assertion failed.

         Spec: nil
        Value: nil

     Problems: 

                 alpha.clj:  282  clojure.spec.alpha/gensub
What am I doing wrong?

andy.fingerhut05:10:27

I am taking a stab in the dark here, but I suspect that the built in generators do not handle the case where you have a function you wrote (`#(not (map? %))` in this case) as the spec predicate. Probably you would need to write your own generator function for such a spec.

Lennart Buit06:10:21

I think that is correct: (s/gen #(not (map? %))) says that its unable to construct a generator 🙂

Lucy Wang13:10:46

Hi, I learned some (very little) clojure before, and am in the progress of relearning it. I was comparing its expressiveness with some other languages (python and go), but so far I don't find it that more expressive as people claimed. Let's take 4clojure problem 41 for example:

;; Drop Every Nth Item - Easy
;; Write a function which drops every Nth item from a sequence.

Lucy Wang13:10:03

Here is my clojure version

(defn to-vector [& rest]
  rest)

(defn keep-index? [n [index _]]
  (not
   (zero?
    (mod (inc index) n))))

(defn my-drop-every [coll n]
  ;; Use transduers to transform the collection
  (into []
        (comp (map-indexed to-vector)
              (filter (partial keep-index? n))
              (map second))
        coll
        ))

Lucy Wang13:10:24

but it's much longer than python version:

def my_drop_every(coll, n):
    return [x for idx, x in enumerate(coll)
        if (idx + 1) % n != 0]

Lucy Wang13:10:52

and even the go version is not so bad

func MyDropEvery(coll []int, n int) []int {
	newcoll := make([]int, len(coll))
	for index, i := range(coll) {
		if index % n != 0 {
			newcoll = append(newcoll, i)
		}
	}
	return newcoll;
}

Lucy Wang13:10:14

am I not getting the idiomatic way to do it in clojure? Thx!

rpkarlsson13:10:47

(defn drop-every
  [n coll] 
  (mapcat drop-last (partition n coll)))

(drop-every 4 (range 32))
;; => (0 1 2 4 5 6 8 9 10 12 13 14 16 17 18 20 21 22 24 25 26 28 29 30)

rpkarlsson13:10:10

need to consider inputs thats not equaly divided by four though

danielneal13:10:33

partition-all?

Lucy Wang13:10:03

Thx! Take a while to reason about it. It's clever to first partition then drop-last. Is this the idiomatic way to write clojure code?

rpkarlsson13:10:47

It's how I would break down the problem. It's not complete and har some flaws with it. As @danieleneal says partition-all is a better tool. Something like

(defn drop-every
  [n coll]
  (->> (partition-all n coll)
       (mapcat (partial take (dec n)))))

Should give a more robust solution

👍 4
Aniket06:10:37

@UP90Q48J3 You can follow people who are good at Clojure on 4clojure and compare your solution to theirs.

Aniket06:10:09

4clojure shows this note once you have solved the problem

Lucy Wang11:10:45

@UHHG094TS thx! Yeah that's sooo helpful.

bronsa13:10:37

(for [[i el] (map list (rest (range)) cool) :when (not (zero? (mod i n)))] el)
is pretty much the same as your python version

Lucy Wang13:10:33

Thx! However I'd say it's much less readable than the python version..

andy.fingerhut13:10:51

Do you find it any more readable when split over multiple lines?

(for [[i el] (map list (rest (range)) cool)
       :when (not (zero? (mod i n)))]
  el)

andy.fingerhut13:10:05

(assuming I can do Slack formatting correctly)

bronsa13:10:19

that's a matter of familiarity :) it took me longer to figure out how to read your python version than rewriting it in clojure

andy.fingerhut13:10:45

If you consider that is much less readable than the Python version, then I would wonder how long you have been programming in Python vs. how long you have been trying Clojure. Familiarity does take some time.

Lucy Wang13:10:38

Thx! I have been using python daily for years. That make sense guys -- I guess I was in the typical trap of "Simple made easy"

mg13:10:11

also possible, (apply concat (partition-all (dec n) n coll))

Lucy Wang13:10:07

This is like magic to me! Would you really write this kind of code in a production team project? Not offense intended.

tschady13:10:17

everything is magic until you understand it

mg13:10:34

Probably not, as I try to avoid apply and concat for performance reasons, unless I know that the size of the collection is fairly bounded

danielneal13:10:32

(defn drop-every [n coll]
  (mapcat (fn [x i]
            (when (not= (mod x n) 0)
              [x]))
          coll
          (range)))

Lucy Wang13:10:49

I like this version! And (not= x 0) is better than (not (zero? x). Didn't know mapcat would ignore nil in the flatten step

danielneal13:10:49

yep @U060FKQPN could explain better but nil will work like an empty sequence, so can be used in concat etc

jaihindhreddy13:10:38

You can use function literals to make it even more concise,

(defn drop-nth [n coll]
  (mapcat #(when (not= (mod %2 n) 0) [%1])
          coll
          range))
Clojure is just as concise as Python IMO with far less syntactic tricks (fn literals being one of them) making it more readable at same levels of familiarity.

👍 4
Lucy Wang13:10:24

Thx @U883WCP5Z! This is really cool example.

mg14:10:59

Also can be done without mapcat and the extra collections which are expensive over large inputs

(->> (map (fn [i x]
            (when-not (zero? (mod (inc i) n))
              x))
          (range)
          coll)
     (filter some?))

mg14:10:44

also pos? is possible rather than when-not (zero? ...

jaihindhreddy14:10:35

That being said though, concision is not really a core value prop of Clojure. Simplicity, Power and Focus are Clojure's real strong suits.

danielneal13:10:46

the partition version is neat, both are idiomatic approaches imo

mg13:10:44

also possible, (keep-indexed (fn [i x] (when-not (zero? (mod (inc i) n)) x)) coll)

❤️ 4
danielneal13:10:41

One thing I’d say is that for this type of thing, there’s not really that much to choose Clojure over any other language. The python solution here is definitely more readable than most of our suggestions. I think the advantages of Clojure imo becomes more apparent once you get into exploring and working with data from databases, apis etc. When most things are data, and all libraries are built around the assumption that you’ll be working with data.

8
👍 4
Lucy Wang13:10:54

I think the keep-index version by @michael.gaare is on the same level as the python version. > I think the advantages of Clojure imo becomes more apparent once you get into exploring and working with data from databases, apis etc. When most things are data, and all libraries are built around the assumption that you’ll be working with data. That's the part I don't quite get yet. I heard so many of people saying things like this on reddit or in the clojure conference videos. Can you give me a concrete example?

danielneal14:10:36

This is a good question; I guess I developed my own opinions about this over a period of time so don’t have a good concrete example to hand. I’ve been on a groovy project recently, however, and definitely miss 2 things from Clojure very much: 1 - the fast feedback from an in-process repl (vs compiling and running) and 2 - in idiomatic Clojure you are usually dealing with maps and vectors that can be explored quite easily rather than objects that need to have methods called on and can inherit behaviour and mutate state

Lucy Wang14:10:07

I see, it may need to take a while for me to understand why less object/classes would be a benefit, because I don't really get troubled by them in python - most of the time I can inspect the fields of an object like a breeze. i.e. it's just like a map. Regarding the REPL - yeah it's really the killer weapon combined with the s-exp syntax. Python also has a powerful REPL, but I hardly could send code to REPL directly because it's tedious to select the code region in the editor.

danielneal14:10:19

How do you feel about mutability/immutability?

Lucy Wang14:10:40

Tbh that's also something I'm exploring. Again I see people claim things like "immutability is superior" , but in my years of python dev experience I don't find mutability biting me at all ...

danielneal14:10:29

It sounds like you’re pretty happy with python 😄

danielneal14:10:50

I think it makes most sense to use a language you feel happy with - perhaps we all get bitten by different things and that affects what we look for

👍 4
Lucy Wang14:10:24

Yeah I am indeed happy with python, I went to clj mainly but I'm trying to write some react-based app and decide to go with CLJS to get some fresh air in the functional programming land

danielneal14:10:41

I think there’s a 10 years of Clojure talk where Rich talks about what the kind of programs Clojure was designed for - it’s certainly not for everyone, or for everything

danielneal14:10:10

hahah react is a whole other world of things to think about 😄

Lucy Wang14:10:42

right, that talk is really great. I watched last week and still remember the graph that shows "domain complexity and misconception" could cost you 10x than choosing whatever language. So true and so verified in my career.

danielneal14:10:38

it’s quite refreshing to realise that language choice is really a small part of the whole equation, I think as programmers we can get caught up in it sometimes

danielneal14:10:03

the language of the system is quite a nice talk in that respect too

Lucy Wang14:10:26

yeah, people can build great products with perl or C, which some young programmers treat as "rabbish language"

Lucy Wang14:10:55

btw I have posted this question about the power of "everything is data" in reddit https://www.reddit.com/r/Clojure/comments/dfhtcp/about_its_all_data/

Lucy Wang14:10:33

need to go now. Thanks for your help! ttyl 🙂

danielneal14:10:58

np, that’s a good reddit question, well worded & should hopefully get some good responses by people wiser than me 😄

macgyver14:10:35

Hey all, new to clojure, coming from node 👋. I am trying to use vscode with some goodies (autocompletion, linting, etc.) and then run that file with repl. On each save, re-run this file. This way I get quick feedback loop and can work in familiar environment instead of in terminal. Does this make sense? If not, what are some alternative methods?

Dmytro Bunin14:10:53

check out #vscode

Dmytro Bunin14:10:02

I mean #calva-dev

calva 8
macgyver14:10:25

@manutter51 yes, I have, but I would like to read more about this. Being that I have several layers of new (syntax, language, environment, etc.) trying to have some familiarity so I can make progress learning the language

Lucy Wang14:10:40

setting up a handy REPL would be of HUGE help and a powerful tool to help you climb the mountain of clojure

macgyver14:10:32

ok! and just to make sure i understand what you are saying, this working in the REPL implies that I am writing my code in the terminal, right?

Lucy Wang14:10:03

nope. It's integrated to your fav IDE, be it Emacs/Intellij/VSCode

Lucy Wang14:10:39

The moment I fell in love with clojure is when I find the power of structural editing combined with real-time eval in REPL

Lucy Wang14:10:42

I came from python, python also has an awesome REPL (ipython, which was the inspiration of the famous jupyter notebook). But the thing that really makes Clojure's REPL powerful is you can send the last s-exp to the REPL with one key, due to the sytnax

andy.fingerhut14:10:20

In Python, do you ever use a REPL to evaluate code inside of a running application? That is something that people sometimes do with Clojure and other (multi-threaded) Lisp systems.

andy.fingerhut14:10:44

most often for examining state for debugging, less often for modifying the state of the application

manutter5114:10:37

There’s a link to a good video on that github page, worth a look if you haven’t seen it before.

👍 4
pez14:10:40

I suggest using Calva (since I am maintaining that and since you use vscode) and use it's in-editor REPL for getting quick feedback on your code. So, write code inside (comment ...) forms and use Calva's commands for evaluating current form and current top level form to examine the results of stuff inside those comment forms. It's in the Calva README: Something to try first.

👀 8
Dmytro Bunin14:10:06

Also if you want to get the feel of the repl, you can check out (night-code)[https://sekao.net/nightcode/], but I would just go with calva instead

Luke Johnson16:10:43

I love nightcode! It is perfect for a beginner looking to toy around with Clojure. The Insta-REPL feature is awesome and the lack of functionality minimizes distractions with keyboard shortcuts. If nightcode had git functionality, similar to VS Code, and a shortcut to comment out lines of text, I’d probably use it exclusively. Since it doesn’t, I’m using Calva on VS Code for my main text editor.

Ashutosh Soni19:10:33

@here i am having 2 years of experience in clojure & clojurescript, can anyone help me in getting remote work or full time work?

andy.fingerhut19:10:16

There are #jobs and #remote-jobs channels on Slack where those things are discussed.

andy.fingerhut19:10:07

http://clojurians.zulipchat.com has a longer history of messages, copied from Slack, for those channels.

hendore23:10:59

I’m putting together a simple use case in a demo web app I’m using to learn clojure. I have a req handler for account registration, when this handler runs I want to insert a new row in the database and then send a welcome email. So far I have this https://gist.github.com/hendore/2d8686fddcec2342688372b300d811e6#file-registration-clj however it’s left me with so many questions, I’m certain there are obvious answers to those that have had more exposure to clojure but I feel a bit lost on where to go next to solve a few things. 1: If the send-welcome-email! function was implemented and actually sent an email, I wouldn’t want this to happen before returning a response, in other languages I would dispatch an event / add a job to a queue to run later to send the email, what is the clojure way for doing this sort of thing? 2: It’s possible that the insert-account! function will throw PSQLException what’s the standard practice for handling this and returning a nicer error back to the request handler function to be given to the user, try/catch maybe? If so where would be the best place to catch the exception and how would I handle that in the request handler to return a nice message? 3: I would like to hash the plain text password before inserting the account, it makes sense for me to do that in insert-account! should I use something like the threading macro to assoc or update password in the account map with a hashed password

(let [account (-> account-info
                  (select-keys [:name :email :phone :password])
                  (update :password hash-plaintext-password))]
  (…))
4: What about other validation before saving, such as ensuring the password is secure (length, characters, etc...) or making sure name isn’t nil? I’m sorry about the long post / several questions, I’m just hoping I can learn these little things that I will need to be able to implement other features going forward.

seancorfield23:10:19

There's a lot to unpack there and the answers to most of those questions is "it depends".

hendore23:10:20

just looking for some pointers / links to go do some further investigation more than a step by step, but I understand there’s a lot being asked here 🙈

seancorfield23:10:20

1. You want send-welcome-email! to run after successful completion of the registration "work". It doesn't really matter if it happens before the handler returns or not, as long as it happens after successful registration. I would probably have something like

(when success ; whatever that is
  (future (send-welcome-email! ,,,)))
so the sending process doesn't hang up the function response (to the user).

seancorfield23:10:43

But you could do it via an agent or a queue or whatever you're used to.

hendore23:10:51

Perfect, just brought up the future and agent docs this is great :thumbsup:

seancorfield23:10:32

2. Yes, try/`catch` is the right choice here -- exceptions are idiomatic in Clojure because they are idiomatic in the host language.

hendore23:10:41

agents look very similar to atoms :thinking_face:

hendore23:10:51

As for the try/catch would it be best to place that in the function that could potentially throw the exception insert-account! and handle it there, maybe return [:error "Email is already in use"] or would you let that exception bubble up and handle it in the register-account! or registration-req-handler functions?

seancorfield23:10:07

3. looks fine (but you probably want to seed the hash, perhaps with the email or something)

seancorfield23:10:49

4. There are lots of options for validation. Too complicated for me to type out right now.

hendore23:10:41

No worries, thanks again for your input, you’ve been really helpful the last couple of days I really appreciate the time you’ve taken to help

seancorfield23:10:34

Re: returning errors -- consider keywords at the lower level (`:ok`, :email-in-use) and turn them into user-facing strings closer to the user.

hendore23:10:38

Ah, that makes better sense

hendore23:10:14

Is this the idomatic way in clojure for returning success / errors ?

seancorfield23:10:36

It depends 🙂

seancorfield23:10:47

How much information do you need to convey to callers

seancorfield23:10:31

You can use keywords, or tuples, or maps, depending on how much information you need to convey back to the caller.