Fork me on GitHub
#beginners
<
2022-06-02
>
zakkor13:06:56

Is the special {} and [] syntax created through reader macros? Or something else?

zakkor13:06:20

very dope, so clean. It really just is macros all the way down 😄

andy.fingerhut16:06:46

To be precise, the macros in the reader are not the same as the macros you can define with defmacro. You cannot define your own reader macros in Clojure source code. You can only add new syntax like {} or [] i Clojure on the JVM by changing the Java source code for Clojure's reader.

hoynk14:06:05

Can anybody point me to a good date/time library that works on clojure and clojurescript?

delaguardo14:06:48

https://github.com/juxt/tick Tick is another one which uses first one to make high level abstractions

Trev15:06:15

Heya! Thanks for having me here. I have managed to complete a coding challenge using Clojure in Emacs w/ Cider. The code runs. I turned on the LSP at the end just to see what it would do with docstrings and it has decided to lint my structs as "unresolved-symbols". Should I stay confident in my code here, or does the LSP know something I don't yet appreciate? I'm new to Clojure/Java.

Trev15:06:40

It's linting on both the definition and anywhere they are referenced:

clojure
(ns tutorial.dealership)

(defstruct car :model :cost)
(defstruct customer :name :budget :coupon)
(defstruct coupon :type :code :discount)

(def cars [(struct car "BMW" 60000)
           (struct car "Ferrari" 100000)
           (struct car "Fiat" 20000)])

(def coupons [(struct-map coupon :type 'percent :code "GIMMIE20" :discount 20)
              (struct-map coupon :type 'fixed :code "THAT5K" :discount 5000)])

ghadi15:06:58

@trev did the coding challenge say to use defstruct?

ghadi15:06:53

to answer your direct question: the LSP implementation doesn't know about structs apparently

🙌 1
👍 1
Trev15:06:10

Haha...Hm. The course focused on defstruct as a means to create structured data, then gave me the challenge to use whatever tools were already taught

Trev15:06:16

So, in a round-about way, yes

ghadi15:06:37

just use plain maps or records if you must. The course is wrong.

Trev15:06:02

Welp, it was $15. I can get over that

Trev15:06:22

Hopefully there aren't too many deprecated concepts

seancorfield15:06:36

I'm curious to know what course that is (so we can help others avoid it)...

ghadi15:06:02

if anything, a linter/LSP should warn: "don't use defstruct"

ericdallo16:06:53

Sounds good to me, actually, if the var has deprecated metadata, clj-kondo/clojure-lsp will already warn about it right?

ghadi16:06:01

it doesn't have the deprecated metadata

ghadi16:06:08

it just shouldn't be used 🙂

😂 1
ericdallo16:06:45

^{:deprecated "Just don't use it, please"}

Trev15:06:42

> just use plain maps or records if you must. The course is wrong. @ghadi I was thinking about composition from the data source, and wondering what that data may look like if it came from some other resource. It structs have that nice shorthand (:key struct) rather than (get map :key). Is it more advantageous to avoid the abstraction of records?

ghadi16:06:01

all maps support the (:keyword access)

👍 1
Trev16:06:15

Coolcoolcool

seancorfield16:06:51

That course has come up here before -- look at the 1 * reviews and you'll see at least one mention of this Slack and feedback on the poor practices in that course.

Trev16:06:21

There weren't many choices

Stef Coetzee17:06:12

Have you taken a look at @U050P0ACR's courses? I'm working through his API course and learning a great deal. :)

Trev17:06:46

Oh man, Eric is how I got here. I was cleaning my car and looking for programming podcasts on AntennaPod, and he came up. I started from Episode 1.

Trev17:06:27

I wanted to gently dip my toes in, so I grabbed the $15 Udemy sale before deciding that I needed to pay the $600 that I am sure his courses are worth

Trev17:06:39

Got what I paid for, clearly

Stef Coetzee17:06:17

Daniel Higginbotham's book (which can be read freely on his website if you'd like to try before you buy) is also a decent intro. Good luck and keep at it! 🙌😁

👍 1
Trev17:06:21

Thank you

ghadi16:06:23

records have a further compiler optimization that specializes access on the record's declared fields My suggestion is: start with maps, hug your maps, use maps as long as possible

ghadi16:06:02

defrecords subsume the old defstruct because records can implement protocols & interfaces, as well as do all the things that structs do

Trev16:06:05

It was supposedly updated in 2022...which was part of my purchasing decision. I see that the update is probably meaningless

Trev16:06:10

@ghadi I suppose it's fair to say that records enforce the shape of an expected model more readily?

Trev16:06:41

If maps have less overhead, I'd rather do that and expect bad input

ghadi16:06:13

none of these mentioned mechanisms "prevent" invalid data

Trev16:06:51

I noticed that. With the record, however, if I omitted the :coupon key, I got an error on implementing the new record, as apposed to some mishap down the line where accessing that key fails.

Trev16:06:12

Potato potato

Ian Oliveira16:06:29

Hi Folks, is there a way to know the caller of a function? like something bellow

(defn print-my-caller[]
  (print *caller*))

(defn my-function []
  (print-my-caller)) 

"my-function"

dpsutton16:06:51

if you want this just for debugging you can look at stack traces. clojure.repl/pst (print stack trace) is a handy way to accomplish this

dpsutton16:06:02

licenses=> (defn foo []
             (pst (ex-info "hi" {}) 4)
             :whatever)
#'build.licenses/foo
licenses=> (defn bar [] (foo))
#'build.licenses/bar
licenses=> (defn quux [] (bar))
#'build.licenses/quux
licenses=> (quux)
ExceptionInfo hi {}
	build.licenses/foo (NO_SOURCE_FILE:217)
	build.licenses/foo (NO_SOURCE_FILE:216)
	build.licenses/bar (NO_SOURCE_FILE:219)
	build.licenses/bar (NO_SOURCE_FILE:219)
:whatever
licenses=> 

dpsutton16:06:39

here i have a function quux which calls bar which calls foo. And that prints a stacktrace of 4 levels. (just ballparking with 4. adjust as desired)

Ian Oliveira16:06:05

(defn get-customer-by-id [customer-id]
  (db/query "SELECT ... where customer-id =" customer-id)) ; example code
 
Imagine that db/query function executes the query and than exports the time spend to execute the query to prometheus.. and we want to add to this metric the "query name" which in this case is get-customer-id and in this case is the name of the caller of db/query

dpsutton16:06:33

oh no this is not a good solution for that

1
dpsutton16:06:56

for human consumption?

nadejde16:06:29

guys I have a mega beginners questions. I’m sorry in advance for having to ask but I tried googling and I probably can’t even formulated the question correctly:( I am trying to build a list of data structure from smaller structures that I save in variables using def before hand. At the end I basically need a list of maps but the maps are big in themselves so I’m trying to split it up in smaller chunks. The way I figured it out to work as I need it to is like this:

mud.interface> (def one {:name "one"})
#'mud.interface/one
mud.interface> (def two {:name "two"})
#'mud.interface/two
mud.interface> (list one two)
({:name "one"} {:name "two"})
Is there a better/more canonical way of achieving this or is this the way to do it?

kenj17:06:08

I think the more idiomatic thing here would be to use a vector literal: [one two]

kenj17:06:26

Despite being a lisp, lists tend to be the exception as oppose to the rule in Clojure

nadejde17:06:20

thank you that’s good insight. So I should try and use a vector if my usecase doesn’t call for a list then. 🙇

hiredman17:06:36

how are you building the maps?

nadejde17:06:38

I just use

(def map-name {...})
And then there’s stuff in the map like other maps or vectors

hiredman17:06:40

is it all constant/literals? or are you pulling data from some source?

nadejde17:06:04

I am using these maps as the data source actually. Like a sort of confing file. Most of the contents is literals but I sprinkle some function names here and there

hiredman17:06:21

it is somewhat uncommon to see a big map def'ed, if it is all constants/literals you might treat it as a data, and load it at runtime using read (avoiding passing it through the compiler), this type of thing is common for configuration files

nadejde17:06:25

because I have function refs in there is why I was thinking of treating them like clj files instead of an external data source. I’m very early in my thinking so I might end up just reading from files

kenj17:06:09

Defining your data as EDN might be what you want. You can define symbols in EDN, which I think would let you reference function names in your data, without having to define everything in clj files

nadejde17:06:51

I’ll look EDN up thank you I don’t know what that is:D

nadejde17:06:34

edn is a system for the conveyance of values. It is not a type system, and has no schemas. Nor is it a system for representing objects - there are no reference types
so I would need a mechanism to convert a literal value into a function ref?

kenj17:06:13

I think symbols would do the trick, but honestly I haven't tired this myself 🙂 https://github.com/edn-format/edn#symbols

kenj17:06:08

I think by reference types, the spec is referring to referencing other things within the EDN file itself. That said, I believe I've seen libraries define their own reference types using custom tagged elements

🙏 1
Trev17:06:26

> I have managed to complete a coding challenge using Clojure in Emacs w/ Cider. Refactored with @ghadi's suggestions in mind. Here's the exercise: A "car dealership" and coupon system. The specifics of what was supposed to be accomplished were pretty simple. I added some layers of complexity, such as variable coupon candidates. I also tried to keep my functions as simple as possible, with one param in, simple return value out.

ghadi17:06:26

rapid fire: list-affordable-cars is Doing Too Much™. It's applying all the discounts, and comparing it to a customer's budget. Separate those concerns: map applying the discounts, filter by budget cars and coupons should be parameters to the functions that want them. I realize this is a learning exercise. It's ok to have them as top level defs, but then explicitly pass those collections to functions, don't just reach for top-level defs from inside your functions.

💥 1
🧵 1
Trev18:06:14

Just to be clear on the objective here: At no point do I ever want to reach for the namespace scope for either cars or coupons. They should be passed into a function from within that scope, and calculated/acted-upon from there?

Trev18:06:36

A function such as a main or init function?

Trev20:06:55

Lighting Round Challenge:

ghadi21:06:22

hate to say it but no

💥 1
ghadi21:06:57

the internal calls to (get-vehicles) are still global state, not parameters

ghadi21:06:28

just pass the vehicles and coupons to present-options -- meaning more arguments

1
ghadi21:06:23

if different parts of your application want to call these functions with different cars and coupons, they cannot because you have globals not parameters

Trev21:06:24

That's easy feedback to fix. I was trying to stick to one argument per function but perhaps in the context of an action such as present-options this is going too far

Trev21:06:44

In terms of separating concerns, are we good?

Trev21:06:12

@ghadi Another try.

ghadi17:06:00

'percent quoting symbols is not idiomatic clojure, more like common-lisp

🧵 1
ghadi17:06:15

use :percent or :fixed

Allan Gonçalves17:06:32

for this specific case, using case instead of cond feels more idiomatic to me

Trev17:06:32

> 'percent quoting symbols is not idiomatic clojure Ah, they were exampled in the course-we-did-already-name. I recognized them from elisp, which is kinda wanting to be like CL. I see them as easier to grok vs :type :percent Is there another practical reason for wanting to use the colon?

Trev17:06:33

Challenge accepted

dpsutton17:06:57

You access coupons by their code but keep them in a list. Rather than inspecting each coupon one-by-one looking for the code, just store them by code in a map of code -> coupon

1
Trev17:06:43

I suppose if the key of the coupon map is a string, that could be easy. In terms of memory, is it better to intern it as a symbol so I can :THAT5K -> coupon?

Trev17:06:54

I can never tell if I'm splitting hairs

ghadi17:06:59

:THAT5K is a keyword, not a symbol You should not use keywords for high cardinality sets of values

Trev17:06:47

TIL what cardinality is

ghadi17:06:33

if you were doing a production system, you'd want an efficient way to check whether a coupon is valid, without scanning the whole list. But the list is gonna be your canonical source probably

Trev17:06:43

That's exactly how I imagine it. Unless it's from a document database, or reformatted after being retrieved.

Trev17:06:37

Though I suppose you would SQL query where (= :code "STRING")

Trev17:06:12

I am used to clunky data formatting from places like Shopify

sheluchin17:06:36

(transduce (map #((juxt :x :y) %))
  (completing
    (fn [r n]
      (reduce + r n)))
  0
  [{:x 1 :y 2}
   {:x 1 :y 4}]
Is there a shorter way to write this as a transducer? Basically trying to pick some vals from a bunch of maps by key and sum them.

Alex Miller (Clojure team)18:06:32

(transduce (mapcat (juxt :x :y)) + 0 [{:x 1 :y 2} {:x 1 :y 4}])

gratitude-thank-you 1
popeye18:06:44

how to check empty condition for clojure.lang.PersistentList ?

hiredman18:06:16

that isn't a list that is a long

popeye18:06:24

sorry updated the question!, I tried (empty? v) which gave me the issue , when I printed type it says it is clojure.lang.PersistentList

hiredman18:06:47

then somtimes v is a list and sometimes it is a long

hiredman18:06:02

if empty? throws that exception it means it was passed a long

hiredman18:06:27

user=> (empty? 1)
Execution error (IllegalArgumentException) at user/eval173 (REPL:1).
Don't know how to create ISeq from: java.lang.Long
user=>

popeye18:06:48

ahhh I see? I am trying to write a condition for emty check! how to check if the collection is empty? which passes for strings and numbers

hiredman18:06:17

(and (coll? v) (empty? v))

hiredman18:06:59

(although keep in mind coll? returns false for nil, which often gets treated as an empty collection in clojure)

popeye18:06:44

super cool thanks you 🙂