Fork me on GitHub
#beginners
<
2022-08-11
>
Fudge07:08:43

I'm trying to get the meta information of functions from another namespace. I've tried doing

(defn meta-info [namespaces]
  (let [meta-information (for [namespace namespaces
                              :let [all-functions (keys (ns-publics 'namespace))]]
                          (for [function all-functions]
                            (meta function)))]
    (prn "meta-info!" meta-information)))
Getting a compilerexception error here

Ben Sless07:08:44

Have you tried first with just one function?

rolt07:08:44

the compiler exception is weird, your code will not work but it should compile

rolt07:08:55

there are two problems though: 'namespace will not work, you should be passing symbols anyway to your meta-info function so just remove the quote you want vals instead of keys, ns-publics is a mapping symbol -> var and the metadata is attached to the var

jumar07:08:07

A couple of things: • you are quoting the namespace - it should be namespace not 'namespace • you need to get a var, not the symbol

jumar07:08:44

(defn meta-info [namespaces]
  (let [meta-information (for [namespace namespaces
                               :let [all-functions (keys (ns-publics namespace))]]
                           (for [function all-functions
                                 :let [fn-var (ns-resolve namespace function)]]
                             (meta fn-var)))]
    (prn "meta-info!" meta-information)))

jumar07:08:49

Ah, what rolt said is better - so just use vals

(defn meta-info [namespaces]
  (let [meta-information (for [namespace namespaces
                               :let [all-functions (vals (ns-publics namespace))]]
                           (for [fn-var all-functions]
                             (meta fn-var)))]
    (prn "meta-info!" meta-information)))

popeye08:08:12

I have defined a def variable globally in the file`(def some-variable (call-function a b c))` whenever I make use of some-variable inside any function ,Does it call (call-function a b c)` each time ? if yes how can we avoid calling (call-function a b c)` each time ?

Godwin Ko08:08:00

no, the function call only happen once when you load the namespace, or re-execute whenever you reload the namespace

popeye08:08:04

what if we use some-variable multiple places ?

Godwin Ko08:08:54

it doesn’t matter how many times you reference a var, the value “store” in a var at the time of def

popeye08:08:44

Thanks for your respone

1
zakkor09:08:06

Is there a way to provide a default value for a single map entry in a function argument destructuring?

delaguardo09:08:00

(defn foo [{:keys [a]
            :or {a "default"}}]
  (prn a))

zakkor10:08:21

Thanks, that's pretty cool

rolt10:08:43

juste beware: if you call (foo {:a nil}) you'll get nil instead of "default"

zakkor10:08:12

Follow up question: is there a way to get the default value in the :as bit?

user=> (defn foo [{:keys [a] :or {a "default"} :as m}] m)
#'user/foo
user=> (foo nil)
nil

user=> (defn foo [{:keys [a] :or {a "default"} :as m}] (assoc m :a a))
#'user/foo
user=> (foo {})
{:a "default"}

rolt10:08:27

that's a bit more than destructuring then 😅

zakkor10:08:36

Is it? I'd expect it would be possible to have a separate :as-default that provides the original destructuring with the missing default values mixed in

zakkor10:08:00

But yeah, my usecase is just a function that takes a map as an argument, destructures stuff, and is able to set default values for missing keys, then pass the whole map further down to other functions

zakkor10:08:51

Or, put it a different way, since you're explicitly defining which values you want for the missing keys, what sense does it make to then receive the original map without the default values for the :as result? It seems likely that I'm interested in the default values, right?

delaguardo10:08:37

destructuring is for binding some local refs to values taken from original data structure. So your code is interested in them (references) rather than original data

delaguardo10:08:28

usually you should have something like “normalise” to turn the data into desired shape.

zakkor10:08:07

Well, yeah.. I'd like to create my normalise by using destructuring and default values, lol

delaguardo10:08:54

then it would be better to have explicit steps to normalise the data in such function 😉 compare those two functions:

(defn normalise-1 [{:keys [a b]
                    :default {a 1
                              b 2}
                    :as-with-default m}]
  m)

(defn normalise-2 [m]
  (cond-> m
    (not (contains? m :a)) (assoc :a 1)
    (not (contains? m :b)) (assoc :b 2)))
first one doesn’t work because there is no such :as-with-default destructuring instruction but it looks like what you ask for. there are a number of things I found “problematic” with normalise-1: • there are unused binding (`a` and b) they will be reported by clj-kondo and (I think) other linters • it is obscure because it hides away how certain keys identified as missing from original m • there are other ways to achieve the same, like (merge defaults m) or a bit more verbose but much more explicit normalise-2

zakkor10:08:20

Ah, you're right. The first two bullet points are a solid refutation

Sasha V. Bogdanov11:08:04

Hi all! Help please((( How to use generators? This function returns Generator object, but I need random string except in argument:

(defn generate-string-except [exceptions]
  (gen/such-that #(not (contains? exceptions %)) gen/string-alphanumeric))

rolt11:08:02

(gen/sample your-generator) for instance. There are other functions depending on your needs

🙏 1
lassemaatta11:08:25

this is a nice walkthrough on how to use generators: https://www.youtube.com/watch?v=F4VZPxLZUdA

🙏 1
Epidiah Ravachol13:08:02

Hypothetically, if you met someone with no experience with Java beyond a semester-long course in the fall of '98—when their mind was far more focused on impressing their creative writing professor—how might you recommend they go about becoming familiar the parts of Java most pertinent to Clojure?

brianwitte13:08:35

I did quite a bit of Java before coming to Clojure and I can't say it helped me much when I started. Learning the syntax and semantics of the Clojure language is what I needed to focus on. At this point, I would say that some of my knowledge from Java (really the JVM itself) has helped, but not in any significant way.

👍 2
brianwitte13:08:14

It depends on what you are trying to do though!

Epidiah Ravachol14:08:55

I guess I'd like to get familiar enough with Java to get a little dangerous with the interop, and maybe get a basic understanding of the JVM.

brianwitte14:08:20

right, yes! this has taken me pretty far in that respect -> https://clojure.org/reference/java_interop

brianwitte14:08:57

and re: jvm, this is a succinct little summary -> https://www.geeksforgeeks.org/jvm-works-jvm-architecture/

brianwitte14:08:24

the class loading stuff is relevant. here is an article from the clojure perspective -> https://danielsz.github.io/blog/2021-05-12T13_24.html

brianwitte14:08:43

just remember to have fun, writing lots of small programs is a great way to get started with clojure. babashka scripts are a great and useful way to do that! https://babashka.org/

👍 2
brianwitte14:08:10

also, using this for project templates is good and then making sure you understand namespaces and lib imports and how to configure your repl -> https://github.com/seancorfield/clj-new

👍 1
loganrios15:08:02

At what level of system complexity is it worth designing around a framework like Mount, Component, or Integrant? I have a couple projects that only have about 2-4 stateful dependencies at most, and I've been a little overwhelmed by the learning necessary to use these frameworks correctly--so now I'm wondering if I'm giving myself a headache for not much gain--and I'm curious if I should bite the bullet and march forward, or if I'm making things too complex for a notch-above-trivial application.

Jan K15:08:03

2-4 stateful components seems like the perfect starting point, definitely worth it IMO

👍 1
R.A. Porter15:08:55

It's hard to say. There are advantages to using one of those frameworks even in a simple system like you've described - easier to switch between dev/prod/etc. environments, easier to provide stubbed services (like for testing), and WAY easier if your system grows in the future. Plus, there's the experience/knowledge you gain from using them. But you can always iterate. If it's bogging you down trying to set up and keeping you from an MVP, then push it to a second- or third-level of application refinement.

1
loganrios15:08:46

Good to hear! I'm in the situation you described (trying to push out an MVP quickly), and hearing that it's not an "absolutely required that you do this pre-launch" is exactly the direction I was looking for--and I'll be excited to factor some of these tools in when I have some more time. Thanks so much!

Ed16:08:35

I've worked on code-bases with over a hundred thousand lines of code, and talked to multiple running systems, like databases, messaging systems and external APIs, without using one of these frameworks and that wasn't a problem. I have also worked on systems that were complicated by the introduction of Component unnecessarily. I think there are several ways you can go to solve the problems that these kind of tools are trying to solve and it's difficult to offer advice about if it's worth it from outside the project. If you really have a problem with your dev workflow, or managing connections in production, for example, I think it's worth being specific about that problem and evaluating the available solutions. If you're working on an MVP, the data you're collecting is probably way more important than the software you're writing and you absolutely should be focusing on the business problems 😉

❤️ 1
loganrios16:08:01

Appreciate the perspective 🙂. Sometimes it's easy for me to get lost in trying to do everything "best practice".

Ed16:08:31

It's an easy trap to fall into and we've all done it.

😅 1
rolt16:08:44

it's useful when you start having complex dependencies between your components. That being said, even with 4 components i'd implement a lifecycle protocol/multimethod (not necessarly using those libs, its really 5 lines of code)

1
loganrios16:08:37

Luckily, I'm at the triviality level where components don't really depend on each other. I think I'll go ahead and implement some basic lifecycle behavior across each of them--and maybe just skip the complex injection stuff for now.

Narendra Bharathi C V15:08:25

Hi, I ran into the issue running out of memory when generating combinations (https://clojurians.slack.com/archives/C053AK3F9/p1659821837556939). I wanted to check if increasing the memory would help and used jvm-opts (16g, 24g, 48g); it did not and performance became worse. For example, (take 1 csv-data)* would respond instantaneously before but after the memory change, would take a good chunk of time before responding. I tried using VisualVM to understand what is going on -- my observation is that memory is steadily used up until the configured limit is reached, throw an OOM error and then respond with value. I had not seen this behavior before. I commented out the code for jvm-opts so that it reset to default 8G but the 'dance' still is the same; (take 1 csv-data) does not respond right away like it used to, but the process uses up memory until the available memory is fully consumed, throw OOM error and then show the result. Subsequent calls are snappier, until a new computation. Question, is there some kind of a reset I need to do, to get back to repl being snappier? Note, csv-data is a map of CSV records. Its a fairly small collection and there is no powerset calculation getting triggered.

rolt16:08:40

this definitively should not happen, are you sure you're not running a big computation in a "def" somewhere ? (def a (expensive-computation)) the expensive computation will be called when you require the namespace that contains "a"

rolt16:08:04

(or even just (expensive-computation) in a namespace)

Ed17:08:55

remember that if you've generated a huge object, it can't get garbage collected until there are no references pointing to it, like vars or the *1/`*2` special vars ... it's these last ones that always catch me out 😉

Narendra Bharathi C V22:08:01

No, there is no expensive compute as far as I can tell -

;; Helper function to read the CSV data into a map
(defn csv-data->maps [csv-data]
  (map zipmap
       (->> (first csv-data)
            (map keyword)
            repeat)
       (rest csv-data)))

;; Read the CSV data into a map
(defn read-csv [f] (->> (io/reader f)
                   (csv/read-csv)
                   (csv-data->maps)))

(def csv-data (read-csv "in-file.csv"))
The in-file.csv has 338 records. I would have run (take 1 csv-data) million times before and the results were always instantaneous.

Brandon Olivier17:08:10

When I return a body from a ring handler, I'm seeing unexpected transformations. Returning {:hello :world} shows up as [:hello :world] in httpie. I've switched http servers, turned on/off a reitit router, and experimented with different middlewares, but nothing seems to work properly. Does anyone know what's going on?

hiredman17:08:58

the ring spec allows for certain different kinds of things as :body value, but a clojure map isn't one of them, so if you are setting a map as the :body value, and not just getting an error, then you have some middleware or something transforming it

hiredman18:08:31

my guess would be something calling flatten or using clojure.walk somewhere, but something in your middleware