Fork me on GitHub
#beginners
<
2020-02-24
>
bnstvn08:02:15

why is this happening?

(def one 1)
=> #'user/one
(case 1 one :one :not-one)
=> :not-one

jumar08:02:58

The clue is in docs: http://clojuredocs.org/clojure.core/case > The test-constants are not evaluated. They must be compile-time literals

jumar08:02:25

In your case, it would actually reeturn :one if you would pass the quoted 'one

(def one 1)
(case 'one
  one :one
  :not-one)
;;=> :one 

👍 8
Tarun09:02:53

so one in the clause is being treated as a symbol?

jumar09:02:28

Yes, not ever evaluated.

Tarun09:02:52

Interesting, I wonder why they made that choice. I can see situations where writing an expression to match might be helpful tho one can use cond with = I guess.

andy.fingerhut09:02:44

case is designed for efficiency's sake, where a multi-way branch of comparing against compile-time constants is being done. If you want to compare against run-time values, case would not be helpful.

andy.fingerhut09:02:07

because of the implementation techniques it can take advantage of because it knows its comparison values are compile-time constants.

👍 4
andy.fingerhut09:02:01

As you say cond is there, and all the rest of Clojure.

Tarun09:02:43

Thanks @U0CMVHBL2 @U06BE1L6T I feel just slightly wiser 🙂

👌 4
bnstvn09:02:25

thank you all — actually i feel more dumb 🙂 can you think any other core macros behaving surprisingly (for a noob) like this?

andy.fingerhut09:02:48

Just about anything can be surprising, depending upon your background knowledge and/or assumptions.

andy.fingerhut09:02:45

Sorry, I know that is a very general and unhelpful statement, but your question was pretty broad 🙂

andy.fingerhut09:02:54

I would recommend reading the doc strings of core macros you want to use, and ask questions about any parts of them that are unclear.

andy.fingerhut09:02:26

In this case, for example, the doc string for case says: "The test-constants are not evaluated. They must be compile-time literals, and need not be quoted."

andy.fingerhut09:02:24

I am not here claiming that all of those terms, and their consequences, should be obvious to someone new to Clojure from reading that, but that is why I suggested asking questions about any parts of them that are unclear.

andy.fingerhut09:02:56

http://ClojureDocs.org contains useful examples for at least many Clojure macros and functions. It is not "official" documentation, so treat it with just a tiny bit of suspicion if something seems wrong there, as the examples there are contributed by anyone in the world with the time and interest, and are not vetted for correctness except by other similar people.

bnstvn10:02:02

thank you for taking the time for explaining. the doc seems reasonable enough actually — seems i have a tendency for ignoring pieces i dont understand

bnstvn10:02:44

i suppose condp is my best bet for this

(condp contains? (.getType eat)
  #{EventAttributeType/TYPE_DATE}
  :date
  #{EventAttributeType/TYPE_FLOAT}
  :float
  #{EventAttributeType/TYPE_STRING}
  :string
  #{EventAttributeType/TYPE_TREE_ACC_BOOK
    EventAttributeType/TYPE_TREE_ANY
    EventAttributeType/TYPE_TREE_LEAF_ONLY}
  :discrete)
(trying to work with this legacy java api — this is were i had hard time with case . wanted to use this as dispatch fn for defmulti )

andy.fingerhut10:02:00

It is pretty common to skim over things that do not make sense, I think -- often you can get what you need to know by focusing on the parts you do understand. Maybe when new to something is a good time to ask on some of the things that do not make sense. I know it can be time consuming, but on occasion might really help in the long term.

👍 4
deep-symmetry03:03:40

In this case, where it looks like you want to dispatch on Java Enum values, you might be able to use this case-enum macro:

(defmacro case-enum
  "Like Clojure's built-in `case`, but can explicitly dispatch on Java
  enum ordinals."
  {:style/indent 1}
  [e & clauses]
  (letfn [(enum-ordinal [e] `(let [^Enum e# ~e] (.ordinal e#)))]
    `(case ~(enum-ordinal e)
       ~@(concat
          (mapcat (fn [[test result]]
                    [(eval (enum-ordinal test)) result])
                  (partition 2 clauses))
          (when (odd? (count clauses))
            (list (last clauses)))))))

niveauverleih11:02:15

Does anybody know if core.logic is still being developed? The 'development' page was last updated 9 years ago https://github.com/clojure/core.logic/wiki/Development

Alex Miller (Clojure team)14:02:08

No one is actively working on it, but would be happy to have someone do so!

niveauverleih14:02:02

It sounded promising. Why doesn't it take off, in your opinion?

Alex Miller (Clojure team)14:02:40

people do use it for things, can't say I know why people do or do not use it

niveauverleih14:02:12

Is there a slack channel for it, or some other kind of forum?

deep-symmetry03:03:11

Projects in Clojure tend to get to a stage where they work well, and are just left alone. To people more familiar with other worlds, where there is more churn, they can seem dead. But sometimes they are just where they need to be.

jsn12:02:58

Judging by CHANGES.md and release history, it's been mostly bugfixes and porting to cljs for the last 7 years

mloughlin12:02:54

what's the best way of concatenating two byte-arrays?

delaguardo13:02:54

Maybe not the best way, but quite elegant solution is to use ByteArrayOutputStream

hindol13:02:54

Create new byte array with a concatenated sequence of the existing two.

hindol14:02:24

But if you want extreme performance, sum the counts to find final size, then create new byte array of said size. Use Java System/arraycopy to fill.

mloughlin15:02:09

ah, thanks both

lepistane12:02:15

hello is there a way to make https://github.com/clojure/java.jdbc take care of _ and - transformation of names for me without me having to specify identifiers or entries?

dharrigan13:02:13

I believe, if you try next.jdbc, it does that 🙂

acim114:02:36

If you are making an app (like some sort of server) and not a library, should your main function be in a namespace like <stuff>.core or <stuff>.main or something else? I ask because core doesn't seem to make sense outside of the library case, but I see a lot of that out there. Looking for an opinion

Alex Miller (Clojure team)14:02:32

whatever makes sense to you :)

acim114:02:17

I know what makes sense to me (`main` or the like) but if there is a predominant trend I'm not going to buck it...but if there ain't there ain't I suppose.

Alex Miller (Clojure team)15:02:52

main is fine (in Clojure itself, the repl/launcher is clojure.main for example)

👍 4
theequalizer7315:02:09

Hello, How can I swap! an atom with nested maps? If I have a vector of questions (def questions ["q1" "q2" "q3"]) and I have an idwhich is a long int, and I want the result to be something like :

{id1 {"q1" "q2" "q3"}
 id2 {"q1" "q2" "q3"}}
Then I would like to get q1 for the first id1 and drop q1, or get q2 for the id2 and drop it. To control the questions I’m asking each user until there are now questions left
{id1 {"q2" "q3"}
 id2 {"q3"}}
Thanks for your help!

bartuka15:02:55

@orlandomr27 I manage to write this solution to your problem:

(def db (atom {1 #{"q1" "q2" "q3"}
               2 #{"q1" "q2" "q3"}}))

(defn remove-question-from-user [id q]
  (let [qrs (get @db id)]
    (swap! db assoc id (disj qrs q))))

(remove-question-from-user 1 "q1")
However, the "nested-map" does not exist. The map {"q1" "q2" "q3"} is invalid, it must obey a [key value] pair structure. So, I imagined you choose this data structure to avoid repetition of questions to each user, right? So I change it to a set

theequalizer7316:02:01

@iagwanderson It worked really fine! I’m flying man, thanks!

parrot 4
deep-symmetry03:03:45

There’s a race condition here though because the value of qrs is obtained outside of the swap! call, so if another thread changes db in between when @ (`deref`) is called, and when swap! is called, data will be lost. Both operations should be performed inside the swap! call.

deep-symmetry03:03:19

Here is the thread-safe way to write it:

(defn remove-question-from-user
  [id q]
  (swap! db (fn [old-val]
              (let [qrs (get old-val id)]
                (assoc old-val id (disj qrs q))))))

deep-symmetry03:03:55

However, there is an even more concise and idiomatic way to write it by using the update-in function:

(defn remove-question-from-user
  [id q]
  (swap! db update-in [id] disj q))

theequalizer7316:02:14

Thank you @iagwanderson It is really a set. I’m going to try your solution. 🙂

👍 4
g16:02:08

could someone help me understand what’s going on here?

user=> (defn aux-fn [] (prn "returning") "test")
#'user/aux-fn
user=> (defn fn1 [] (prn "orig fn") (aux-fn))
#'user/fn1
user=> (fn1)
"orig fn"
"returning"
"test"
user=> (def store (atom {:fn fn1}))
#'user/store
user=> ((:fn @store))
"orig fn"
"returning"
"test"
user=> (defn aux-fn [] (prn "returning something else") "bananas")
#'user/aux-fn
user=> ((:fn @store))
"orig fn"
"returning something else"
"bananas"
user=> (defn fn1 [] (prn "orig fn but changed") (aux-fn))
#'user/fn1
user=> ((:fn @store))
"orig fn"
"returning something else"
"bananas"

g16:02:53

tldr, define a function A that calls some other function B, store fn A in an atom. i cannot redefine A, but i can redefine B

👍 4
markmarkmark16:02:56

when you store the function like that it looks at the function stored at that var at that time and stores that specific function

markmarkmark16:02:01

it doesn't look in the var every time

markmarkmark16:02:14

there's a shorthand to store the var rather than the function

markmarkmark16:02:31

I'm not sure what it is off the top of my head... I think it might be #'fn1

g16:02:35

ok, and the function calling B looks at what’s stored at the var?

g16:02:46

when it evaluates

g16:02:56

so this has nothing to do with scoping

markmarkmark16:02:17

this is also something that can happen when you use things like partial and juxt to create functions out of other functions

markmarkmark16:02:42

they take the function out of the var at the time of the call to partial/juxt, so when you update that var with a new function, the old one will stick around

g16:02:29

got it. thanks

Akshay C. Gollapalli17:02:38

Using emacs and the accompanying tools (cider etc) , having started a web server (Iike Yada’s) how does one modify a ring handler and have it reload without closing their repl and starting a new one. I have to quit my repl session and open a new repl every thirty seconds and might as well not be using a repl at all.

Akshay C. Gollapalli18:02:27

It seems I only actually find what I’m looking for, after I make up my mind to ask about it here. Sorry to bother

andy.fingerhut18:02:57

I doubt anyone will mind, unless you do it 10 times per hour 🙂

seancorfield18:02:17

@acgollapalli A helpful trick is to refer to the handler function with #' so that a Var is passed, instead of the function "value" -- that allows you to modify the function via the REPL and have it take effect immediately without any sort of reload needed.

Michael J Dorian18:02:39

Is it good practice to use the built in memoize function for a function that will be called with a really huge map as an argument?

Michael J Dorian18:02:14

Or rather, does the caching that makes memoizing work have significant costs for a large set or large args?

seancorfield18:02:31

The caching overhead is: memory to store the arguments that you want the function to be memoized for and the comparison overhead of equality testing the arguments.

Michael J Dorian18:02:13

Will it ever dump old cached args?

andy.fingerhut18:02:26

memoizing will remember a reference to arguments, including large ones, so those values cannot be GC'ed if they are no longer used by your application, unless the memoize option you use times out old entries, too.

andy.fingerhut18:02:48

The default memoize implementation never times out old entries, but you could create one using Clojure's core.cache library that did.

seancorfield18:02:53

If you call the function with lots of different arguments, that could potentially be both a large space overhead and perhaps even a noticeable comparison overhead. The built-in memoize keeps data "forever". org.clojure/core.memoize lets you memoize with different caches, such as TTL, LRU, etc.

seancorfield18:02:16

core.memoize (you don't need to build it from core.cache directly).

andy.fingerhut18:02:29

What Sean said 🙂

Michael J Dorian18:02:48

Ok, cool. So just because I keep all my global state in a single atom and could call this function pure if I pass in the global atom doesn't mean I should. 😁 thanks.

Michael J Dorian18:02:17

I'll figure out some sort of caching, the state changes in a predictable way so I know exactly how long the cache needs to exist for

andy.fingerhut18:02:36

If you mean passing in an immutable value that is the current value stored inside of an atom, into a function that is pure, then I have no arguments against that terminology. If you meant passing the atom itself, i.e. the mutable "container" that stores a reference to an immutable value, that is a mutable object, and I wouldn't think any function you pass that to could be considered pure in any traditional sense, nor take advantage of memoization.

4
Michael J Dorian18:02:42

I would be passing in an immutable value, but it's a value that takes 30+ KB of text to represent and it is replaced with a new state 25 times a second, with a relatively low chance of ever being exactly the same twice. This function gets called a few hundred thousands times a "frame" so it's worth the effort to cache it properly, I just saw a fancy functional concept and wanted it to apply to my weird situation 😁

Michael J Dorian18:02:22

You could also argue that maybe I shouldn't call it hundreds of thousands of times but we will see where caching gets me

noisesmith18:02:20

if you break it up into smaller pieces the individual pieces are more likely to be cachable

noisesmith18:02:31

but the nature of caching is any change breaks the cache

andy.fingerhut18:02:39

You may not want to rely upon this in your use case, but note that if two immutable values are the same object in memory, then in most if not all cases of that, Clojure = is just a pointer comparison and return true if the pointers are equal. i.e. no deep comparison is done if they are the same object. How often two big collections are the same object in memory depends entirely on how your application is written.

4
noisesmith18:02:13

@doby162 one pattern is to have the messy "state" blob, and a function of that that derives a "key", where you know that any two states that should derive the same answer would have the same key (even if they are not equal), then cache based on the key

noisesmith18:02:55

it could be that you could break your function into smaller calculations that each have their own way of deriving a key

Michael J Dorian18:02:34

I thought about that, I could cache based on the tic-number in my simulation. But without the ability to dump cashed data that is more than N seconds old, that will still reach infinity in theory. I should be able to precalculate for a frame instead of calling the function to recalculate every time I need the answer

noisesmith18:02:29

TTL is one of the classic cache variants, clojure/core.memoize makes different memoization functions out of various cache types

noisesmith18:02:22

or you don't even need TTL - a LRU for a fixed N frames is cheaper CPU wise and would do exactly what you want as far as discarding older data

noisesmith18:02:22

another pattern, if you need on-demand calculation but to reuse those calculations if they come up again in the same scope, is to use a let block with recursive delays

Michael J Dorian18:02:22

Thanks, I will look into that!

noisesmith19:02:01

% clj
Clojure 1.10.1
(ins)user=> (load-file "/tmp/delays.clj")
"Elapsed time: 2005.193238 msecs"
74088
(ins)user=> (println (slurp "/tmp/delays.clj"))
(let [x (delay (Thread/sleep 1000) 31)
      y (delay (Thread/sleep 1000) (+ @x 11))]
  (time (* @y @y @y)))

nil

Santiago20:02:22

I’m thinking about saving a future in an atom with the intent of running something in parallel (fetching data from an API) that I’ll need further down the line my data pipeline. Is that a bad idea and how do I deref both without blowing things up? I haven’t tried it, but it sounds weird

hiredman20:02:53

It just works (you deref the atom, get the future, deref the future get whatever)

Santiago20:02:45

so something like (deref (:myfuturevar @myatom)) ?

bfabry20:02:52

user=> (def box (atom (future "hi!")))
#'user/box
user=> @@box
"hi!"

bfabry20:02:31

oh right, future not delay. yeah that seems to work fine

Scott Starkey20:02:57

Hi folks - I’m wanting a list of just 5 letter words. I’ve got a list of all the words from my local dictionary with:

(def words
  (set (map str/trim (str/split-lines (slurp "/usr/share/dict/words")))) )
[Using the excellent Clojure tutorial at https://www.bernhardwenzel.com/articles/clojure-spellchecker/ .] How can I process that to just keep the 5 letter words? I’m somewhat new to Clojure and sure I’m overthinking this.

bfabry20:02:26

(filter #(= 5 (.length %)) words)

Scott Starkey20:02:43

sweet - sweet - sweet!

bfabry20:02:22

that operation will return a list though, not a set, fyi

bfabry20:02:43

sorry, a "lazy sequence" not a "list". but it amounts to something very similar

happyb3at20:02:43

(def scores {"Fred" 1400, "Bob" 1240, "Angela" 1024}) (get scores "Fred") => #function[clojure.core/get] ??? Why dont repl display 1400 here instead of that above??? 🙂

bfabry20:02:33

this doesn't quite look like a copy/paste from a cli repl. where is this from?

happyb3at20:02:51

emacs cider nrepl

happyb3at20:02:42

i just eval current sexp in emacs and pasted the evaluated string here

bfabry20:02:43

my guess is you are hitting the key combination to "evaluate form under cursor" rather than the key combination for "evaluate this whole line / surrounding expression"

bfabry20:02:08

try positioning the cursor on the opening ( character, rather than the word get

happyb3at20:02:18

oki i will try another eval 🙂

Eddie20:02:30

If you try your code on a fresh repl (I used http://clojurescript.io) it works as you expect.

happyb3at20:02:48

was using eval current sexp

bfabry20:02:17

get by itself would be considered a valid sexp

happyb3at20:02:26

and SPC , e f normally worked for me but also messes up 🙂

happyb3at20:02:47

Eval top level espression helped 🙂 thx

Eddie20:02:07

Many times in the past I have foolishly defined values under the symbol count and then been confused as to why the count function is throwing. My first thought was something along those lines.

happyb3at20:02:09

only weird I think 🙂 must read more on cider I guess thx for the help

Scott Starkey20:02:44

@bfabry - Thanks… I’m just using the wrong (newbie) terminology. I meant “lazy sequence”.

bfabry20:02:11

I was actually correcting myself, not you 🙂

Scott Starkey20:02:03

I just improved my code using a thread. (First time I’ve tried this.)

(def words
    (->> (slurp "/usr/share/dict/words")
        (str/split-lines)
        (map str/trim)
        (set)
        (filter #(= 5 (.length %)))
        (filter #(Character/isLowerCase (first %)))
        )
    )
I also added another filter to take out the proper names. (Ensuring first character is lower case.)

bfabry20:02:04

yup, you're getting it. one pointer you don't need () around str/split-lines or set

Scott Starkey20:02:44

Cool - Yeah, ok - that makes sense.

noisesmith20:02:50

also use count instead of .length - if it's a perf question then at least provide a hint to the length call, but that's not likely to be a perf bottleneck

👍 8
johnj21:02:09

any takes on a more elaborate version of this tweet? https://twitter.com/stuarthalloway/status/1229856488560234497

johnj21:02:38

hey says avoid using someones (org/person) name?

noisesmith21:02:18

@lockdown- if you look at the rest of the thread, it's about using someone else's org name for your stuff

johnj21:02:28

yeah, thought so, the initial tweet sounded weird