Fork me on GitHub
#beginners
<
2019-02-05
>
gisf00:02:16

I agree. But, you know that programmers likes a challenge. To me has been a big challenge start with clojure and learn how to work with emacs at the same time. Itโ€™s hard but i like it

nbtheduke01:02:40

Iโ€™ve been listening to a great podcast called Coding Blocks, just listening to their series on the book Clean Architecture, which is very C-descendent focused, and it made me wonder: are their any resources out there for Clojure that tackle similar concepts?

dmytro.bunin01:02:58

Iโ€™ve watched this video (https://www.youtube.com/watch?v=0EX3UIl-Sd8) some time ago, which kinda talks about using Clean Architecture when using clojure

nbtheduke02:02:24

Awesome, thanks! Iโ€™ll give that a watch

mitchell_clojure01:02:56

Hi all, cider has been giving me the following error on blank projects/lein profiles and I am not quite sure how I am supposed to figure out what the culprit is. I would appreciate any help or nudges in the right direction. Thanks!

seancorfield01:02:13

@mitchell_clojure It's just a warning so you can ignore it. If you ask in #cider they might be able to help you make it go away.

seancorfield01:02:58

(it's a standard warning from the slf4j Java logging library that appears when you don't have certain other libraries on your classpath)

mitchell_clojure01:02:48

Thanks, Sean. Really appreciate it.

charlesg301:02:29

Include this require and it goes away: [org.slf4j/slf4j-nop "1.7.25"]

mitchell_clojure02:02:18

@charlesg3 That seems to have done the trick. Thanks a ton. Is this an isolated issue somehow? Or well documented anywhere? I did some searching on github but maybe I was not self-reliant enough.

mathpunk21:02:23

@mitchell_clojure If I understood the way it was explained to me when I had this problem: any time you rely on a library that uses the slf4j logger, you'll get that message, because it's warning you that no one told it what code to run for logging. Including slf4j-nop tells it, explicitly, what you want to happen is a no-op, i.e., to do nothing. (Which is what it defaulted to, but it warned you it was doing so.)

charlesg302:02:52

No idea. I just experienced the same issue on several projects, did some searching... found that solution and use it on all projects that use logging.

mitchell_clojure03:02:07

Interesting. Thanks for the help and have a great night!

caleb.macdonaldblack05:02:56

Any strategies for avoiding symbol name clashes instead of using synonyms. For example for symbols like update, get, name ect

seancorfield05:02:16

Do you mean as top-level definitions or as local symbols in let?

caleb.macdonaldblack05:02:51

Well as a general rule of thumb I thought it was a good idea to avoid using built in symbols altogether. Solutions for both ideally

caleb.macdonaldblack05:02:18

I find myself thinking about this whenever I write code to do crud operations as update is taken

seancorfield05:02:23

For top-level definitions, there's no problem really. If the ideal name for something matches a clojure.core function, you can still use it in your namespace. You can use :refer-clojure :exclude [...] in ns to avoid pulling in the matching core functions -- which also acts as documentation that you plan to override them -- and them you can refer to the core versions with clojure.core/<function> if you need to. It's recommended to put your version of such functions near the end of your file so you are less likely to use them by accident (instead of clojure.core/<function>). That's why we have namespaces.

seancorfield05:02:16

For local symbols shadowing clojure.core functions, that can be okay too -- again, if that's the obvious name to use -- and linters like Eastwood should be able to warn you that you're doing that (using Eastwood is a good idea anyway!).

seancorfield05:02:05

In clojure.java.jdbc, I use update! rather than update since it involves a side-effect on an external resource so it (potentially) would not be safe in a transaction. I tend to use ! to indicate side-effects at the edge of the system -- although that library has query rather than query! even tho' it could return different results at different time.

caleb.macdonaldblack05:02:47

So it's essentially a trade off between the occasional confusion caused by overriding core functions and the confusion of choosing a poor alternative symbol name?

seancorfield05:02:57

Zach Tellman's excellent "Elements of Clojure" has plenty to say on this topic -- highly recommended.

seancorfield05:02:07

Yes, exactly that trade off.

caleb.macdonaldblack05:02:35

Thanks for your help ๐Ÿ™‚ I checkout "Elements of Clojure" now

seancorfield05:02:42

From Zach's book

If these functions are in a namespace specific to payloads, they can simply be called get, delete, and compress-and-get. We can assume that other namespaces will refer to our namespace with a prefix, such as payload/get or p/get. This means that shadowing Clojure functions like get is safe and useful, but we should take care to specify this at the top of our namespace:

seancorfield05:02:13

(again, why we don't use :use or :refer :all in most cases -- and prefer :as instead)

caleb.macdonaldblack05:02:00

Yea I very rarely refer to a symbol without prefixing with a namespace alias (using :as) for that reason.

seancorfield05:02:23

(aside: I'm curious as to where it is 4pm right now?)

caleb.macdonaldblack05:02:42

Queensland Australia :flag-au:

seancorfield05:02:53

Ah, I have Sydney as 5pm on my world clock and expected all of Eastern Australia to be on that TZ... am I incorrect?

seancorfield05:02:41

Oh, you're west of that?

caleb.macdonaldblack05:02:52

Sydney & NSW is on daylight savings. I'm pretty sure none of Queensland is

seancorfield05:02:54

(sorry, my Australian geography is poor)

seancorfield05:02:14

TIL! Yeah, just looked that up. You stopped doing DST in 1992. Very smart of you. Wish we'd do it here in California! ๐Ÿ™‚

lennart.buit07:02:31

You will never get rid of your timezone troubles. I live in The Netherlands, where we used to have a offset of +00:19:32 until 1937, thats bugging me in code sometimes ๐Ÿ˜›

lennart.buit07:02:34

(for example, when someone tries to be funny and put an epoch on 01-01-1900 at 00:00 local time)

caleb.macdonaldblack06:02:26

Yea. Daylight savings seems to just confuse things to me.

caleb.macdonaldblack06:02:44

Ah so Queensland is AEST, and everything else on the east is AEDT

seancorfield06:02:51

Back to your question, I just looked in our codebase and send, update, and get are the most common names we override of the core functions.

seancorfield06:02:06

We also have one namespace that overrides find and reset!, and another that overrides set!, and one that overrides +, -, and *

caleb.macdonaldblack06:02:49

That's good to know that others do that. I've just avoided it entirely as when I've done it in the past, it was incredibly confusing to debug without realising it's overridden. Although its one of those things where once you've done it your on the lookout when your debugging similar issues in the future.

caleb.macdonaldblack06:02:07

I'd much rather use the correct name though

seancorfield06:02:50

You usually get warnings when you override at the top-level but not local let bindings. I've certainly shot myself in the foot by shadowing name and key and a few others in let... ๐Ÿ˜ž

caleb.macdonaldblack06:02:02

I made some wrapper functions for promises in cljs and it drove me nuts not using resolve. I actually just used p-resolve instead but always hated it

seancorfield06:02:27

resolve on a promise? Interesting.

caleb.macdonaldblack06:02:24

(.-resolve promise-obj)

caleb.macdonaldblack06:02:59

I got deref confused with resolve

seancorfield06:02:40

Interesting that you seem happy to overload namespace aliases (e.g., o) with local variables (argument o) ๐Ÿ™‚

caleb.macdonaldblack06:02:10

As I understand it they don't clash

caleb.macdonaldblack06:02:20

But I guess it is kind of strange

seancorfield06:02:36

They don't, and lots of people do that. But given you asked about strategies to avoid clashes... ๐Ÿ™‚

seancorfield06:02:33

(and it is extremely common to alias clojure.string to str which of course matches a core function!)

caleb.macdonaldblack06:02:50

Yea I've used that too

caleb.macdonaldblack06:02:49

I was more accepting of that because it didn't clash and require debugging. I'm not sure I've ever been confused with ns alias vs symbol. However I've had tough times debugging symbol clashing with symbol

caleb.macdonaldblack06:02:02

It always happened with name

seancorfield06:02:18

Yeah, name is a common source of clashes I think.

caleb.macdonaldblack06:02:56

and I destructure a lot too so namespaced keywords didnt really help {:keys [foo/name]} makes name available

caleb.macdonaldblack06:02:38

destructuring was my primary source of clashes as apposed to let. As it's easy to rename a symbol in let to something else. Destructuring isn't as simple you either need to name the key something else, or use alias {foo-name :foo/name}

caleb.macdonaldblack06:02:52

Which means it's not as elegant as keys ๐Ÿ˜ž. However Ill override from now on when in makes sense

seancorfield06:02:14

Even with a local name binding, you can still always use clojure.core/name to disambiguate. But, yeah, it takes care (use Eastwood!).

caleb.macdonaldblack06:02:20

yea hence the only real downside to overloading is understanding code and debugging a unknown clash

abiduzair42017:02:56

Hello Clojurians! I want to learn Clojure by building projects or solving a problem or writing a compiler. Although I am familiar with basic syntax but I never got to building anything. I tend to appreciate a language and learn more by building. Can anyone please recommend any free online course(prefer videos) or beginner level Github repo which can be used as a reference.

borkdude17:02:12

@abiduzair420 maybe @plexusโ€™s https://lambdaisland.com would suit your need.

borkdude17:02:41

(sorry, not free)

dpsutton18:02:28

there are a few free ones and the quality seems quite good

mario.cordova.86218:02:36

How to require a cheshire in my repl, started by lein repl? This is being run outside of a project

dpsutton18:02:04

also tim baldridge has some videos that are fantastic

borkdude18:02:12

@mario.cordova.862 but you have the dependency on your REPLโ€™s classpath?

chase-lambert20:02:49

Thank you for linking this! I can't afford Lambda Island or Purely Functional tv yet but have never come across this one.

borkdude18:02:00

@dpsutton did you watch all of them? Iโ€™m still curious why he says โ€œwhy not use HOFsโ€

mario.cordova.86218:02:24

Ill just start one within a project.

dpsutton18:02:26

i haven't seen all of them no. but the few that i have are excellent

dpsutton18:02:01

i haven't seen that one. i think i still have a membership. it's crazy reasonable like $3 a month or something

borkdude18:02:11

Thereโ€™s also this paid one: https://purelyfunctional.tv/ by @ericnormand

dpsutton18:02:30

> Clojure is a functional programming language, so we should use Higher Order Functions all the time right? Maybe not, let's talk about the downsides.

borkdude18:02:01

yes, I got that, but what downsides? maybe related to errors and functions names

dpsutton18:02:21

i'm intrigued. (was putting it there for others)

dpsutton18:02:14

yes i'm still a member. It's 3.99 a month. highly recommended

noisesmith18:02:04

off the top of my head, higher order functions capture the value of a var, so don't see redefs (unless you explicitly use the var...)

noisesmith18:02:30

(fn what-it-does [y] (foo x y)) is better than both - it also has a readable name in a stack trace

borkdude18:02:28

who reads stack traces (kidding)

noisesmith18:02:15

not me, I never get stack traces because my code is perfect

abiduzair42018:02:39

Yeah it is not free. This has been an issue in my Clojure learning journey. I understand they do it for a living but I wish there was one full-fledged course

borkdude18:02:12

There are free books online.

dpsutton18:02:24

plexus and tim both have free subsets of their videos. clojure for the brave and true is the entry for a huge portion of professional clojurists and is free online

abiduzair42018:02:01

Alright I shall try the brave book. Thank you @borkdude @dpsutton for your time

dpsutton18:02:51

and of course there's always #beginners with lots of people here explicitly to help and chat ๐Ÿ™‚

abiduzair42018:02:26

Yes that's the best part about Clojure community.

mitchell_clojure18:02:15

I mean, just the Brave book is probably not enough info to land something is it?

dpsutton19:02:17

if you're a programmer and learn clojure from brave I bet you could work on a code base if you had coworkers in the room. I think you could land an entry clojure job with some programming chops and comfort with brave

gabriele.carrettoni20:02:45

hi, i'm trying to create an array of enum with

(into-array org.lmdbjava.DbiFlags '(DbiFlags/MDB_CREATE))
but i get
java.lang.IllegalArgumentException: array element type mismatch
and i don't understand why

noisesmith20:02:30

@gabriele.carrettoni that is a list with a symbol in it

noisesmith20:02:05

I'd try (into-array org.lmdbjava.DbiFlags [DbiFlags/MDB_CREATE])

gabriele.carrettoni20:02:40

@noisesmiththanks, what a stupid mistake

noisesmith20:02:45

demonstrating the issue more directly:

(ins)user=> (type Math/PI)
java.lang.Double
(ins)user=> (type 'Math/PI)
clojure.lang.Symbol

gabriele.carrettoni20:02:53

still doesn't work because

No matching method openDbi found taking 2 args for class org.lmdbjava.Env$Builder

noisesmith20:02:07

but you didn't even call the method

noisesmith20:02:13

(in the code you shared)

gabriele.carrettoni20:02:22

you're right, my bad

gabriele.carrettoni20:02:26

(deftype Lmdb [global-options]
  AStorage
  (open-conn [this db]
    (let [env (doto (Env/create)
                (.setMapSize 10485760)
                (.setMaxDbs 5)
                (.open (.-file db) nil))
          db (.openDbi env
                       (.-name db)
                       (into-array DbiFlags [DbiFlags/MDB_CREATE]))]
      (->LmdbConnection env db))))

gabriele.carrettoni20:02:55

the method does exist ๐Ÿค”

noisesmith20:02:15

why is DbiFlags an arg there and what is its value? n/m I misread

gabriele.carrettoni20:02:00

i think it's because of the doto?

noisesmith20:02:05

you might want to type hint args to help the method dispatch properly

gabriele.carrettoni20:02:06

it returns the builder

gabriele.carrettoni20:02:28

yes, it's the doto

noisesmith20:02:32

right, there are multiple methods with the same arity, and clojure has to decide when compiling which method to call

gabriele.carrettoni20:02:46

(deftype Lmdb [global-options]
  AStorage
  (open-conn [this db]
    (let [env (-> (Env/create)
                (.setMapSize 10485760)
                (.setMaxDbs 5)
                (.open (.-file db) nil))
          db (.openDbi env
                       (.-name db)
                       (into-array DbiFlags [DbiFlags/MDB_CREATE]))]
      (->LmdbConnection env db))))
this works

noisesmith20:02:19

so your first arg was the wrong type (the builder instead of the thing it returns when you call .open on it)?

gabriele.carrettoni20:02:37

yes and

No matching method openDbi found taking 2 args for class org.lmdbjava.Env$Builder

noisesmith20:02:49

yeah, doto would do that

gabriele.carrettoni20:02:59

thanks for the help ๐Ÿ™‚

ben.borders20:02:26

is there any way to clean this up?

(->>  (repeat 100 {})
                            (map make-purchase-event)
                            (partition-all 10)
                            (pmap batch-send-txn))
batch-send-txn is io sending to SQS. It seems to be taking a long time and i canโ€™t seem to get comp working correctly

noisesmith20:02:58

my first concern is where that data goes. If nothing consumes the result of pmap, it is lazy and the code doesn't run

ben.borders20:02:16

it just goes to sqs, and then we read from the result of pmap if all messages were sent successfully

noisesmith21:02:07

that's totally broken

noisesmith21:02:23

I misread what you said here, "we read from the result of pmap if all messages were sent correctly" sounded like "first we verify all messages were sent, then we consume the result of pmap", which would be a deadlock, as no side effects would happen until you consume from the pmap output

noisesmith21:02:01

also, do verify that whatever consumes from the pmap result is eager, this is a common source of bugs

ben.borders21:02:33

ah i see. yes the consumer is eager.

noisesmith20:02:08

pmap is a red flag for me in code reviews - it gets used wrong more often than it gets used correctly

ben.borders20:02:10

sure. the problem i suppose is that its easy to use, but apparently also eeasy to misuse. it uses futures under the hood which seem to be good for io though, yes?

noisesmith21:02:53

sure - but see my other remarks

noisesmith20:02:39

@ben.borders out of the 100000 values supplied to pmap here, only 32 are consumed

(ins)user=> (def processed (atom #{}))
#'user/processed
(ins)user=> (do (pmap #(swap! processed conj %) (range 100000)) nil)
nil
(ins)user=> (count @processed)
32

ben.borders21:02:08

gotcha. all the args i supply it always get consumed

ben.borders21:02:16

have given it (range 2000) before

ben.borders21:02:21

its just slow..

noisesmith21:02:04

how do you know they are consumed? who consumed the return value of pmap?

ben.borders21:02:33

the same function that uses pmap

ben.borders21:02:42

we consume the results of the sqs/sendmessagebatch

noisesmith21:02:13

it's not easy to use, it's hard to use correctly. if you don't consume the return value from pmap, it does nearly nothing

noisesmith21:02:22

the 32 is just a batching artifact

noisesmith21:02:36

in a future clojure version that could change to 100, or 0

noisesmith21:02:46

OK - if you are consuming the return value of pmap eagerly, I'm not sure where your bottleneck would be. Note that the batching that pmap applies internally assumes that your code is CPU bound, and you probably shouldn't use pmap for io bound code

noisesmith21:02:08

it limits the concurrency based on the number of physical processors visible to your runtime

ben.borders21:02:58

interesting.. that all makes sense.. seems its allocated here:

(+ 2 (.. Runtime getRuntime availableProcessors))

ben.borders21:02:14

which makes sense for cpu intensive usages.

ben.borders21:02:50

then i guess im back to square 0 of the best way to parallelize io calls like this. maybe in a go-loop where each io op dispatches a thread?

ben.borders21:02:59

thank you for your help btw this has been enlightening

noisesmith21:02:24

the claypoole library has parameterized versions of this stuff using thread pools

noisesmith21:02:02

the problem with using go-loop is you are introducing a heavyweight library that has a long startup time and many ways to use it wrong, for something that doesn't require any of its unique features

ben.borders21:02:56

interesting. I suppose so. Then i guess i need to manually pick my parallelism myself. Can use a loop i suppose and dispatch the # of futures = to my parallelism and deref them all afterwords in a map to ensure doneness.

ben.borders21:02:29

or it seems this library u suggested basically does that for me.

noisesmith21:02:10

instead of deref in a map (once again lazy), perhaps group-by with a failure checking function so you get a list of success and a list of failure (eager)