Fork me on GitHub
#beginners
<
2021-12-20
>
mister_m02:12:05

Kind of a side question - I don't think I've ever used namespacing of keys in a map before. What is a situation where that is helpful? Is there a reason to use a key namespace over, say, nesting "a" and "b" under the key "x" ?

emccue03:12:39

lets say you have two sql tables - person and dog

emccue03:12:31

SELECT person.name, dog.name 
FROM person
JOIN dog ON dog.owner = person.person_id;

emccue03:12:09

if you use next.jdbc, the results of this query will look like {:person/name "Joe" :dog/name "fido"}

emccue03:12:43

and while, yeah you could make that be

{:person {:name "Joe"}
 :dog    {:name "fido"}}
is that really worth making the result of
SELECT * 
FROM person;
be
{:person {:name "Joe" :age 45}}
?

mister_m04:12:21

I see, thanks for the illustration

emilaasa07:12:50

Nice explanation

pithyless08:12:44

@U01188CHUFL - as a side comment to your side question, you may be interested in Wilker's work with Pathom, where he strongly argues for the benefits of "don't nest, if you can namespace" and use context-free names (i.e. fully-qualified keywords). This may be a good primer: https://www.youtube.com/watch?v=IS3i3DTUnAI

mister_m15:12:38

I haven't ever heard of "namespacing" keys before tbh - is that sort of unique to clojure?

pithyless23:12:15

Clojure did not invent namespacing keywords and/or using them in associative maps. But, they are clearly something that the Clojure community has taken and run with and also something that Rich Hickey must have been thinking about for a long time on how to make idiomatic and easy to use. Here's an excerpt on the motivation for qualified keywords in Clojure: > Keywords are also an integral part of the ‘just use maps’ story. Keywords are a symbolic scalar type, distinct from symbols, with a high-performance (identity) equality check. As distinct from symbols, which are resolved to bindings during evaluation, keywords always evaluate to themselves, and thus don’t require quoting. Thus keywords are the preferred first-class attribute names: keys in information maps. > [...] > Clojure does support multi-segment namespace qualifiers and aliases thereof for both symbols and keywords (e.g., :prefs/favorite-food above). When combined with Java package name reverse-domain conventions (e.g., :org.my-org/my-name, as is encouraged in Clojure) keywords are as useful for defining openly extensible, mergeable, conflict-free keysets as are URIs for RDF properties. > [...] > These facilities make Clojure a good language for information programming. Specifically, they make it easier to write context-independent, generic, loosely coupled information processing programs that can be driven by data. Keys and maps are first-class values not encoded in specific program types, readable from text without specific program code, and can be produced by different languages. This is the way that large, loosely coupled programs that interoperate over wires are constructed, and in my opinion it’s the way that large loosely coupled programs that don’t (yet) operate over wires should be constructed as well. That's an excerpt from "A History of Clojure": https://download.clojure.org/papers/clojure-hopl-iv-final.pdf Notice the repetition of ideas of "context-independent" and "information systems" (similar to what we see in previously linked Pathom work). These, I would argue, are ideas that are very prevalent, if not necessarily unique, to the current Clojure ecosystem. I might also add, that while Rich Hickey has obviously been thinking about this for far longer, I think the use of qualified keywords in the larger Clojure community really started taking hold after clojure.spec and Datomic were released (both forced the usage of qualified keywords and I think particularly paved the way for everyone else to catch on to their advantages).

👍 1
ChillPillzKillzBillz07:12:49

Hi, Anybody has had experience is using clojure(script) for mimic-ing browser requests to a server, without a browser? Very unclear question... so for example can all the aspects of cookie management, and session management be done easily enough... or this is more than one person can chew?

pavlosmelissinos08:12:02

etaoin does that stuff programmatically but it does require an installed browser

ChillPillzKillzBillz14:12:17

looks promising!! Many thanks!

popeye08:12:44

Which is the better approach of defining a variable ?

(def dark-green "#143")
(def light-green "#175")
(def dark-purple "#449")
(def light-purple "#6ad")
(def dark-red "#944")
(def light-red "#f8c")

popeye08:12:52

or

(def color
   :dark-green "#143"
   :light-green "#175"
   :dark-purple  "#449"
   :light-purple "#6ad"
   :dark-red  "#944"
   :light-red "#f8c")

Ben Sless08:12:31

(def colors {:dark-green ,,,})

Ben Sless08:12:05

That also leaves you open to extension in all the right ways

popeye08:12:51

Sorry I did not get you

Ben Sless08:12:42

Let's say you do this

(def colors
  {:dark-green "#143"
   :light-green "#175"
   :dark-purple  "#449"
   :light-purple "#6ad"
   :dark-red  "#944"
   :light-red "#f8c"})
Then have a function called draw
(defn draw [shape])
If you wanted to modify the colorscheme, how would you do it?

Ben Sless08:12:52

(defn drwa
  ([shape]
   (draw shape nil))
  ([shape colors]
   (let [colors (if colors (merge default-colors colors) default-colors)])))

Ben Sless08:12:31

Define the default globally, leave an option to pass it as arguments to override the default

popeye08:12:26

I am defining both globally only, but wanted to know better approach

Ben Sless08:12:21

• a map is nicer • you can't def multiple bindings, so you'll need a pile of defs • what do you plan to do if the color you want isn't defined?

popeye09:12:39

Yeah then I would update the map

Ben Sless09:12:43

Think about it at run time. You wouldn't modify a global map.

popeye09:12:44

like, if any enhancement comes in the code then we can add it in a same map right? will that be a problem ?

Ben Sless09:12:09

that's not a problem, what I mean is even a case where you (get m k), if it's a map you an always have a default value in case you don't know, if those are defs you're in a problem

popeye09:12:27

ahh make sense, Thank you

popeye09:12:22

i started learning reframe! and I am basically backend engineer, I wanted to understand what is subscribe in reframe ? why do we need it ? Can anyone help me please

manutter5112:12:48

Re-frame subscriptions are a mechanism that automatically re-renders a GUI component whenever the data changes. It's also an easy to reference individual pieces of data stored inside a large nested map in an atom named app-db.

manutter5112:12:42

I recommend reading through the docs at https://day8.github.io/re-frame/re-frame/ if you haven't already. I found them very helpful in getting a grip on the core concepts.

manutter5112:12:13

I also gave a talk on re-frame at Clojure Conj 2018, you might find it helpful: https://www.youtube.com/watch?v=JCY_cHzklRs&amp;feature=emb_imp_woyt

popeye13:12:20

@U06CM8C3V I have seen the video and it is very useful, but did not get the difference between (rf/subscribe [:time]) and rf/reg-sub

manutter5113:12:26

rf/reg-sub creates a subscription--you do that once to define where to look up the data in the app-db. Once you have a subscription defined, you use rf/subscribe to say, “Ok, I’ve defined a subscription named [:time] and now I want to use the current value of [:time] in my GUI component.” So rf/reg-sub defines where the data is, and rf/subscribe actually goes and gets the data.

popeye13:12:45

ahhh ! I got that

popeye13:12:59

are you the one who made this video ?

manutter5113:12:15

Yes, that’s me in the video.

manutter5113:12:53

Don’t let it fool you, though, I’m not a re-frame expert, just a guy on the job. 😉

manutter5113:12:38

There’s people in the #re-frame channel who know a lot more than me about re-frame stuff, and how subscriptions work under the hood and stuff.

popeye14:12:36

oh ok.. Good resource btw, Yeah I am beginner to reframe and I joined that channel

👍 1
Filip Strajnar10:12:45

how do i update nth value in a list?

popeye10:12:59

list is not indexed based, so you cannot do that , Can you elaborate bit what you trying to achieve

dgr17:12:07

You can do it, but there is no standard library function to do it. You would have to recurse down the list with rest, counting how far down you are, replace the value when you get to the right spot with cons and then rebuild the first part of the list with cons as you unwind the recursion. If this sounds inefficient, that’s because it is. Which is why there is no standard library function to do it. But it can be done. But a better question is, if your algorithm requires you to replace something at a specific index, why aren’t you using vectors?

Filip Strajnar10:12:45

do i just put it to vector and assoc?

andy.fingerhut10:12:32

That is certainly one way. Note that it takes linear time to put it in a vector, so if you are going to do this very frequently on many lists in some inner loop, you may not like the performance, and may want to re-think how to get better performance.

andy.fingerhut10:12:51

If you want to do it outside of the hot paths in your code, then sure, go for it.

Ed12:12:35

You could always use map-indexed with a map?

(->> (range 10 20)
       (map-indexed {3 :fourth}))
which will replace the index 3 with the value :fourth in the resulting seq.

Filip Strajnar13:12:42

thing is I kinda want to do this in a tree

Filip Strajnar13:12:59

so, nested lists, example ((((3 4) 0) (7 7)) (1 6))

Filip Strajnar13:12:21

so map and assoc don't really do all that much

Filip Strajnar13:12:26

when i need to keep this structure, also performance isn't a concern atm and I'm aware of how vector and lists look as data structures so i understand their limitation

alexmiller13:12:19

(it does not take linear time to assoc into an indexed structure like a vector)

andy.fingerhut15:12:55

Understood. The linear time I mentioned was to take a list of values and create a vector from it. If his goal was to do that for many different lists in a hot code path, that was my warning.

andy.fingerhut15:12:41

@U02QJVDHHE2 Your original question was about replacing the nth element of a list. Do you have an example of a nested list, and some other parameters you want to give to a function, and what you want the return value to be? It isn't clear to me what replacing the nth element of a list has to do with your nested list.

Filip Strajnar15:12:31

((((3 4) 0) (7 7)) (1 6)) this is the example, day 18 of AoC requires me to modify a number to the left and a number to the right

Filip Strajnar15:12:59

i know how to recurse over the list, I just don't know how to modify the nested lists

Filip Strajnar15:12:06

in order to keep the structure

andy.fingerhut16:12:07

a number to the left, and a number to the right, of what?

andy.fingerhut16:12:53

As I mentioned, sample parameter values, and desired return value, make your request more precise.

andy.fingerhut16:12:08

(and even with an example, an English description of the general rule is also good)

oly12:12:05

Any info on what .cpcache folders are / do I occasionally hit this sort of error

Could not locate java_time__init.class, java_time.clj or java_time.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.
removing cpcache fixes it but I dont understand why it only happens sometimes so I am obviously missing something here

alexmiller13:12:09

cpcache is where the Clojure CLI caches classpath and other files created by the CLI

alexmiller13:12:35

I presume from the error that the cached classpath has gone stale somehow (deleting something out of ~/.m2 maybe?) if removing the cache fixes it. You could get the same effect by using -Sforce on the CLI to force a recomputed classpath. But could be something else with your project setup, hard to say without more info.

oly13:12:57

I am doing a refactor if that helps so moving code around when I run it sometimes i get the above error, I have been adding it to deps then removing the folder to fix the issue, its like adding it to deps is not enough because the code itself has not changed so its not recalculating, the force option is good to know about however 🙂

ho0man18:12:35

Hi everyone, I got a question that concerns more with JVM rather than Clojure, but I'd greatly appreciate the help. We want to write an engine that depends on some protocol/interface. And whose implementing objects are not present in the compile-time but are added later on to the runtime classpath and should be loaded dynamically by the JVM, i.e. the compiled jar file be dropped on the classpath. Is this possible? What considerations do I have to consider when doing this in Clojure? • Ref-Lib: Contains multiple protocols • Core Engine: Manages instance of objects that implements protocols inside ref-lib and has ref-lib as a dependency. The Instances are identified by some namespace keyword (or full java class name) • Instance Libraries: they have ref-lib as dependency and implement it. Thanks a lot

emccue18:12:23

Service Loader

emccue18:12:37

this is how slf4j, jdbc, imageio, etc work

emccue18:12:54

its not the most well supported thing in clojure

emccue18:12:33

specifically because you generally can’t run code in a namespace unless that namespace is required

emccue18:12:59

so its hard to get that “magically discovered just because its on the classpath” behavior

emccue18:12:44

if its internal and you can specifically enumerate all the possibilities you can conditionally resolve each namespace (idk the right incantation for that)

emccue18:12:18

so your core would be like

(if-exists your.thing.impl-1
   (register! registry your.thing.impl-1/implementation))

emccue18:12:56

and if you only have a single implementation you expect you can have each instance library just provide that one namespace

emccue18:12:22

(require 'your.thing.impl) ;; same regardless of what impl

emccue18:12:32

you can definitely do SPI stuff from clojure with AOT if you need to

emccue18:12:13

and then your (i think) final option is to have the user explicitly register or include the library

emccue18:12:44

xtdb does something in this array of options i just dont know what

emccue18:12:11

ah they have you pass a symbol to resolve

ho0man18:12:56

Thanks a lot @U3JH98J4R for the detailed response

hiredman18:12:36

basically yes, but once you are doing that kind of runtime dynamic code loading stuff you will want to stop thinking in terms of the classpath and think in terms of classloaders

hiredman18:12:55

the classpath is a thing used to configure a top level level classloader when the jvm starts, but classloaders can be created at anytime and load jvm classes and resources from anywhere (filesystem, jars, urls, build them in memory, etc)

ho0man18:12:17

Thanks a lot @U0NCTKEV8 It really helped

ho0man12:12:06

I had one follow up question @U0NCTKEV8 if you could help me Consider I wrote a custom classloader how can I modify the running JVM to use that classloader for dynamic class loading? Thanks again

randomm char20:12:55

Weird issue I ran into with memoized fib

(def fib
  (memoize #(condp = %
              0 (bigdec 0)
              1 1
              (+ (fib (dec %)) (fib (- % 2))))))
with a fresh repl when I do (fib 136) i get Execution error (StackOverflowError) at user/fn (REPL:5). null then I do (fib 135) I get 7308805952221443105020355490M then I can do (fib 136) without getting an overflow with a fresh repl I can also do (map fib (range 1 1000)) without getting an error

solf20:12:47

If you call fib 136 from a fresh repl, it will drill all the way down to fib 1 since nothing has been memoized yet

solf20:12:30

When you do the range 1 1000, every time you call fib, it won't have to compute the previous ones

solf20:12:12

For example, in the range call, fib 136 will just get the precomputed results of fib 135 and 134

randomm char21:12:56

(fib 136) gives me an overflow error on a fresh repl (fib 135) gives 7308805952221443105020355490M on a fresh repl ?

mister_m21:12:55

is there any kind of global clojure repl config available where I can - for example - set it up to always use pretty printing?

mister_m21:12:37

I think using lein I'd be able to provide some config on a per project basis via the :repl-options :init mechanism

John Bradens23:12:22

I'm trying to put a link in a web page. I have hiccup in the project.clj file. But in my clj file, it says it doesn't know what to-uri is, or link-to. Do I need to also require hiccup in the namespace? I tried [hiccup :as h] and it said it couldn't find hiccup.

John Bradens23:12:34

All the other hiccup stuff works fine, like [:p "paragraph"] etc... I want to be able to have something like [:p "For more info search (link-to "http://google.com" "Google")].

dpsutton23:12:17

to use functions from another namespace, you need to require that namespace, and then its quite helpful to use an alias. (ns foo (:require [hiccup.core :as h]))

dpsutton23:12:37

note you need to require specific namespaces from the library, not the library in general like you have [hiccup :as h]

dpsutton23:12:50

I’m not sure i know what to-uri or link-to are though

dpsutton23:12:48

ah. that’s in hiccup.element. so (:require [hiccup.core :as h][hiccup.element :as e]) and (e/link-to ...)

John Bradens23:12:32

If I have

[hiccup "1.0.5"]
in my project.clj folder is that enough to also include hiccup.core and hiccup.element? I just saw I have [hiccup "2.0.0-alpha2"] but idk where that came from. When I look up hiccup now I only see the 1.0.5 version. Should I just use that?

John Bradens23:12:38

Thanks for the help btw!