Fork me on GitHub
#beginners
<
2021-12-17
>
mister_m00:12:04

Thanks. Also wondering if clojure (or I suppose if Java) has anything similar to python's pathlib: specifically contructing paths from pieces similar to the pathsegment argument of https://docs.python.org/3/library/pathlib.html#pathlib.PurePath

Cora (she/her)00:12:34

you can also use this, it's quite nice https://github.com/babashka/fs

Cora (she/her)00:12:05

it's a single file, too, which is nice

👍 1
Darin Douglass14:12:28

( some-dir inner-dir file-name) also works

Darin Douglass14:12:03

(i totally didn’t see that you found this on your own, whoops 😛 )

mister_m00:12:39

Actually it looks like I can do that https://clojuredocs.org/clojure.java.io/file

👍 1
Noah Bogart00:12:59

If I want to implement my own type (not just a specialization of a map), I know I should use deftype. Is there an equivalent to Python’s dunder methods or Abstract Base Classes to help guide creating my desired type?

Sam Ritchie00:12:37

I have a bunch of not too wild types that try to mimic clj data structures to some extent here if you want to skim a list of potential interfaces or protocols https://github.com/sicmutils/sicmutils/search?q=deftype&amp;type=

Noah Bogart00:12:53

I’ll check that out!

Sam Ritchie00:12:33

I will often scan the protocol implementations in Clojurescript as a nice way to navigate

👍 2
Noah Bogart01:12:03

I think I'm hoping for a table like https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes for Python’s collections that somewhat neatly shows me which interfaces and methods needed to get which functionality

Alex Miller (Clojure team)01:12:59

I have a diagram for that in Clojure Applied

👍 1
Alex Miller (Clojure team)01:12:26

A cool gist that's floated around for years … https://gist.github.com/semperos/3835392

chef_kiss 1
Noah Bogart02:12:37

oh this is awesome

Alex Miller (Clojure team)02:12:19

was originally a Christophe Grand hack, but you can find a variety of variants of it around with embellishments

👍 1
Alex Miller (Clojure team)01:12:01

^^ given a type / object, builds a scaffold to fill in

Noah Bogart02:12:06

Oh excellent, I’ve been looking for a reason to pick that book up

quoll02:12:44

I did a partial diagram at one point

Alex Miller (Clojure team)02:12:30

the one in Clojure Applied includes the Java methods and their mappings to Clojure fns. I would share it but copyright etc

quoll02:12:40

I need to read that book one day! 🙂

quoll02:12:33

But I should finish the above diagram, filling in the missing pieces, etc. Despite its shortcomings, I still find it useful

Filip Strajnar10:12:57

is there something i'm missing, because in the book they presented a macro like this:

Filip Strajnar10:12:15

but i was able to reproduce the macro with just a regular function

Filip Strajnar10:12:28

although granted, the ' had to be added

Lennart Buit10:12:29

Well your backwards function uses eval to evaluate the newly created form (with arguments reversed) at runtime

Lennart Buit10:12:01

The macro implementation will run at compile time, expand to the reversal of the args, and splice in the result on the macro call site

andy.fingerhut11:12:19

If you are wondering: Why do I need macros for this? The answer is: For this use case, you don't, unless you want the reversal to happen at compile time instead of run time. A general rule of thumb in Clojure is that implementing something as a function is usually preferable to implementing something as a macro, because you usually don't NEED a macro. You can pass functions to map apply filter, etc. You can't pass macros to those.

andy.fingerhut11:12:42

But when you NEED a macro, they are nice to have.

Filip Strajnar11:12:28

what'd be an example where a macro is necessary? maybe that will paint a better picture for me

wilkerlucio13:12:05

you should never need eval really, its a dangerous operation (may allow remote users to run arbitrary code if not used with a lot of care).

wilkerlucio13:12:24

I've doing Clojure for 7 years now, 5 professionally, I had never used eval in any of my applications 🙂

emccue16:12:46

(defmacro time-compiled [] 
  (str (Instant/now)))

emccue16:12:21

basic and perhaps not super-dee-duper useful, but this is a thing you can only accomplish with macros - eval runs at runtime

emccue16:12:55

most macros are either def-like, wrapping up some boilerplate for a declaration of something

emccue16:12:06

(defrecord ABC [a b c])

emccue16:12:38

or with-like, wrapping up some lexical context

emccue16:12:52

(with-open [will-close-when-done (thing)]
  ...)

emccue16:12:25

the more DSL-ey macros that have more complex semantics tend not to survive

emccue16:12:35

but one of my favorite examples of macros is this

emccue16:12:56

(let [name "bob"]
  (>> "Hello ~{name}, how are you?"))

emccue16:12:13

this >> macro expands to (str "Hello " name ", how are you?")

emccue16:12:02

you need to do that as a macro other wise you wouldn’t have name in scope

emccue16:12:48

and its a clear example of something that other languages need to add language features for explicitly, but can be accomplished by a library in clojure

andy.fingerhut12:12:19

Implement a variant of when, where the body is only evaluated if the condition is true.

andy.fingerhut12:12:07

Implement a variant of for, where the body is evaluated a variable number of times, e.g. the number of times equal to the number of elements in an input sequence.

Lennart Buit12:12:11

Right, not sure how you stand in this, Andy, but my personal preference would be to use eval as rarely, or even more rarely than a macro

andy.fingerhut12:12:12

Macros have (at least) 2 unique powers that are either impossible or at least harder to do than without using macros: (1) Control whether the arguments are evaluated, or not, or how many times they are evaluated, and (2) introduce new names bound to values, e.g. as let does.

andy.fingerhut12:12:38

You don't need to use eval to write something that behaves like when as a Clojure / Common Lisp macro.

andy.fingerhut12:12:54

Nor to write for as a macro

andy.fingerhut12:12:08

And I completely agree that eval is rarely, if ever, needed for most applications.

Filip Strajnar12:12:07

when i use str directly, this works, but

Filip Strajnar12:12:31

when i use expression that evaluates to str˛it doesn't?

raspasov12:12:33

Can you please paste the code as text?

Filip Strajnar12:12:26

(ns app1.core
  (:gen-class))
; (nth (reverse args) 0)

(defn
  backwards
  "doesn't do much"
  [args]
  (apply str (vec (drop 1 (vec (reverse args))))))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (do
    (println (backwards '(1 2 3 str)))))

Filip Strajnar12:12:50

(ns app1.core
  (:gen-class))
; (nth (reverse args) 0)

(defn
  backwards
  "doesn't do much"
  [args]
  (apply (nth (reverse args) 0) (vec (drop 1 (vec (reverse args))))))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (do
    (println (backwards '(1 2 3 str)))))

Filip Strajnar12:12:52

this one doesn't

quoll12:12:12

Because of the quote, you are passing the symbol str, and not the function that str is bound to in the clojure.core namespace

Filip Strajnar12:12:53

i thought symbol is the function

Filip Strajnar12:12:33

so how would i pass in a function

Filip Strajnar12:12:10

if function is 1st class citizen, i have it in the list, this should mean i can access function inside the list

lsenjov12:12:27

(list 1 2 3 str)

quoll12:12:44

Well, you can build the list with the function in it. For instance: (list 1 2 3 str)

lsenjov12:12:58

So when you do that, str is evaluated as the function that str is bound to

quoll12:12:10

At my repl, this prints as:

(1 2 3 #object[clojure.core$str 0x3241713e "clojure.core$str@3241713e"])

raspasov12:12:42

Yeah, I would say using quote, aka ' , is rare in day-to-day Clojure https://clojuredocs.org/clojure.core/quote

lsenjov12:12:42

If you use ', it quotes everything in the list, including the str

lsenjov12:12:06

Which prevents it from evaluating str to the function

lsenjov12:12:11

If you were to evaluate it a second time, it would evaluate to the function

raspasov12:12:32

@U02QJVDHHE2 In Clojure, we typically use vectors to store and pass data around rather than lists; that would work right away: [1 2 3 str]

lsenjov12:12:56

'str evals to the symbol str which then evals to the function

quoll12:12:10

If you ever have a symbol, you can get the function that it’s bound to in a namespace (like clojure.core), but that’s getting more advanced, and unlikely to be what you want to do here. (you can call eval, or you can look up in the namespace)

Filip Strajnar12:12:42

oh, this works thanks

Filip Strajnar12:12:52

clearly I'm missing knowledge on the escape characters

Filip Strajnar12:12:00

thank you all very very much

❤️ 2
quoll12:12:30

It’s a little tricky, but honestly, it’s rare for most code to need it

Filip Strajnar12:12:45

i really appriciate

Filip Strajnar12:12:53

clarified a lot, once again

Filip Strajnar12:12:05

really helpful people on this server 🙂

lsenjov12:12:08

It's more useful for when you get to macros, which clj doesn't push as much as other lisps

Filip Strajnar12:12:35

i can see why, the functions are insanely powerful

quoll12:12:42

I recommend @U050KSS8M’s advice: use […] for passing around sequences of data. Vectors convert to seqs automatically in most cases

Filip Strajnar12:12:17

i could just (vector )

Filip Strajnar12:12:33

same thing right?

lsenjov12:12:38

You could, but it's longer

quoll12:12:42

Well, that’s what square brackets do for you, and they’re shorter 🙂

Filip Strajnar12:12:04

i know, I just get carried away to simplify the syntax

quoll12:12:05

The compiler turns it into a call to vector, yes

Filip Strajnar12:12:12

because with simple () you get to do so much in clojure

Filip Strajnar12:12:26

it just carries me away 😅

quoll12:12:51

Unless I’m working at parsing or emitting code, then I don’t need lists very much

💯 1
raspasov12:12:13

Also, this is a stylistic advice, but I think it's relevant. Use let for clarity and to showcase the "order" of operations:

(defn
 backwards
 "doesn't do much"
 [args]
 (let [args         (reverse args)
       f            (nth args 0)
       numbers-data (drop 1 args)]
  (apply f numbers-data)))

(defn -main-2
 "I don't do a whole lot ... yet."
 [& args]
 (println (backwards [1 2 3 str])))

raspasov12:12:55

Brevity in excess can obscure clarity

☝️ 1
Filip Strajnar12:12:55

i'm using calva and whenever i format, it kinda does it own thing

quoll12:12:25

That’s OK. But the let advice is sound

raspasov12:12:26

I've been doing Clojure since ~2013 and it took me a few minutes to unpack the initial code 🙂

lsenjov12:12:32

You could also destructure with (let [[f & args] (reverse args)] ...)

lsenjov12:12:51

But that's getting to more terse and less readable

quoll12:12:57

Also, I advise people who are new to Clojure to learn about macros, but not to use them. I also advise people who have used Clojure for several years to not use macros 🙂

☝️ 2
1
😂 1
lsenjov12:12:29

You should know what they are for those few times when you need to use them

lsenjov12:12:48

And it's very rarely you need them

quoll12:12:27

(Stuart Sierra did a talk about this many years ago: The first rule of macro club: Do not use macros. The second rule of macro club: DO NOT USE MACROS. … the talk went on to give sound advice about how to use macros if you REALLY have to use them)

raspasov12:12:43

Yeah... Macros are hard to get right. I would even say that you don't need to worry about them until you have a decent understanding of the entire language. But curiosity is always good.

raspasov12:12:58

@U051N6TTC Do you have a link to that talk by any chance? I might have watched it but not quite sure.

quoll13:12:13

There is no video, but the slides are available

raspasov13:12:03

Ah, got it. Thanks anyway.

Filip Strajnar13:12:36

this is better style?

💥 1
Filip Strajnar13:12:06

out of curiosity, can i use (vector ) instead of [] for function arguments?

raspasov13:12:09

@U02QJVDHHE2 this looks much nicer, yes

andy.fingerhut13:12:09

When you define a function using defn, you must use [] around its arguments.

andy.fingerhut13:12:41

When you call a function, you can put whatever expressions you want in the call to send as parameter values.

Filip Strajnar13:12:55

I see, unfortunate

Filip Strajnar13:12:07

would be fun to use just the () in entire program

Filip Strajnar13:12:12

thanks for the answer

andy.fingerhut13:12:18

Why would you consider it useful to use (vector ...) inside of defn ?

Filip Strajnar13:12:51

it's not useful, it'd just be ultimately simple, as I'd use symbols and parentheses

andy.fingerhut13:12:11

Some other Lisp family languages use () consistently everywhere. Rich Hickey knew about that, and explicitly chose to use [] in places like defn and let for readability.

🙌 1
quoll13:12:25

When defining, the defn is a macro so if you tried:

(defn add (vector a b) (+ a b))
Then the defn macro would see a list in the argument position, with the first element being the symbol vector. But it needs to see an actual vector object there, so you would get an error

andy.fingerhut13:12:48

i.e. NOT always using () for everything, so there are explicit visual cues that some things are parameter lists in a function definition, or a vector, or a map, and they look different

🙌 1
Filip Strajnar13:12:02

i agree it's more readable

quoll13:12:12

Perhaps some of the conversation can go back to the main channel, so individual questions can be threaded out?

👌 1
Filip Strajnar13:12:05

so, what I'm hearing is, I could make a macro such as (vectorify) , which would evaluate that list to a vector?

Filip Strajnar13:12:26

which would allow me to use lists for function argument

Filip Strajnar13:12:38

even though, again, this is by no means useful

andy.fingerhut13:12:06

I am not sure about that. I think that perhaps a macro is restricted to replace lists with lists.

andy.fingerhut13:12:01

You could define your own macro mydefn that had a list of parameters inside of it, and replaced it with a standard defn with a vector for the parameters.

Filip Strajnar13:12:41

oh yea, that'd work

andy.fingerhut13:12:02

When I say "restricted to replace lists with lists", I meant more precisely that the top level thing returned by a macro must be a list. It can replace "inner lists" with other things.

Filip Strajnar12:12:48

how come this doesn't work

quoll13:12:41

From the thread: @andy.fingerhut > i.e. NOT always using `()` for everything, so there are explicit visual cues that some things are parameter lists in a function definition, or a vector, or a map, and they look different I hate reading Scheme for this reason

Filip Strajnar15:12:10

is google still invested in clojure/clojurescript anyhow? (i'm aware this might be wrong channel to ask this type of thing but i don't know where it'd fit)

pyry16:12:29

Would think not? I wonder if you're mistaking Clojure for https://developers.google.com/closure ?

Noah Bogart15:12:31

to follow up on my questions about deftype and creating a new type, I have been poking at the clj-commons/ordered library and the implementation of OrderedMap. In the implementation of the IPersistentMap interface, the method entrySet is defined, but that only exists on java.util.Map . When defining my own types, should I be looking at the java interfaces for methods to implement or just the clojure ones? (I don’t plan on using my type in any java interopt)

Alex Miller (Clojure team)15:12:56

I think the real answer is "it depends" but generally I'd say that you should plan to implement the read parts of the collection interfaces

Noah Bogart15:12:55

the clojure collection interfaces?

Alex Miller (Clojure team)15:12:25

well certainly the Clojure collection interfaces, but I meant the JDK collection interfaces

Noah Bogart15:12:08

okay, cool. thanks

Alex Miller (Clojure team)15:12:37

so, I would expect get to work on a map, but much less likely Clojure's impl would ever put (as we don't do that on persistent colls)

Noah Bogart15:12:24

you’re talking about .get as an explicit method, right? vs IAssociative’s .entryAt or ILookup’s .valAt

Noah Bogart15:12:35

thanks for all the help, i appreciate it

Alex Miller (Clojure team)15:12:55

yes was referring to Map.get()

👍 1
Alex Miller (Clojure team)15:12:27

certainly, you should implement the IAssociative / ILookup stuff - you know Clojure will depend on those

Alex Miller (Clojure team)15:12:15

it's possible some code may rely on the Java interfaces somewhere, but I can't say I've ever audited for that, for sure things like .iterator() are called

Noah Bogart15:12:21

that makes sense

mister_m15:12:48

What is the #(Integer/parseInt %) syntax called when using a java method in a higher order function like map?

Noah Bogart15:12:00

the #(…) form is a concise form of (fn [args] (…)) : https://clojure.org/reference/reader#_dispatch

Noah Bogart15:12:11

which is to say, an anonymous function

mister_m15:12:17

ah thank you

Alex Miller (Clojure team)15:12:11

and fyi, in Clojure 1.11, you'll be able to use parse-long for this

nice 1
mister_m15:12:18

Is the only reason to use #(..) over (fn ...) because it is more concise? I imagine it maybe is more helpful in other contexts - like maybe macro writing - when you need to grab parameters

Alex Miller (Clojure team)15:12:52

it's just syntax sugar for more concise fns

mister_m15:12:37

Thank you both

mister_m15:12:12

Is there a shorthand for def similar to defn- that will allow me to make a var defined as (def my-var 42) private and only accessible in my current namespace? I am aware that I could add ^:private as metadata to the my-var def .

mister_m15:12:14

I can cycle the privacy easily enough in emacs just wondering about a more concise way to do it

dpsutton15:12:43

there’s no shorthand for def.

dpsutton16:12:09

At a previous job they wouldn’t use defn- but rather defn ^:private so it would mimic the def ^:private

☝️ 1
mister_m16:12:13

actually that raises a good point; that would make private methods and defs easily searchable

seancorfield18:12:55

@U01188CHUFL Also worth noting that you can "get around" private in Clojure by using #'some.ns/private.var to reference it -- which bypasses the access check. So ^:private is more "advisory" than, say, Java's private access.

1
mister_m18:12:02

ah I see - I didn't know that. I think that is okay, common lisp is similar in that regard you can use :: to access non-exported symbols in a namespace as opposed to : to access the exported symbols. As long as people know they are being naughty!

Michaël Salihi16:12:24

Hello, is there a more idiomatic way to write:

#(reset! form-value- (when-not (zero? %) %))

quoll16:12:30

I wouldn’t do the reset unless the value required it, so:

#(if (zero? %) % (reset! form-value- %))

quoll16:12:42

Or reverse the arguments:

#(if-not (zero? %) (reset! form-value- %) %)

Michaël Salihi17:12:00

Thanks for the suggestion. In my case, I must assign nil if the value is equal to zero.

quoll17:12:53

Ah, I misunderstood. Then it was all good as it was

Michaël Salihi17:12:39

Perfect, thanks!

noisesmith19:12:50

or I think this follow's @U051N6TTC's intent - not doing a reset unless the value changes:

#(if (zero? %) (reset! form-value nil) %)

noisesmith19:12:03

atom operations are expensive

Michaël Salihi17:12:00

Thanks for the suggestion. In my case, I must assign nil if the value is equal to zero.

Dmitrii Pisarenko17:12:36

What could be the reason that

(defn render-text-fragments-overview-page
  [data target-file fragments-dir]
  (spit target-file
        "Hello"
        )
  )
throws the exception
(render-all data)
Execution error (FileNotFoundException) at java.io.FileOutputStream/open0 (FileOutputStream.java:-2).
clojure.core$str@6d91790b/Users/dp118m/dev/misc/dpisarenko.com/content/conscience-of-nation/fragments/overview.org (No such file or directory)
even though the directory exists? (Similar spitstatements work fine with other files being written in the same call.)

R.A. Porter17:12:23

I suggest you tap>, log, or prn the value of target-file there. It's not what you think it is, based on that exception.

Dmitrii Pisarenko17:12:43

@U01GXCWSRMW If I add (println (str "target-file: " target-file)), I get the following output:

target-file: clojure.core$str@6d91790b/Users/dp118m/dev/misc/dpisarenko.com/content/conscience-of-nation/fragments/overview.org

quoll17:12:56

It looks like the str function was prepended to the front of the path. Maybe an extra str that you didn’t need got in where you were building the path?

☝️ 1
Dmitrii Pisarenko17:12:14

target-fileis FRAGMENTS_OVERVIEW_PAGEbelow.

(def MAIN_DIR "/Users/dp118m/dev/misc/dpisarenko.com/content/conscience-of-nation/")

(def FRAGMENTS_DIR (str str MAIN_DIR "fragments/"))
(def FRAGMENTS_OVERVIEW_PAGE (str FRAGMENTS_DIR ""))
There was a duplicate strin definition of FRAGMENTS_DIR. Now it works. Thanks, @U01GXCWSRMW and @U051N6TTC.

👍 1
noisesmith19:12:54

if you don't write clojure like

(defn foo [bar]
   (frob bar nil
    )
)
I won't write javascript like
function foo
  (bar)
  {frob(bar null);};

noisesmith19:12:12

(to be less snarky, it helps readability / mutual understanding if we use coding conventions native to the language we are writing, and we don't do hanging ) in clojure)

noisesmith19:12:21

I actually had to double check that even worked 😆

> function foo
... (bar)
... {console.log(1 + bar);};
undefined
> foo("help");
1help
undefined

Fudge20:12:07

How to add path in the system variable on Mac?

dpsutton20:12:14

@jabeen2699 what are you trying to do?

Fudge20:12:14

The part in this image which says, in the system variables.. go to path etc

dpsutton20:12:25

to persist things in a terminal session you can just type export VAR_NAME=value. I’m not sure what directions you are following though

dpsutton20:12:57

or if you need those values to be set in all future terminal sessions or just this particular terminal session while you do a tutorial

Fudge20:12:47

It should be a permanent variable

Fudge20:12:18

I tried doing that already and it dint work

dpsutton20:12:41

not sure what you shell is but most likely zsh on macosx. you might already have a file called ~/.zshrc. I have the following in there:

path=(~/bin $path)

MB_DIR=~/projects/work/metabase/
MB_SCRATCH_DIR=~/projects/work/scratch/
which sets some environmental vars. You could do the same

👍 1
hiredman21:12:42

osx, years ago, when I used it, would set PATH differently for apps launched via the gui, and you had to edit an xml file to adjust that

hiredman21:12:50

similarly, if you are setting environment variables in your shell, but then doing stuff via emacs launched from the gui, I don't believe emacs will get your env variables

Cora (she/her)21:12:25

also there's a direnv library, if that's how you're loading things

Cora (she/her)21:12:35

intellij supports dotenv

Filip Strajnar22:12:43

how do i include a local dependency with leiningen?

emccue22:12:00

this was a driving force behind our switch to deps.edn its a bit easier there

emccue22:12:16

{components/abc {:local/root "../../components/abc"}}

Filip Strajnar00:12:51

I've tried checkout but it doesn't work, perhaps because i'm on windows

Filip Strajnar00:12:44

all i did was a shortcut, may not be same as a link on linux

Drew Verlee05:12:46

@U02QJVDHHE2 When you have time look into using clojure deps so you can take advantage of the logic emccue is pointing out above.

Filip Strajnar22:12:57

I made a simple library, I wish to use it in a completely different project

noisesmith03:12:25

if I'm not working on both projects in parallel, the thing I've found easiest is running lein install in the lib project, then adding it to the other project as a normal dep

noisesmith03:12:40

the gotcha with lein install is you need to remember that projects that use libs installed that way won't work on other computers (and the version installed and your latest edits can go out of sync)