Fork me on GitHub
#beginners
<
2018-11-07
>
TK05:11:24

what would be a name convention to remove-attribute-key, that does

(dissoc my-obj :attribute)

TK05:11:05

more on name convention: what is the best practice for boolean functions?

has-something?
or
something?
Any other?

TK05:11:20

and boolean map key?

enforser05:11:17

my personal preferences: I would name (dissoc obj :something) remove-something i.e. drop the -key I don't care for the has I would make a map key end with ? only if the associated value is definitely a boolean

enforser05:11:30

actually maybe the has- isn't bad. I suppose the value under the key IS an attribute. (attribute? obj) makes me think your checking if obj is an attribute, not if obj contains an attribute

enforser05:11:31

maybe (contains-attribute? obj) mainly because that naming is more similar to the already existing contains?

TK05:11:05

makes sense to me!

orestis06:11:26

Why not just keep the dissoc?

henrik06:11:04

I would use prefixes like has- if it conveys some information that ? doesn’t cover, and ? tends to implicitly mean “is”. For example, (has-rabbit? hat) seems a lot less ambiguous than (rabbit? hat), whereas (rabbit? animal) is unambiguous.

👍 8
orestis06:11:19

A rule of thumb that I see emerging for me, is that if it’s harder to name something rather than understanding what it does, perhaps avoid naming? I have to read the elements of clojure again :)

kenran_06:11:33

@orestis That's very inspiring. I often find myself in this situation. I guess it's worse because of my OO background.

orestis06:11:59

It helps immensely to have a naming buddy if you’re working in a team. In some recent work I named some weird function aaa and moved on, then asked for help. This was JS, which does make things more verbose though, so naming is more important.

seancorfield06:11:58

I don't think naming an invocation of a core function adds to readability (re: the (dissoc obj :something) example above).

seancorfield06:11:42

It's like Stuart Sierra's advice not to name (map some-fn coll)

Lennart Buit07:11:08

how so, if you partially applied map, then it makes sense to name the intend of your partial application right.

Lennart Buit07:11:17

“This map function really only adds 2 to every element”

didibus07:11:09

I'd have to concur. Why are you going to wrap 3 words into a whole function just to name it. That's a terrible abstraction. If you think of an abstraction as something that says more with less. Which often words in a language are. That function is a way to say more with less. So if many lines of logic is given a name, that name is now an abstract representation of the concrete implementation of the function's logic. dissoc is already an abstraction of that sort. Remember how complicated removing a node in a tree is? Well that's dissoc for you. But now, we don't need to mention all the step of that for someone to understand the gist of the process, it is just dissoc. Now sure, in English, many things have synonyms, but I'm not sure in code that makes it any better. Now, a better abstraction is if this was related to your domain problem in some way. Say it was remove-from-user. Now at first, that's just (dissoc user something). But eventually that could involve many more things happening, maybe you push some audit trail to some DB before this happens for example. So while I still think it is premature to create this abstraction, eventually, it could make sense, and would be much better. Otherwise, you are just making people believe lots is happening and have them dig up the function to see that nope, it's 1 line of code.

didibus07:11:58

@lennart.buit I'm not sure what the others truly meant, but I think your example is different. You're talking about creating an add2 function which takes a collection and adds 2 to every element.

didibus07:11:13

With the intent of reusing this over and over in many places

Lennart Buit07:11:41

Yeah I am trying to find what Stuart Sierra had to say about naming map invocations

didibus07:11:12

I'm imagining:

(defn add2 [coll]
  (map #(+ 2 %) coll))

Lennart Buit07:11:29

yeah, but thats basically a partial application of map right

Lennart Buit07:11:52

(def add2 (partial map #(+ 2 %))

didibus07:11:31

((partial map (partial + 2)) [1 1 1])

didibus07:11:11

But you're doing it for the prupose of reuse

Lennart Buit07:11:06

so, how is that different than calling #(dissoc % :user) by its name, e.g. remove-user. That is also a partial application abeit with its arguments reversed. Where to you draw the line.

Lennart Buit07:11:20

Sorry if I come about as being rude, I am trying to understand ^^

didibus07:11:31

Ya, there's no real wrong and right here. Mostly style. Let me think where I draw the line, it's mostly intuition in practice, so it's hard for me to define some rules about it

didibus07:11:22

So, (remove-user) to me is much better. That's what I meant in my long comment above. It creates a domain level abstraction.

didibus07:11:59

But you have to balance that with a few considerations, such as, how much reuse are you going to get out of this? Does the implementation need to be modified or extended transparently from the caller? If neither of these are true, especially the latter, you are just obscuring the code through an extra jump.

Lennart Buit07:11:19

Yeah, exactly that

didibus07:11:50

Have you ever heard of ravioli code ?

4
Lennart Buit07:11:33

There is indeed a fine line between sensible abstractions and adding abstractions for the sake of adding abstractions :’)

didibus07:11:22

In my opinion it is often a case of that, when someone puts every little thing behind a component of some sort, without good reasons, it tends to code that is actually less readable because now everything is too small to understand the bigger picture, and you just have to look here and then there and back over here to put the pieces together, where you'd rather they'd all fit in your screen in one read.

didibus08:11:19

Well, that particular blog post not that great, C2 wiki has it much better: http://wiki.c2.com/?RavioliCode

didibus08:11:40

Spaghetti code I think everyone knows. When all things depend on all other, and everything is coupled to everything else.

didibus08:11:18

Lasagna is when you've got layers of layers of layers. Where most layers do nothing except take from the below layer and give it to the bottom layer.

didibus08:11:53

Ravioli code is when your components are so small, that they no longer are responssible for anything relevant to the problem on their own.

didibus08:11:59

Ideally, you code is not a metaphor for any kind of pasta 😛

didibus08:11:08

That said, of the three, ravioli is the least harmful in my opinion. And I rarely see code reach that point, most programmers are too lazy to factor things out to that level.

Lennart Buit08:11:13

the naming tho

deliciousowl08:11:42

is this not lazy?

(reduce + (take n (filter even? (range))))

deliciousowl08:11:52

timed out during a coding challenge so I'm wondering whether that's a server / speed / lazy-seq issue

schmee08:11:12

the (take ...) part is lazy, yes

deliciousowl08:11:11

hmm, is this the fastest way of summing a seq/vector of (for example even) numbers?

deliciousowl08:11:26

(defn f-one [n]
  (reduce + (for [x (range 1 (* n 2))]
    (if (even? x) x 0))))

(defn f-two [n]
  (reduce + (take n (filter even? (range)))))

(println (str 
  (time (dotimes [x 1000]
    (f-one 5000)))

  (time (dotimes [x 1000]
    (f-two 5000)))))
First one works, the second one times out.

schmee08:11:03

no: (transduce (comp (filter even?) (take n)) + 0 (range)) will be faster

deliciousowl08:11:26

that's the one! thanks

schmee08:11:30

f-two shouldn’t time out though:

user=> (time (reduce + (take 100000 (filter even? (range)))))
"Elapsed time: 60.656893 msecs"
9999900000

schmee08:11:59

also, the fastest way to sum even numbers from 2 to n 😉

(defn sum-of-first-n-even [n] (* n (dec n)))

deliciousowl08:11:36

(dec n) though

deliciousowl08:11:05

or are my functions wrong

deliciousowl08:11:21

for now I conclude that I definitely need a coffee

schmee09:11:36

yes, sry for the typo 😄

didibus09:11:04

Don't know why it times out on your end? What do you even mean by time out? OutOfMemory? Infinite Loop?

didibus09:11:00

I think you believe it times out, because it is around 10 times slower

didibus09:11:34

user=> (time (f-one 1000000))
"Elapsed time: 62.203734 msecs"
user=> (time (f-two 1000000))
"Elapsed time: 605.268095 msecs"

deliciousowl09:11:14

It was a quick online challenge, it didn't specify the error. But yeah that was probably it, I'm assuming they had a time limit for each computation

didibus09:11:41

In my tests, f-one is still faster than the transducer version

didibus09:11:32

Also, the transducer one with (range n) is a bug, as it does not behave the same way as f-one and f-two.

didibus09:11:08

user=> (time (f-three 1000000))
"Elapsed time: 86.150797 msecs"
999999000000

didibus09:11:54

Hum, though on repeated usage, seems like they probably average pretty similar to one another

didibus09:11:19

Probably a criterium bench would show us better if one is actually faster then the other

schmee09:11:29

user=> (defn end ^long [^long n] (* n (dec n)))
#'user/end
user=> (time (end 1000000))
"Elapsed time: 0.03092 msecs"
999999000000

schmee09:11:34

¯\(ツ)

didibus09:11:50

Oh damn, adding a type hint to n makes it take 2ms:

(defn f-four [^long n]
     (loop [c 0 i 0 sum 0]
       (if (>= c n)
         sum
         (recur (inc c) (+ i 2) (+ i sum)))))

user=> (time (f-four 1000000))
"Elapsed time: 1.433124 msecs"
999999000000
Type Hints for the win!

andy.fingerhut10:11:27

If you are familiar with Java and its performance characteristics, decompiling JVM bytecodes produced by Clojure back to Java source code can be illuminating (or disassembling back to JVM byte codes can also be done, if you are familiar with those, but I would guess more people are familiar with Java source than JVM bytecode). https://github.com/clojure-goes-fast/clj-java-decompiler

👍 4
andy.fingerhut10:11:12

Yes, no.disassemble if you are familiar with, or willing to learn, JVM byte codes.

petr.mensik11:11:14

I have a sort of naive implementation going through list of users, check for updated items and insert them to DB

(defn sync-calendar [user]
  (run! #(insert-event-to-db % user) (get-events (:id user)))
 
(let [users (select :users)]
  (run! sync-calendar users))       
The problem with this pseudocode is that it only inserts events to DB for last user in list. Events for other users gets inserted as well (DB correctly returns new event with generated id from sequence) however it won't get commited to the DB permanently, only events for last user in list will. I am using Postgres and Korma if it matters

seancorfield16:11:52

Without seeing more of your code, it's kinda hard to tell. I don't know how many people still use Korma (is it still being maintained?). If you were using plain ol' clojure.java.jdbc I could probably help more in the #sql channel.

dl11:11:15

hey guys

dl11:11:34

I have a current system in another language and would like to move some functions over to clojure

dl11:11:48

what is the best way to create an api in clojure with datomic?

dl11:11:13

can I use datomic ions and run code there in order to make data accessible via an api?

val_waeselynck17:11:14

Yes you can, Datomic ions will save you a ton of hassle for making a production-ready cloud deployment of this API, and Datomic is a pretty darn good database for most information systems. Having said that, assessing whether it's the "best way" or not would require a lot more detailed knowledge of your project, and using Ions will still leave a lot of decisions to be made for implementing this API.

gklijs11:11:09

As I understood datomic is using the api gateway as glue, so I guess this would mean exposing those endpoints to other users/services.

dl12:11:25

yeah I want to expose the endpoints to other services, but basically the questions is if I can run functions on ions just like on a regular clojure instance?

sb15:11:09

What do you think about this? https://docs.snapcraft.io/java-applications/7819 with clojure?

gklijs16:11:44

From what I quickly read I think GraalVM is more promisefull

TK19:11:04

there are any best practice on negative functions? Is it better to use

(defn not-something [] ...)

(not-something?) 
...or
(defn something [] ...)

(not something?)

Lennart Buit19:11:56

personal taste would be to write them in positive form

Lennart Buit19:11:14

so always something? instead of not-something?

👍 4
shaun-mahood19:11:49

I think the existing predicates back that up as well - http://blog.cognitect.com/blog/2016/8/9/focus-on-spec-predicates

👍 4
Lennart Buit19:11:49

because something? reads better than (not (not-something? ...))

seancorfield20:11:42

(def not-something? (complement something?)) 🙂

👍 4
seancorfield20:11:33

But, yeah, better to always have positive predicates since we already have if-not and when-not

Lennart Buit20:11:06

having both would be very unwise.

Lennart Buit20:11:36

Just wondering, whats the reasoning behind having an explicit if-not?

Lennart Buit20:11:22

it feels like ruby’s unless, and that somehow always makes me confused

Logan Powell20:11:59

is there a recommended alternative to Oracle for Clojureists?

didibus04:11:46

@U9TSSLS2J OpenJDK, or IBM JDK, or the Azul JDK are all not owned by Oracle. Also self-hosted ClojureScript, which completely avoids Java. Finally you have ClojureCLR running on .net

tavistock20:11:01

unless fumbles me up all the time, at least if-not has what id does in the name

Lennart Buit20:11:01

try using unless/else, that really messes with your brain

Dormo20:11:05

I've been using OpenJDK just fine.

Dormo20:11:15

java -version
openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-1ubuntu0.18.04.1-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)

Logan Powell20:11:27

I just want to try something not owned by Oracle 😉

Logan Powell20:11:05

Also, I'm on windows

Dormo20:11:05

Oh, so not OpenJDK?

Logan Powell20:11:12

OpenJDK would be fine

Logan Powell20:11:26

but there are a few options: AdoptOpenJDK, Zulu

Logan Powell20:11:40

just wondering if anyone knows of one that works well

markmarkmark20:11:54

openjdk is the reference implementation, so you can't really go wrong with that

Dormo20:11:19

Is there a way to make error log messages to appear in red in lein repl?

markmarkmark20:11:25

adoptopenjdk is probably among the easiest ways to get it on windows

Lennart Buit20:11:15

TIL that there are, by default, no standard binaries for OpenJDK on windows

enforser20:11:00

I'm hoping someone can help we clojure.core.async. I'm playing around with go blocks that loop over the content of a channel and output the transformed values to another block. It works, except when I get to 8 go-loops being evaluated into the REPL I get an infinite hang. I think that the loops are parking/stopping when there are no more elements, so I'm not quite sure why this is happening. I'll post my little code snippet to recreate it as a reply to this message to keep from polluting the main channel

👍 4
enforser20:11:23

(defn increment-by-one
  [input-chan]
  (let [c (chan 10)]
    (go (loop []
          (let [result (<!! input-chan)]
            (if (= result :last)
              (>!! c :last)
              (do (>!! c (inc result))
                  (recur))))))
    c))

(defn print-results
  [c]
  (loop []
    (let [result (<!! c)]
      (when-not (= result :last)
        (prn result)
        (recur)))))

(-> (let [c (chan 100)] (dotimes [n 10] (>!! c 1)) (>!! c :last) c)
    increment-by-one ;; 1
    increment-by-one ;; 2
    increment-by-one ;; 3
    increment-by-one ;; 4
    increment-by-one ;; 5
    increment-by-one ;; 6
    increment-by-one ;; 7
    print-results)

markmarkmark20:11:43

use <! instead of <!!

enforser20:11:56

it stops working when I add one more increment-by-one to the threading

noisesmith20:11:31

the blocking is caused by using >!! inside go

enforser20:11:40

oh, that seems to have fixed it

noisesmith20:11:13

go blocks are not for parallelization, they are for coordinating things, it's easy to lock them up

enforser20:11:16

that makes sense! I will remember to use <! inside of go blocks. Thanks a lot @U051SS2EU and @U1XTUTPMY

noisesmith20:11:45

in general, the core.async docs are good at specifying if an operation is blocking or parking

noisesmith20:11:03

parking ops are only possible inside go blocks, and blocking ops should not be done inside go blocks

noisesmith20:11:39

if you need to do something that blocks from the logical flow of a go block, there's core.async/thread which returns a channel you can park on with <!, but uses its own larger thread pool

noisesmith20:11:48

so it won't starve your go blocks

enforser20:11:41

what is the best way to achieve parallelization with async? Is thread the right way to go?

noisesmith20:11:54

you can replace (go (something-that-takes-forever) (<! foo)) with (go (<! (async/thread (something-that-takes-forever)) (<! foo))

noisesmith20:11:43

yes - or you can use a proper thread pool library like claypoole and then use core.async at the boundaries where coordination is needed

enforser20:11:19

great, that makes sense! thanks!

noisesmith20:11:23

the key is often separating the concepts of parallelism (which you do to make things faster) and asynchronous coordination (which you need to do because parallelism gets messy)

noisesmith20:11:32

core.async is really good at the second part

enforser20:11:43

right, I'm currently trying to write a library where you can define 'jobs', which are a series of functions to be applied to some data set - but I'm only providing a certain set of ways to pass a function, so that I can make assumptions on how to parallelize and coordinate their data behind the scenes. Sounds like I need to read up on where to use threading and async.

noisesmith20:11:25

@trailcapital you are doing something that blocks rather than parking

noisesmith20:11:52

>!! blocks a thread

noisesmith20:11:03

use >! inside go

noisesmith20:11:49

core.async only uses a small number of threads, and it is easy to starve the pool if you do anything that blocks inside the body of go

Joe20:11:28

how workable is lighttable as a clojure ide? is it just a prototype, or can people actual be productive with it for nontrivial projects?

noisesmith21:11:15

if you want a real IDE, your best bet is probably Cursive with Intellij Idea

Joe21:11:38

my problem with intellij is that it's bloated and has way more than what's necessary or even useful for pure clojure projects

Joe21:11:11

are there any premade configurations that cut it down?

jeff.engebretsen21:11:36

if it's a fresh install it'll ask about plugin sets and you can disable the unnecessary ones. Or go into settings -> plugins to disable after install. I've never checked for Intellij configurations, I haven't seen it used like that (like Eclipse did).

Joe21:11:14

okay, got rid of everything but Cursive and git integrations. is there anything else that's useful?

Lennart Buit21:11:02

I was so sad when the debugger wasn’t really working all too nice with Clojure 😞

Joe21:11:08

huh, that's really bad. what was wrong with it?

Lennart Buit21:11:30

well it does work like it can break on breakpoints

Lennart Buit21:11:45

but in Java, intellij is just plain better at evaluating local variables

Lennart Buit21:11:58

and ‘step’ is just strange in functional code

Joe21:11:15

how does it compare to other Clojure debugging tools?

Lennart Buit21:11:53

I dont know, I am mostly back to debugging with repl and println, If anyone knows any better debugging tools I’d like to know

noisesmith21:11:37

given that locals are usually immutable, using swap! to append a hash-map of locals onto a global atom is extremely useful

noisesmith21:11:54

call function, play with the data it added to your atom, edit code, etc.

noisesmith21:11:34

in some ways this is better than standard debugging via stepping because you are using the real language and repl, not a almost-compatible subsystem

Lennart Buit21:11:13

I’ll check that out

Lennart Buit21:11:45

I have been clojuring for about a month or so, so I am finding my footing still ^^

Joe21:11:54

wrt to step and local vars, is this a general problem with debugging in LISPs, or does Clojure suffer especially because of the underlying Java runtime?

Lennart Buit21:11:14

Nah its just Intellij IDEAs debugger being written specifically for the Java language on the JVM.

Lennart Buit21:11:42

it kinda works for Clojure, since it is using the same provisions of the JVM to break on breakpoints. Its just way more useful when you are programming in Java instead of Clojure

Joe21:11:51

but do other Clojure IDEs even have a debugger?

jaawerth21:11:30

I tend to prefer REPL and test-based debugging to step-debugging anyway, to be honest - even in other languages, unless there is a significant compilation cost I tend to prefer iterating on tests and/or running things from a REPL over ever touching a debugger unless I'm trying to figure out some really sneaky bug

jaawerth21:11:41

But I would encourage the use of tests, too - often the stuff you're trying to verify from the REPL can just as easily be written into a test (sometimes more easily) you can then run from the REPL either manually or automatically. Bonus: now you have a decent starting point you can use, after cleanup, for your actual tests

Lennart Buit21:11:54

ofcourse, but I like to use it when I want to trace some - typically - integration type problem through the application

Lennart Buit21:11:26

testing you should do for sure!

Lennart Buit21:11:21

the JVM is such a cool piece of tech, it always amazes me how much control you have over it even when it is running

orestis21:11:53

Cider has a pretty neat debugger.

didibus04:11:51

I think Cider is the only other editor I know with a debugger.

didibus04:11:00

And no, there's nothing inherent to Lisps that prevent having debuggers. In fact, I beliebe they were first to have one. It's only a matter of lack of resource and interest

Mario C.21:11:35

Have you tried spacemacs?

👍 4
didibus04:11:10

Love Spacemacs. Use it with Ivy in holy mode, it's a joy.

Mario C.21:11:25

I like that I am able to quickly move from project A to project B or C relatively quickly using the same window

jeff.engebretsen21:11:19

You could also look at inspections. I don't know if it's smart enough to only run on appropriate file types, if it isn't then turning them off would make it faster.

jaawerth21:11:48

I've heard good things about lighttable, but it doesn't have the developer resources you see in other IDEs/editors so there are a few convenience things that might be missing

jaawerth21:11:23

Personally I just use neovim with vim-fireplace and a couple other goodies to give me cider-nrepl, autocomplete, and commands for evaluating, docs lookups, etc

jaawerth21:11:05

some people have raved about maria and nightcode, though, when it comes to IDE stuff

Joe21:11:54

wrt to step and local vars, is this a general problem with debugging in LISPs, or does Clojure suffer especially because of the underlying Java runtime?

Joe21:11:13

I'm surprised that so few people seem to use a debugger in Clojure. I never found it that useful in Java / Scala, and only use a REPL in Haskell, but I find it invaluable in Smalltalk, and I assumed this would extend to other dynamic languages. then again, whenever I heard about debugging in LISP, it was with CL's SLIME, so maybe CIDER's debugger is inferior?

orestis21:11:06

I’ve used ciders debugger to debug a single function and it works pretty well. I don’t know about larger systems though or conditional breakpoints and whatnot.

Lennart Buit21:11:01

oh cool, thanks!

Lennart Buit21:11:24

definitely watching that soon

val_waeselynck21:11:43

@joebetz91 it turns out that Clojure REPLs give you a lot of debugging power - see for example https://clojure.org/guides/repl/enhancing_your_repl_workflow#debugging-tools-and-techniques

👍 4
seancorfield22:11:04

And if you're on Clojure 1.10, tap> adds a lot more power (since you can easily leave it in the code and just hook up whatever "taps" you want, dynamically in the REPL, whenever you need/want them).

Deyan Yotsov22:11:06

@joebetz91 I use the Cursive debugger every now and again. REPL is great, but it is nice to be able to fire up the debugger too, when needed.

seancorfield22:11:04

And if you're on Clojure 1.10, tap> adds a lot more power (since you can easily leave it in the code and just hook up whatever "taps" you want, dynamically in the REPL, whenever you need/want them).