Fork me on GitHub
#beginners
<
2020-06-21
>
Mark-James M.00:06:10

Just figured it out

dpsutton00:06:30

its a perfectly cromulent question 🙂

Mark-James M.00:06:38

Needed to add float

Mark-James M.00:06:01

(def average (float (/ (+ 20 50 60) 3)))

noisesmith13:06:55

float is only needed for interop (and even then is very rarely needed) - use double, and also you can count on the result being floating point if 1 arg is floating point

Mark-James M.02:06:45

I generated a new Leiningen project and am trying to run it using lein run but am getting the following error: No :main namespace specified in project.clj.

dpsutton02:06:34

can you give me repro steps? sounds strange

dpsutton02:06:45

(how you generated and then how you're running it)

Mark-James M.02:06:25

I created the project in Cursive, then I had been running it in the REPL

Mark-James M.02:06:31

Then I opened a command prompt and typed lein run in the project dir

dpsutton02:06:08

weird. i wonder what cursive did.

/tmp ❯❯❯ lein new app thing && cd thing && lein run
OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
Generating a project called thing based on the 'app' template.
OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
Hello, World!

Mark-James M.02:06:55

That's really odd, creating a project through the command line worked

dpsutton02:06:10

how did you create it in cursive? I'll try it there but i'm still new at cursive

dpsutton02:06:27

and i get a little lost in cursive when it does a bit too much

Mark-James M.02:06:38

File--> New Project --> Leiningen

Mark-James M.02:06:50

looking at the non cursive project

Mark-James M.02:06:52

:main ^:skip-aot learn.core

Mark-James M.02:06:03

is inside project.cli

dpsutton02:06:03

there are templates. i guess lein new app app-name adds that stuff

dpsutton03:06:42

you can specify a template to be used. i put app in the template spot and it does what the command line version does. hope that helps

AS05:06:25

What's going on under the hood when (seq [1 2 3]) is called? The https://clojure.org/reference/sequences suggests that seq returns an implementation of ISeq that "appropriate to that collection", but if that's the case why does (conj (seq [1 2 3]) 0) append to the left rather than the right, which would be the most performant way to append to a vector?

hiredman05:06:38

an implementation of ISeq is a seq, not a vector

hiredman05:06:13

"appropriate to that collection" means it returns a seq that knows how to traverse that collection, not some kind of seq that is different

dpsutton05:06:51

i agree that wording is a little strange though

dpsutton05:06:15

its correct when you know what's going on but is understandably confusing when reading for the first time

seancorfield05:06:45

@abhiyantsingh This might be good reading, as it talks about the differences between "collections" and "sequences" http://clojure-doc.org/articles/language/collections_and_sequences.html

AS05:06:45

Got it. Appreciate all the replies! One example I'm curious about is (-> [1 2 3] (conj 4) (seq) (conj 5)) => '(5 1 2 3 4). In this situation, conj adds to the right of the vector but to the left of the seq generated from it. Based on the code that @dpsutton linked, it seems like seq contains references to the original vector, does that mean that any conj calls to a seq made from a vec are O(n)?

dpsutton05:06:40

hiredman put it very well. it knows how to traverse a vector quickly. but you have a think with two functions on it: first and rest. and the only way to add is to put something as the new first

seancorfield05:06:46

Once you call seq on a vector, you get a "chunked seq(uence)" and conj on a sequence acts like cons so it prepends the new item.

seancorfield05:06:18

You can see some of the types in play here

user=> (type [1 2 3])
clojure.lang.PersistentVector
user=> (type (seq [1 2 3]))
clojure.lang.PersistentVector$ChunkedSeq
user=> (type (conj (seq [1 2 3]) 0))
clojure.lang.Cons
user=> (type (conj [1 2 3] 0))
clojure.lang.PersistentVector
user=> 

dpsutton05:06:21

if your view of the world is the first thing and then the remaining things, the only way to add quickly to it is to plop it as the new first.

seancorfield05:06:25

(also, from the above types, you can perhaps infer that (conj (seq [1 2 3]) 0) is O(1) because it simply returns a Cons object with 0 as the first and the original sequence as the rest)

AS05:06:13

So is the cost of calling seq on a vector O(n)? Because it has to create the ChunkedSeq? It seems like there's a traversal happening in the constructor there in arrayFor so just wanted to confirm.

dpsutton05:06:26

the cost is O(1)

dpsutton05:06:44

reading that code i linked, its just a java object with the vector and an index

AS05:06:45

So, based on @seancorfield's example, it constructs a ChunkedSeq, and my interpretation of the code https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentVector.java#L373-L378 is that it's doing a traversal; am I misinterpreting what's calling what?

AS05:06:31

I suppose what I'm struggling with is that if you start with a vector and end up with an operation that appends to the left side, such as (conj (seq [1 2 3]) 0), I don't see how an O(n) operation is avoided, since you either append to the left of the vector, which is O(n), or spend O(n) to build a collection that has O(1) left appends. Does that make sense?

dpsutton05:06:48

you don't append to the left of the vector in the sense that the vector is now [0 1 2 3]. you append to the left of the seq in the sense that you have 0 first and the rest is a seq view of the vector

AS05:06:56

Ohhh. Thank you! That finally made it click for me.

AS05:06:09

Appreciate all the help/patience, everyone!

dpsutton05:06:09

the chunked stuff and reading the java code can often introduce more complexity when first learning it. so i hope i didn't muddy the waters too much

AS05:06:35

Not at all, the concreteness helped! I'd been poking around the code base for the last hour trying to figure this out but was looking in the wrong places.

andy.fingerhut15:06:09

If you are poking around the Java implementation of Clojure to learn, then you might in some cases find this library useful for visualizing the in-memory representation of Clojure values as JVM objects: https://github.com/jafingerhut/cljol

AS18:06:06

This looks sick. Thank you!

dpsutton05:06:04

as did i. i went right to APersistentVector and sean did a very thoughtful thing and asked the repl what is actually going on

dpsutton05:06:44

I still get a little muddy understanding how IPersistentVector, APersistentVector and PersistentVector interact. but those concerns are far from #beginners channel 🙂

AS06:06:31

Yup, I'm saving that rabbit-hole for sometime... later 🙃

Mark-James M.06:06:54

I purchased the Getting Clojure book the other day and its been a huge help

kimim07:06:54

dear all, how to avoid circular require? Thanks.

raspasov07:06:56

Extract functions that cause the circular require into a new namespace

zach08:06:02

Hi all! I am getting started with clojure (with some javascript experience) and quite love it, though am having difficulty figuring out the app-packaging side and wondering if anyone had advice/or a good direction to point me. In short: I am building a daily-update page using clojure and hiccup…it reads some weather info, plus an .edn file I made of my feelings and observations I’d made about weather that day, and translates it into a single static html page. I made this with using lein new app and it works well enough, but where I am lost is how to add new feelings/observations. I was imagining a simple terminal interface that just said “how you doing!” then “how’s the weather?” and takes the input from both these lines and writes them as a new map to my feelings.edn, and then run the page generator and take the top map from this vector to update the page.

zach08:06:38

I’m finding it hard to conceptualize how to make this simple terminal interface…of essentially printing “What is your feeling: ” and waiting on text input. On searching online I found a cool looking [cljs-tui](https://github.com/eccentric-j/cljs-tui-template) template plus [lanterna](https://multimud.github.io/clojure-lanterna/4-reference), but felt that both might be overkill for what is a simple ask?

zach08:06:06

(part of this is that I’m coming from javascript experience of looking for a framework first, then making that work for my needs…and trying to get away from that)

zach08:06:19

Thank you for any opinions/directions you’d recommend on this!

dpsutton08:06:46

clj -e '(println "how are you feeling?")(let [feeling (read-line)] (spit "feelings.edn" {:feeling feeling}))'

zach10:06:07

oh, awesome, thank you, @dpsutton! Do you think the best way to do a two-step question (e.g. “how are you feeling”->input, then “how is the weather?“->input) be to define a map of {:feeling :weather}, then do a print statement for feeling, assoc read-line to :feelings, then second print for weather, assoc read-line to :weather, then spit that map to feelings.edn? This feels like a straightforward /imperative way to do it, but not sure if it’s idiomatic.

raspasov11:06:39

I think that’s ok:

(let [_       (println "how are you feeling?")
      feeling (read-line)
      _       (println "how's the weather?")
      weather (read-line)] 
  (spit "feelings-and-weather.edn" {:feeling feeling :weather weather}))
Seems to work inside a clj repl, for some reason doesn’t work via the “clj -e” command

zach02:07:00

this is great!

dpsutton08:06:20

run that in a terminal. will spit the results into a file "feelings.edn'

vish08:06:01

Hello everyone i am vishal ,i am a freelancer i offer services in web development,app development,SEO.I will provide services at lower prices without compromising the quality of product. Dm me if anyone has any project.

David Pham16:06:03

Anyone who knows some resources to compile ClojureScript using GitHub actions?

littleli17:06:56

Just a curious question... in Java it is possible to represent larger numbers with using underscore like this: 1_000_001 so it's more readable and actually writable. Is there anything similar to this in Clojure?

andy.fingerhut17:06:41

The Clojure reader does not support that, no. One could write their own code/data reader that does, but that would not help you write numbers in that way in an unmodified version of Clojure.

littleli17:06:34

@andy.fingerhut do you think it's realistic to request such improvement? It's obviously nice to have, low priority thing, but still...

andy.fingerhut17:06:14

I suspect it already has been requested -- checking to see if there is a JIRA issue for it already. I don't expect such a change to make it into Clojure soon, if ever. It isn't pure advantage -- consider the use case of searching for occurrences of the number '1000000' in source code using tools like grep.

andy.fingerhut17:06:24

I didn't find an existing issue in a minute of looking. You are welcome to ask the question on https://ask.clojure.org yourself and see what kind of response you get.

dpsutton17:06:11

i would love the feature.

andy.fingerhut17:06:49

Understood completely. I am not against it myself. But neither am I the decision maker, and it isn't a democratic process.

andy.fingerhut17:06:00

(nor do I think it should be a democratic process)

dpsutton17:06:21

totally agree. just chiming in with a little +1

littleli09:06:12

I've found this https://ask.clojure.org/index.php/8511/add-digit-separators-support-to-number-literals?show=8511#q8511 I actually give it a short and did some implementation myself. TL;DR I have a working implementation, working on tests. I'll try to be initiative and propose a patch with some rationale and pros and cons as I see them.

andy.fingerhut14:06:04

Cool. I didn't see any comments from someone like Alex Miller in that http://ask.clojure.org conversation, for or against the proposed change. That simply means there is no information there on whether the Clojure core team is interested in such a change. It is at least possible that they are not interested, but haven't mentioned it in that issue. If you enjoy making experimental patches for the sake of your learning, great, but if you are becoming strongly emotionally invested such that your world view will change if they say "no" to the idea, you may want to get some indication of that before spending lots of time on it.

adam19:06:03

How do I make this work:

(contains? (sort #{2 1 3}) 1)
It gives me contains? not supported on type: clojure.lang.ArraySeq

dpsutton19:06:20

(sort #{2 1 3}) returns a seq (1 2 3) which doesn't support contains?

dpsutton19:06:48

(contains? #{2 1 3} 1) should report true

adam19:06:30

yeah, that's just an example. In reality my set/arrayseq is coming from (sort (java.time.ZoneId/getAvailableZoneIds))

adam19:06:08

(def timezones
  (sort (java.time.ZoneId/getAvailableZoneIds)))

dpsutton19:06:18

why do you need it sorted here?

adam19:06:28

using it on for display (web form)

dpsutton19:06:41

check out (doc sorted-set)

dpsutton19:06:42

could you have a sorted version of it for iteration and a set for membership checking?

adam19:06:37

yeah, sure. I thought there was a simpler alternative for contains? that works with arrayseq

dpsutton19:06:07

arrays can only check membership through linear scans checking each element for equality. keep a datastructure that has O(1) membership check and a version that is an ordered version if you also need that

dpsutton19:06:55

the docstring of contains? points you towards some if you do need to search through an ordered collection for an item

adam19:06:59

got it, thanks for the clarification

dpsutton19:06:57

i point that out because (doc [thing]) is an incredibly useful thing. the repl is self-documenting to a large extent and very helpful. your editor most likely has some keybindings and UX to ease this as well

adam19:06:38

I've been using http://clojuredocs.org for the accompanying examples mostly, will give doc a try

andy.fingerhut19:06:39

http://Clojuredocs.org contains output of doc at the top, before the examples

dpsutton19:06:56

@somedude314 what editor are you using?

dpsutton19:06:03

many have clojure-docs built into the editor

andy.fingerhut19:06:20

At least for things that http://clojuredocs.org covers. It does not cover many Clojure libraries, if any

dpsutton19:06:33

hit control-j on a symbol and it should show the docs and clojuredocs in a popup

adam19:06:58

Ah awesome, thanks. F1 is showing me the docs for what's under the cursor

dpsutton19:06:33

yeah. i think those are bound by default to the same keybinding. not sure. but i think you're seeing what i was suggesting

adam19:06:02

Am gonna roll with this then: (defn timezones [& {:keys [sorted?] :or {sorted? false}}] (let [tz (java.time.ZoneId/getAvailableZoneIds)] (if sorted? (sort tz) tz)))

dpsutton19:06:05

Passing in sorted true is more verbose than just calling sort on the caller side

adam19:06:35

haha true, I think I'll do just that

adam19:06:43

This makes me wonder how many over complications I have in my Clojure code as a beginner 🙂

seancorfield19:06:18

A good rule of thumb is for functions to do one thing only: a function to return the timezone IDs, a function to sort them. The latter is built-in. Small, composable functions, mostly pure.

bartuka22:06:32

If I would like to provide a different implementation to a java method like isEmpty to my custom record, how should I approach it?

(defprotocol IMyInterface
  (isEmpty [this]))

(defrecord MyData []
  IMyInterface
  (isEmpty [this]))
The above code will not work with the following error msg: Duplicate method name "isEmpty" with signature "()Z" in class file

seancorfield23:06:10

@iagwanderson Records already implement .isEmpty() based on whether they have any fields or not:

user=> (defrecord Foo [])
user.Foo
user=> (.isEmpty (Foo.))
true
user=> (defrecord Bar [x])
user.Bar
user=> (.isEmpty (Bar. 42))
false
user=>

bartuka23:06:57

I noticed that, I was following a java example where the property of being empty for a specific class had a custom definition. I wanted to implement the same and found this "problem"

bartuka23:06:32

I know I could change the name but I got curious about how to go about "overwriting" the default implementation

seancorfield23:06:29

Changing the definition of isEmpty on a record could break all sorts of things in Clojure operations on it, I suspect.

bartuka23:06:14

interesting...

seancorfield23:06:03

I think you'd have to use deftype and build up a map-like data structure from scratch in order to provide custom isEmpty behavior... which would be a lot of work and also produce a custom data structure with "unusual" behavior...

dpsutton23:06:19

or a protocol that has your own custom notion of emptiness

bartuka23:06:24

I didnt get the protocol option @dpsutton

dpsutton23:06:17

i think you could maybe proxy here?

dpsutton23:06:53

Hmm. I’ve never thought about protocols with name clashes

bartuka23:06:21

@dpsutton when you said about using proxy was something like this?

(defrecord Value [])

(def v
  (proxy [Value] []
    (isEmpty [] (println "You are empty"))))

bartuka23:06:55

the error informs we can't inherit from final classes.