Fork me on GitHub
#clojure
<
2017-09-27
>
rpepato01:09:47

Hi there, clojure noob trying to understand the language concepts and basic structures. May I ask for some clarification? I’m trying to build a prime-number-seq generator using lazy-seq and the idea from the Sieve of Eratosthenes. This is the code I wrote:

(defn primes
([]
(concat
(sorted-set 2)
(primes (remove #(= (mod % 2) 0) (drop 2 (range))))))
([s]
(let [x (first s)]
(cons x
(lazy-seq (primes (remove #(= (mod % x) 0) s)))
))))

It works for getting, for example, the first 1700 primes from the sequence. When I ask for the 1772th prime number, I got a Stack Overflow. From the research I did, I suspect that this has some to do with head retention but I can’t see where is the problem. Any ideas?

sophiago05:09:54

I realize this is for learning purposes and hence not what you're asking, but fwiw there's a great SoE example using core.async: https://github.com/clojure/core.async/wiki/Sieve-of-Eratosthenes

Ben Hammond08:09:45

those calls to (lazy-seq (primes (remove build up and so sooner or later you blow the call stack whilst attempting to chew throrugh them

Ben Hammond08:09:50

is tangentially related

rpepato14:09:59

Thanks a lot @U2TCUSM2R @ben.hammond . I think that I did not got lazy-seqs the right way and I’ll do some homework on this topic before trying to move on

noisesmith01:09:59

why are you passing s into the recursive call when you know that the first element will never be used?

rpepato14:09:45

In fact I’m passing it around just to capture the first element and discard all multiples of it

noisesmith01:09:31

I'm not sure this causes your problem, but it's definitely making your code do redundant work

noisesmith01:09:36

I think the issue is that the nested remove calls each add to the depth of the stack needed to realize the next item

noisesmith01:09:55

by the time you get to the Nth prime you are executing (first (remove f (remove g (remove h (remove ....... coll)))))) to get x

noisesmith01:09:53

if instead of nesting calls to remove, you pass in a set (and add an item on each recursive call) you can keep stack usage constant instead of having it grow linear with N

noisesmith01:09:08

also, the usage of (sorted-set 2) is bizarre, because concat instantly removes all the sorted-set properties and leaves a simple lazy-seq, it would be simpler to do (cons 2 ...)

rpepato14:09:50

I didn’t really know about this but thanks for the clarification. I’ll try to investigate the relationship between seqs and its interactions with the collections implementations

noisesmith17:09:59

a good rule of thumb is that anything lazy turns its result into a LazySeq - nothing lazy ever preserves the type of its input collection

noisesmith17:09:13

(unless by coincidence the input was already lazy)

pesterhazy08:09:26

you should consider adding joker to the list - its linter mode works wonders and works for cljs: https://github.com/candid82/joker

pesterhazy08:09:22

we use it on CI to check for unused namespaces at least, and locally as an emacs mode to catch some errors early (like eslint)

dominicm09:09:05

I wasn't able to build joker, which was quite frustrating

hkjels11:09:47

whats the most idiomatic way of keeping tabs on an element of a collection? Do I store the index of the element or do I store the element and do some sort of lookups, or perhaps something else entirely?

Ben Hammond11:09:11

er, depends if your collection is a a vector or a map or a set or a list

hkjels11:09:04

values will always be distinct, so a set would make sense in this particular case

hkjels11:09:06

hmmm. That actually simplifies my use-case indeed 🙂 I could probably use the element itself and a combination of fnext and select

dominicm11:09:55

(let [x (calc-x)]
(when (pred? x)
(do-something-to x)))
I feel like this is something I could use. I like when-some, when-first, etc. Is there a more idiomatic way I should be writing this?

Ben Hammond11:09:37

(some-> x calc-x pred? do-something-to)
?

Ben Hammond11:09:56

not necessarily an improvement though

dominicm11:09:33

@ben.hammond I think that only works if pred? returns what you gave it for "true" and "nil" for false?

dominicm11:09:08

(some->
10
(number?)
inc)
gives an exception about booleans & numbers 🙂

sundarj11:09:36

there is cond-> too, but i guess for a single condition-function pair that might be overkill

Ben Hammond11:09:37

that code you originally posted looks clear to me

dominicm11:09:52

@sundarj cond-> doesn't take the previous value in for the predicate though, (cond-> (calc-x) pred? do-something-to) wouldn't work

sundarj11:09:13

yeah you'd need a let as well alas

dominicm11:09:34

It's definitely clear, I've just been a little spoiled by:

(when-first [a [nil]]
(println a))

sundarj11:09:17

(whenp pred? [x (calc-x)]
(do-something-to x))
i guess you could write this macro if you fancied it 😉

dominicm11:09:49

I didn't think there was anything better, but I thought I'd fish in case I could rephrase it differently so there was no intermediate value.

dominicm11:09:02

Maybe this is just aesthetic though 🙂

sundarj11:09:17

no shame in that

Ben Hammond11:09:08

can any of youse point me at an explanation ?

Ben Hammond11:09:36

is it that with-bindings creates a thunk that closes over the new value, and thus handles multi-threading better?

dominicm11:09:33

@ben.hammond one does bindings via a {}, one does it like a let in a []

Ben Hammond11:09:06

yeah but must diff by more than just syntax

I remember reading about a book someone was writing that was a annotated read-through of the clojure.core source code. Does anyone know what I'm talking about and can point me to it?

that's it, thanks

sundarj13:09:00

no problem 🙂

vinai16:09:55

I'm experiencing a strange issue where when I'm recompiling a test namespace (clojure.test based) after any change I get an error that all my other required namespace aliases already exist:

Caused by java.lang.IllegalStateException
Alias sut already exists in namespace project.account-test,
aliasing project.account
This is how the imports look:
(ns project.account-test
(:require [project.account :as sut]
[project.account.event-handlers :as account-events]
[clojure.test :refer :all]))
The only way I've managed to resolve it (until the next change) is to restart the repl 😞 (ns-unmap 'project.account-test 'sut) didn't help, just as (reloaded.repl/reset-all) doesn't help. It's really annoying. Any idea what might be causing this?

jeaye17:09:27

@pesterhazy Thanks for the suggestion! Joker slipped by, but I'll give it a go on this code and add an entry to the post.

noisesmith17:09:16

@vinai ns-unmap is for vars, not for aliases

noisesmith17:09:19

@vinai to remove an alias to another ns as created by require / :as - use ns-unalias

vinai17:09:33

Thank you @noisesmith, good to know. That allows me to clean up the mess manually. I still am curious why that is happening though.

noisesmith17:09:25

if you use refresh, that deletes a namespace and replaces it

noisesmith17:09:38

aliases aren’t by namespace name iirc, but by their object identity(?)

noisesmith17:09:01

so my theory is that the new namespace has the same name but isn’t the one aliased

noisesmith17:09:36

(it would require some experimentation to verify this) - regardless, the simpler solution might be to also refresh the test namespace (which does the same thing to that one as well, deleting it entirely and recreating it)

This may be more of a java question, but I've got a clojure service behind a KafkaConsumer that I've had to trim and expose as a gen-class that another service can call directly as a class/library for performance reasons, rather than going through kafka. How do I keep the quick development cycle on the clojure side without having to compile down to an uberjar and copy it into the other project (and then restart that other service) on every change? What was nice as a kafka consumer was that the service respected the reloaded workflow so a change was as simple as a (reset) in the REPL

if it makes a difference, the parent service is a scala application

bfabry18:09:09

the interface class is hosed, whatever you're generating with gen-class if you make a change there you're going to have to restart

bfabry18:09:32

but that could be just left as a tiny shim that uses tools.namespace to reload etc

noisesmith18:09:18

@dadair if you use clojure.java.api.Clojure.var to look up the function to call, it will see redefinitions every time you invoke it

noisesmith18:09:49

then you don’t need gen-class either, just use require to make clojure load your code, and Clojure.var to look up the var you can call

bfabry18:09:55

require won't reload files if they've changed though fyi, that's where you need tools.namespace

noisesmith18:09:58

or, you can make sure you have a layer of indirection between the ns that uses :gen-class and the code you are calling, so that if you reload the clojure code in the process, the gen-class methods call the new definition

noisesmith18:09:30

@bfabry it takes a :reload arg, but that’s not what I was talking about, I was talking about the usage flow when not using gen-class - you need to remember to call require before using your code

bfabry18:09:31

well unless you tell it to using :reload/:reload-all. but tools.namespace is a bit cleaner because it clears out old vars

noisesmith18:09:15

the reloading part is separate, I was just describing how to replace gen-class

are there any performance considerations (on the order of ms) between using a (gen-class ..) and using clojure.java.api.Clojure.var?

noisesmith18:09:02

the noticible difference is on initial load (what would happen on startup in the case of gen-class, or on usage of require in the Clojure.var use case)

chalcidfly18:09:31

Noob question. How can I get (clojure.repl/source my-function) to work in a lein repl?

chalcidfly18:09:45

noisesmith18:09:55

noisesmith18:09:20

the source function needs to know a file and line number, clojure doesn’t actually store the raw source of the code it compiles in memory

chalcidfly18:09:29

chalcidfly18:09:33

That doesn’t seem to do anything for me

noisesmith18:09:37

sure - that’s what cider etc. are doing

noisesmith18:09:52

load-file ensures that the metadata is present that allows repl/source to operate

chalcidfly18:09:15

noisesmith18:09:27

… checking

dpsutton18:09:46

call meta on it to see what the var info is

dpsutton18:09:49

breeze.mast.services.accounts-test> (meta #'permission-for)
{:arglists ([perm perms]), :line 37, :column 46, :file "*cider-repl localhost*", :name permission-for, :ns #object[clojure.lang.Namespace 0x73f10384 "breeze.mast.services.accounts-test"]}
breeze.mast.services.accounts-test>
breeze.mast.services.accounts-test> (meta #'permission-for)
{:arglists ([perm perms]), :line 44, :column 1, :file "/home/dan/ops/projects/breeze_ehr/mast/test/breeze/mast/services/accounts_test.clj", :name permission-for, :ns #object[clojure.lang.Namespace 0x73f10384 "breeze.mast.services.accounts-test"]}

bronsa18:09:23

you can't get the source code of a function that's not defined in a file

noisesmith18:09:13

@chalcidfly oh, the other gotcha is the file needs to be in the expected place on classpath. source is an odd little macro.

chalcidfly18:09:29

Oh. what’s the default cp for lein repl?

noisesmith18:09:42

@chalcidfly use lein cp to see it

noisesmith18:09:00

or more accurately I guess lein with-profile repl cp (typo fixed, wants string not keyword)

chalcidfly18:09:13

I’ve got clojure-projects/expand/src on there, do I need clojure-projects/expand/src/expand as well?

noisesmith18:09:37

no - as long as the ns-declaration in your file matches the path from src to the file

chalcidfly18:09:55

Weird. Still doesn’t work.

noisesmith18:09:07

noisesmith18:09:01

odd that it would need that…

chalcidfly18:09:44

Thanks a million for your help, idk what was going on with load-file there

dpsutton18:09:30

how were you invoking the load-file functionality?

noisesmith18:09:37

@dpsutton a namespace loaded via load-file means that repl/source doesn’t work - I verified this myself

noisesmith18:09:49

there’s only one way to use load-file, you give it a string with the path to the file

noisesmith18:09:16

(well maybe you could also give it a java.io.File object, haven’t tried, but I wouldn’t expect this to act differently either0

uwo19:09:11

if you’re in a place where you you’re wishing you had multi-arity anonymous functions, is it safe to say that you’ve overcomplicated things? 🙂

Alex Miller (Clojure team)19:09:04

you can do multi-arity anonymous functions

Alex Miller (Clojure team)19:09:21

and I’d say no, not necessarily :)

uwo19:09:56

I’ve missed reading that for awhile now

uwo19:09:00

bwstearns20:09:19

Does anyone have advice for introducing clojure at work? I have a prototype in clojure for replacing some really gnarly xslt stuff and I’m going to be proposing switching to it to the team sometime this/next week. Curious if others have had success or failures introducing it to a non-clj workplace.

eriktjacobsen21:09:18

What's the rest of the stack? any jvm? Good place to focus on a similar deployment / prod environment / toolchain

bwstearns21:09:45

It’s mostly a python shop in our area and we’re pretty functional with python. Farther towards the front-end there’s a lot of scala use. The part I’m proposing replacing is actually a Java program to run XSLT transformations and the .xsl files required to transform them, so to some degree I’m replacing one non-standard piece of tech with another one (and I think it’s fair to say that almost everyone would rather learn clj than xslt)

beoliver22:09:45

is there a threading operator that given a value keeps threading until a return value is not nil and then breaks early?

noisesmith22:09:05

@beoliver that is what some-> and some->> are oops, I misread, that doesn’t exist, but you can use or

noisesmith22:09:00

what would be threaded, since you expect things to return nil until one doesn’t

beoliver22:09:15

just not as pretty as (or (function-1 x) (function-2 x) ...)

noisesmith22:09:59

that would be a straightforward macro to write, the hardest part would be a decent name for it

noisesmith22:09:10

oh wait, is this some-fn?

noisesmith22:09:52

@beoliver oh, yeah, this is some-fn

clojure.core/some-fn
([p] [p1 p2] [p1 p2 p3] [p1 p2 p3 & ps])
Takes a set of predicates and returns a function f that returns the first logical true value
returned by one of its composing predicates against any of its arguments, else it returns
logical false. Note that f is short-circuiting in that it will stop execution on the first
argument that triggers a logical true result against the original predicates.

beoliver22:09:50

and as any return value will be truthy, this should work

noisesmith22:09:12

=> ((some-fn :a :b :c :d) {:c 2 :d 3})
2

noisesmith22:09:40

be aware that when given N args, it checks each arg separately as an arg to each fn

beoliver22:09:02

would it not be easier to write with reduce and reduced?

beoliver22:09:33

would need a let

noisesmith22:09:02

#(reduce (fn [_ f] (some-> % (f) (reduced))) nil fns) would do it

noisesmith22:09:09

but I think some-fn is still clearer

noisesmith22:09:35

(apply some-fn fns) is equivalent to the above

beoliver22:09:53

@noisesmith I think the some-fn will work perfectly