beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
joe smith 2021-01-20T00:31:58.241Z

anybody know if there is a #crux channel

joe smith 2021-01-20T00:32:29.241300Z

nvm it exists already! sweet

seancorfield 2021-01-20T00:35:45.241400Z

@mathpunk If you're using an up-to-date clojure CLI (1.10.1.697 or later), you can use the -P option. See http://clojure.org/releases/tools

seancorfield 2021-01-20T00:36:13.241600Z

That will just "prepare" things, i.e., download the dependencies you need and then not run anything.

joe smith 2021-01-20T00:37:39.241900Z

does #datomic exist?

seancorfield 2021-01-20T00:38:41.242600Z

@mojitosmi If you click the next to Channels in the left side bar, it lets you Browse channels

seancorfield 2021-01-20T00:39:14.243300Z

You can also try to just switch to a channel (control-k on Windows, cmd-k on Mac) and just start typing.

seancorfield 2021-01-20T00:40:19.244Z

(I can't remember whether the "quick switcher" is enabled by default -- check Preferences > Advanced under Search Options)

2021-01-20T00:56:36.244600Z

What's the recommended approach for dealing with dates and times in clojure? It seems like a bit of a mess. Should I stick with Java 8 APIs?

grazfather 2021-01-20T01:08:56.244700Z

The questions ask whether you use it professionally, and then veer off and assume you do

seancorfield 2021-01-20T01:13:44.245300Z

Either use Java Time directly or a wrapper like cljc.java-time or tick

Yang Xu 2021-01-20T02:32:26.251700Z

Is it possible to mix Java and Clojure to implement some complex projects? I mean here is a Clojure open-source project, I need to reconstruct it, So which is the better way? Reconstruct completely by Java or by mix Java and Clojure?

seancorfield 2021-01-20T02:36:26.252500Z

@xu20151211 You can have a Java app that loads and calls into Clojure code, and you can have a Clojure app that calls into Java code.

seancorfield 2021-01-20T02:37:16.252800Z

See https://clojure.org/reference/java_interop for some examples/background.

seancorfield 2021-01-20T02:43:23.254500Z

When we first adopted Clojure at work (a decade ago now), we used it for low-level stuff and gradually replaced parts of our legacy applications -- so we had mixed language projects, calling into Clojure. Over time we rewrote more and more of them in Clojure.

zackteo 2021-01-20T03:44:45.257900Z

Hello, is it possible to do something like this for spec?

(s/def ::plain-coord
  (s/keys :req-un [::row ::col]
          :opt-un [::sheet]))

(s/def ::merged-coord
  (s/and
    (s/keys :req-un [::first-row ::first-col ::last-row ::last-col]
      :opt-un [::sheet])
    #(<= (:first-row %) (:last-row %))
    #(<= (:first-col %) (:last-col %))))

(s/def ::coord
  (s/or :merged (s/keys :req [::merged-coord])
    :plain (s/keys :req [::plain-coord])))
Or do I have to create a plain-coord? and merged-coord? to do the below
(s/def ::coord
  (s/or :merged-coord merged-coord?
    :plain-coord plain-coord?))

zackteo 2021-01-20T03:47:19.259100Z

Like what I really want is to be able to do something like

(s/def ::coord
  (s/or ::merged-coord 
    ::plain-coord))
but it seems like spec does not work this way :thinking_face:

alexmiller 2021-01-20T03:52:10.259200Z

-P is the preferred way to do this

alexmiller 2021-01-20T03:53:11.259500Z

there is both s/merge and s/or

alexmiller 2021-01-20T03:53:41.259800Z

it's unclear to me what you want or what doesn't work

alexmiller 2021-01-20T03:55:22.261Z

one handy (undocumented) tool for use with s/or is s/nonconforming

alexmiller 2021-01-20T03:56:05.261200Z

as it says at the top, just skip anything that doesn't apply

alexmiller 2021-01-20T03:56:11.261400Z

only the first 2 questions are required

zackteo 2021-01-20T04:12:30.262700Z

Hmmm, I would like for :coord to either take the the form of ::plain-coord or ::merged-coord

alexmiller 2021-01-20T04:13:32.262900Z

in what way does it not work?

alexmiller 2021-01-20T04:15:38.263300Z

(s/def ::coord
  (s/or :merged ::merged-coord 
    :plain ::plain-coord))
should work?

zackteo 2021-01-20T04:18:13.264500Z

Let me try that

zackteo 2021-01-20T04:23:27.266100Z

Thanks, think that should be working! Not sure why I didn't try that just now

alexmiller 2021-01-20T04:36:07.267500Z

if you're conforming, s/or will conform with the :merged or :plain tag which you likely don't want - in that case, wrap the spec in s/nonconforming. if you're just using s/valid? then it doesn't matter

zackteo 2021-01-20T04:41:57.268500Z

just using s/valid? but for subsequent reference, by wrapping you mean ...

(s/def ::coord
  (s/or (s/nonconforming ::merged-coord) 
    (s/nonconforming ::plain-coord)))
?

seancorfield 2021-01-20T05:06:32.269200Z

@zackteo (s/nonconforming (s/or ...)) as I recall...

seancorfield 2021-01-20T05:07:05.269800Z

It turns the whole s/or part into a non-conforming spec, i.e., if it matches, it returns the original data structure.

alexmiller 2021-01-20T05:08:31.270100Z

yeah, wrap the nonconforming around the s/or

alexmiller 2021-01-20T05:09:01.270500Z

it conforms with the spec but returns the original value

seancorfield 2021-01-20T05:09:16.270800Z

user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/conform (s/or :int int? :str string?) 42)
[:int 42]
user=> (s/conform (s/or :int int? :str string?) "one")
[:str "one"]
user=> (s/conform (s/nonconforming (s/or :int int? :str string?)) 42)
42
user=> (s/conform (s/nonconforming (s/or :int int? :str string?)) "one")
"one"

👍 1
alexmiller 2021-01-20T05:09:38.271300Z

we will probably have some kind of nonconforming or option in spec 2

seancorfield 2021-01-20T05:11:06.273Z

I guess it also prevents similar conformance on any nested specs? (In the arms of the s/or) Right, @alexmiller?

alexmiller 2021-01-20T05:12:12.273700Z

doesn't prevent, just ignores

alexmiller 2021-01-20T05:12:38.274300Z

it just conforms, then returns the original value

seancorfield 2021-01-20T05:12:39.274400Z

So you'd get a different result to flowing it through (s/conformer second) (which would still preserve nested conformance and just erase the tag of s/or)

alexmiller 2021-01-20T05:12:58.274800Z

correct

seancorfield 2021-01-20T05:13:17.275300Z

Just something to be aware if you actually need some conformed values deeper in the data (which I've needed occasionally).

2021-01-20T06:47:23.276100Z

You could try this: https://wiki.helionet.org/features/java You'd need to switch to https://github.com/marchrock/ring-tomcat-adapter I think. And I've never tried them.

2021-01-20T07:20:47.276700Z

But generally, people use VPS hosting, which I do not know of any free options

2021-01-20T07:23:15.276900Z

DigitalOcean, Linode, AWS, are popular options, they have things in the 5$ a month range.

2021-01-20T07:23:37.277100Z

I think Heroku might have a free option for MVPs and personal projects, not sure

roelof 2021-01-20T07:47:15.277700Z

good morning 😪

Nadav Benyamini 2021-01-20T08:18:48.280200Z

Hello, I was wondering about the difference between "first" and "peek". Would their results ever be different? Is there maybe a performance difference in some cases?

2021-01-21T23:52:37.083300Z

Ya, peek is given a stack, returns the last inserted element. So its LIFO. first is given an ordered collection, return the first one from its order

2021-01-21T23:53:25.083500Z

And it seems that peek can also be: given a queue, return the first inserted element FIFO

2021-01-21T23:54:09.083700Z

Basically, it does something different based on the type of coll, so just make sure you are aware what type of coll you use it with

2021-01-21T23:54:52.083900Z

So my trick is: You peek from a stack or queue. And Lists and Vectors are also Stacks

pavlosmelissinos 2021-01-20T08:21:29.280300Z

from the docstrings: first:

Returns the first item in the collection. Calls seq on its
  argument. If coll is nil, returns nil.
peek:
For a list or queue, same as first, for a vector, same as, but much
more efficient than, last. If the collection is empty, returns nil.

Nadav Benyamini 2021-01-20T08:22:29.280500Z

Got it, thanks 👍

Piotr Brzeziński 2021-01-20T10:23:37.281800Z

Hi! Would that be a good solution to replicating items in a sequence?

(fn [xs count] (mapcat #(take count (repeat %)) xs))
it works but I’m trying to see if there’s a better way.

2021-01-20T10:26:27.283300Z

I don't know if it is better, but you could use #(repeat count %) in place of #(take count (repeat %)) there to get the same results. Unless one is worried about optimizing run-time performance to the utmost, what you have and that small variation both seem like perfectly good ways to me.

Piotr Brzeziński 2021-01-20T10:26:51.283700Z

Ah, nice, I thought repeat is a single arg function. That makes it easier to read for sure, thanks!

2021-01-20T10:27:13.284Z

repeat has 1-arg and 2-arg variants

Piotr Brzeziński 2021-01-20T10:27:57.284300Z

Awesome. Thanks!

raspasov 2021-01-20T10:34:13.284600Z

Transducer option (slightly more verbose but a bit more “decomplected”):

(let [f (fn [xs cnt]
          (transduce
            (comp
              (map #(repeat cnt %))
              (mapcat identity))
            conj
            xs))]
  (f [1 2 3] 3))
=> [1 1 1 2 2 2 3 3 3]

Piotr Brzeziński 2021-01-20T10:35:02.284800Z

Nice, I’ll save it for future reference since I’m not comfortable with transducers yet. Thank you 🙂

👍 1
raspasov 2021-01-20T10:35:45.285100Z

You’re welcome 🙂

alexmiller 2021-01-20T13:11:06.288400Z

Note that peek returns the value at the insertion point (for stack usage) and for vectors that’s at the end, not the beginning

👌 1
alexmiller 2021-01-20T13:11:47.289300Z

first is the correct function to use if you want the first element

roelof 2021-01-20T15:02:56.290Z

hmm, why do I get a server error when trying to read a css file

(defn create-image-element [{:keys [object-number width height url]}]
  [:html
    [:head
     [:meta {:charset "utf-8"}]
     [:title " Most popular paintings from the Rijksmuseum "]
     [:link {:href "public/css/styles.css", :rel "stylesheet"}]]
    [:body]])

roelof 2021-01-20T15:03:13.290400Z

from the root , there is a styles.css in that directory

dgb23 2021-01-20T15:13:12.291400Z

A guess would be to remove the “public/” part. To test it you’d want to try and access the css file directly in the browser.

dgb23 2021-01-20T15:14:15.292300Z

Typically a static file server points into a directory (often called “public”), while your root directory contains all the source code.

roelof 2021-01-20T15:38:39.292600Z

sorry, tried it without any luck

2021-01-20T15:39:40.292900Z

do you have a project.clj file or a deps.edn file?

roelof 2021-01-20T15:41:09.293300Z

yep, that looks like this :

(defproject paintings "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "<http://example.com/FIXME>"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "<https://www.eclipse.org/legal/epl-2.0/>"}
  :dependencies [[org.clojure/clojure "1.10.0"]
                 [cheshire "5.9.0"]
                 [clj-http "3.10.3"]
                 [ring "1.8.2"]
                 [compojure "1.6.2"]]
  :repl-options {:init-ns paintings.core})

2021-01-20T15:43:11.294Z

You said you have a public/css directory, is that in the project root, or is it in resources/public/css?

roelof 2021-01-20T15:44:44.294600Z

just public/css` in the root directory

2021-01-20T15:45:25.295200Z

It’s conventional to have resources/public/css, and you may be libraries that are expecting that.

2021-01-20T15:45:49.295600Z

You might want to have a look at the “Static Files” section at https://learnxinyminutes.com/docs/compojure/

2021-01-20T15:46:20.296200Z

(I had to google that--I’ve switched from using compojure to using reitit, and I didn’t trust my memory of how compojure works.)

roelof 2021-01-20T15:47:59.296700Z

Maybe I need to change something then at my routes

roelof 2021-01-20T15:48:24.297100Z

(defroutes app
  (GET "/" [] (-&gt; (display-data)
                  (convert-to-hiccup)
                  (hiccup/html)))
  (GET "/favicon.ico" [] ""))

dpsutton 2021-01-20T15:50:19.297500Z

is public on your classpath?

roelof 2021-01-20T15:53:10.297700Z

yes, it working

roelof 2021-01-20T15:53:25.298200Z

I had to make the directory resources

roelof 2021-01-20T15:54:12.299Z

and add this to my routes (route/resources "/")

2021-01-20T15:56:08.299200Z

:thumbsup:

2021-01-20T16:04:51.299400Z

except for PersistentQueue, which as designed peeks from the opposite end of its conj

2021-01-20T16:05:13.299600Z

(it's much more rarely used than lists and vectors though)

2021-01-20T16:07:13.299900Z

also, as I correct "seqs" to "lists" above - peek only works with stacks/queues, so doesn't actually work with things like lazyseq

Michal 2021-01-20T16:10:00.302800Z

Hi Guys, I have no experience with a clojure, I have to add a new line into the clojure logic, I have a list of strings: "a", "b", "c" etc... I need to return true/false if the following list contains a string equal to the op-name variable. If the list cantains the op-name I need to return true, otherwise false. Could you help me with a quick snippet for this. Will appreciate that.

2021-01-20T16:10:17.303Z

the normal thing is to have resources/public/css and resources/public/js etc. since resources is used for other things not just web assets

Michal 2021-01-20T16:10:22.303300Z

BTW. I will have to create this list first.

Mno 2021-01-20T16:12:10.304800Z

(some (set [op-name]) list-of-strings)

Mno 2021-01-20T16:12:19.305500Z

lemme check if that works though 😅

dpsutton 2021-01-20T16:12:19.305600Z

some could be a useful function for you. "Returns the first logical true value of (pred x) for any x in coll," So (some (fn [x] (= x op-name)) my-list) would return whether that list had a member equal to op-name

Michal 2021-01-20T16:13:30.306700Z

Thank you guys, does the list of string should be a comma separated like (some (set [op-name]) "a","b","c") ?

dpsutton 2021-01-20T16:14:13.307400Z

commas are literally whitespace in clojure. ["a" "b" "c"]

Mno 2021-01-20T16:15:57.309100Z

Also available options: (list "a" "b" "c"), '("a" "b" "c") , (vector "a" "b" "c") All arguably less pretty

Michal 2021-01-20T16:17:22.309800Z

Nice! Thank you for a quick help! Have a good day/afternoon 🙂

🎉 1
alexmiller 2021-01-20T16:22:55.310100Z

vector, not vec

alexmiller 2021-01-20T16:23:02.310300Z

vec takes a coll

Mno 2021-01-20T16:23:18.310500Z

ah good shout. Thanks!

roelof 2021-01-20T16:23:37.310800Z

did make it

Christian 2021-01-20T16:37:57.311700Z

pure vs. referential transparency. Is it the same thing? This article is confusing: https://edward-huang.com/functional-programming/tech/programming/scala/2020/01/30/pure-function-vs-referential-transparency/

dpsutton 2021-01-20T16:40:52.312800Z

not a great article. its premise is that pure functions are a proper subset of referentially transparent functions. Then in an edit, the author says "If a function is referentially transparent, it is also pure." Probably not worth the time if the original was so off the cuff and probably good for #off-topic rather than #beginners

Christian 2021-01-20T16:43:54.313Z

Thank you, I'll try it there

Scott Starkey 2021-01-20T18:29:00.318200Z

Hi folks - I’m a bit of a beginner, and I’m working on a project that takes a CSV file (through Java interop), does some processing on it, and exports the new file to [filename]-processed.csv. I’m using https://github.com/clojure/data.csv/ and it works splendidly!

(with-open [reader (io/reader file-info) writer (io/writer new-file)]
      (as-&gt; (csv/read-csv reader) $
            (map #(eval-line % header-info) $)
            (csv/write-csv writer $)))) ; ALL THIS WORKS! 
However, these are pretty big files, so the program takes about 15 seconds, and looks like the system locks up. Since I’m already using Java interop, I’d like to add a Java progress bar to show progress. However, I’m having problems wrapping my head around how I would implement it with the lazy processing above. Would it even be possible?

pyry 2021-01-20T18:39:28.319500Z

I think you could decompose the problem into two parts: 1) calculating the progress and 2) reporting the progress. I understand you have something (ie. rendering a progress bar) in mind already for 2) above.

2021-01-20T18:40:51.320900Z

use reduce (over a possibly partitioned set of data) instead of just passing all the data to write-csv

tws 2021-01-20T18:41:33.321300Z

checkout https://github.com/thebusby/iota

tws 2021-01-20T18:42:08.322200Z

depending on what your processing needs are - sequential access is a lot slower, reducers are much faster.

2021-01-20T18:42:24.322600Z

(and don't use as-> when ->> will do)

pyry 2021-01-20T18:43:38.322700Z

For calculating progress (number of lines processed / number of lines in total), you'd need to know the number of lines in total when processing each individual line. You could perhaps make a first pass through the data to just calculate the number of lines if efficiency is not a concern; or perhaps you could figure out the number of lines to process via some other route. Then perhaps just process the data in batches matching some fraction of the total number of lines and reporting progress once per batch.

pyry 2021-01-20T18:46:18.323700Z

Oh, as hiredman also mentioned, you'd need to pass batches of data to write-csv for this to work, instead of trying to write the whole thing in one go.

Scott Starkey 2021-01-20T18:46:19.323900Z

I’ve written a defn to tell me the number of lines.

Scott Starkey 2021-01-20T18:48:07.325700Z

@hiredman Thanks about the as-&gt; comment. I think I needed it at one point, now I don’t.

2021-01-20T18:48:19.325800Z

Hi! Is there any nice standard function for the use-case of "if expr is true, return expr otherwise return other"

(if expression
    expression
   (other-expression))

2021-01-20T18:48:34.326100Z

or

👍 1
😎 1
2021-01-20T18:48:48.326400Z

thanks 🙂

2021-01-20T18:48:55.326600Z

to easy

pyry 2021-01-20T18:50:18.326700Z

Then, perhaps something like

(doseq [batch (partition-all batch-size lines)]
    (report-progress!)
    (write-csv writer (map #(eval-line % header-info) batch)))

Scott Starkey 2021-01-20T18:51:23.326900Z

Yeah! I think doseq is what I needed!

Scott Starkey 2021-01-20T18:52:24.327100Z

How would (report-progress!) know what line we’re on, however?

pyry 2021-01-20T18:52:53.327300Z

Oh, right. You'd need to pass it some parameters to suit your needs.

Scott Starkey 2021-01-20T18:53:15.327500Z

Is there any way to get a counter on a doseq?

pyry 2021-01-20T18:54:06.327700Z

`

(let [progress (atom 0)]
  (doseq [batch (partition-all batch-size lines)]
    (swap! progress inc)
    (report-progress! (* @progress batch-size))
    (write-csv writer (map #(eval-line % header-info) batch))))

pyry 2021-01-20T18:55:51.327900Z

Or perhaps

(doseq [[i batch] (map-indexed vector (partition-all batch-size lines))]
    (report-progress! (* (inc i) batch-size))
    (write-csv writer (map #(eval-line % header-info) batch)))

Scott Starkey 2021-01-20T18:58:27.328100Z

Thanks! I will try something like that!

Nassin 2021-01-20T19:10:35.330300Z

In a video Hickey alludes place oriented programming (even if it's update-in-place) is bad, what problems does PLOP cause?

2021-01-20T19:13:42.331100Z

for one thing, it means you need to understand every function that has access to that place in order to understand the code

2021-01-20T19:14:14.331700Z

but as I recall he went into quite a bit of detail in that same talk

matt sporleder 2021-01-20T19:14:15.331800Z

I think the main problem is "fragility"

2021-01-20T19:16:02.333600Z

@kaxaw75836 as a counterexample: when debugging clojure code, I can usually "capture" a value as it appears in a function, and then debug my code based on that value in isolation. In a C++ program that rarely if ever helps, and I need a stepping debugger to understand what happens to the places the function sees.

2021-01-20T19:16:29.334100Z

in good clojure code, what matters is the value, the place isn't my concern

👍 2
caumond 2021-01-20T19:19:14.336100Z

This is one of my favorite clojure advantage. Even if I know this is not an exclusivity

2021-01-20T19:19:54.337400Z

right, even in java you can snapshot some object out of a function to play with it in test code (in c++ this probably leads to a memory leak, or accessing freed data - place problems)

Nassin 2021-01-20T19:20:00.337600Z

@noisesmith are you referring to immutability? or abstracting the machine?, if so is no different in JS or Python?

2021-01-20T19:20:31.338200Z

@kaxaw75836 I'm referring to the difference between thinking about places as the building blocks of code, vs. thinking of values as the building blocks

2021-01-20T19:21:10.339300Z

it's different than JS or Python because most JS and Python code still works in terms of updating hidden internal state, but those are still less place-bound than c++

2021-01-22T18:03:52.128500Z

sorry, lost track of this yesterday I might have lost my point along the way here, but on further thought it really does come down to immutability (at least on the interface level). vectors and hash-maps do contain mutable internal state, but that's there because it has to be there, and a given object will give you back the same value every time you reference it. but, things like io streams and container types are there specifically to represent a state. that is, their utility is to provide access to something stateful, or create a stateful object over stateless internals) compare this to java / js / c++ where it's normal to have mutable private fields that actually do effect the behavior of the object, and many objects require specific initialization before usage - they are designed to mix state and value rather than separating them but I fear I've gotten too abstract here, and others might provide a better explanation than what I'm going for here (I was motivated to try answering this because I've been trying to go back to C / C++ lately but wanting to use them in a more fp way...)

2021-01-20T19:22:09.341100Z

@kaxaw75836 but this is a very general thing - it even applies to the difference between managing the contents of a tree of files (places) vs. data in a db (values with other values that describe their relations)

2021-01-20T19:23:30.342900Z

Backus's 1977 turing award lecture "Can Programming Be Liberated from the von Neumann Style?" is trying to solve the problems created by plop, so it also describes those problems to some degree

dgb23 2021-01-20T19:24:04.344700Z

IMHO PLOP has two aspects that are worth considering. Like @noisesmith said, it decreases your facility for reasoning. Another aspect is increasing brittleness: code and data that change over time propagate changes to rigid structures around them.

Nassin 2021-01-20T19:24:33.345900Z

talking about DBs, he mentions tables in RDBMS is also a place(bad) that creates coupling, and that documents are also a place that create coupling

2021-01-20T19:25:03.346700Z

@denis.baudinot right, in imperative place oriented code 90% of your code is ad-hoc inline adapters between ad-hoc data structures, this kind of thing doesn't even need to exist in clojure

2021-01-20T19:26:06.348600Z

@kaxaw75836 all programs have places, perhaps I picked a bad example, but this isn't a black/white thing, it's a way of guiding a design

caumond 2021-01-20T19:26:54.349700Z

The point is that you could use java with only immutable object and the place orientation of java is less an issue.

dgb23 2021-01-20T19:34:01.355800Z

Clojure is not the only community that concerns itself with disentangling from PLOP. Game programmers have introduced Struct of Arrays, Data Oriented Design and Entity Component systems. They use data structures that are more relational and adaptive, mostly for performance reasons (CPU caching) but this has also a decoupling effect and generalises functionality. Frontend developers are shifting towards using frameworks like React etc., which have a clearer separation of state, data flow and rendering. The problems around PLOP seems to be unspecific to this community.

👍 1
Nassin 2021-01-20T19:34:11.356100Z

1977 wow, the summary looks pretty good high level overview will have a read

phronmophobic 2021-01-20T19:36:06.357500Z

thanks for sharing!

2021-01-20T19:36:08.357700Z

he also is critical of lambda calculus based programming languages, because he wants a more restricted basis to make things easier to reason about

2021-01-20T19:37:32.358500Z

the system he describes sort of sounds like coding directly in a combinatory logic like SKI, but less bare bones, which seems kind of like a nightmare

roelof 2021-01-20T19:42:33.359Z

so he also do not like Haskell which I like a lot 😢

2021-01-20T19:49:29.360100Z

https://crypto.stanford.edu/~blynn/compiler/ may be of interest, it is a series of blog posts writing a haskell compiler and it goes haskell -> lambda calculus -> ski, it is very cool

Nassin 2021-01-20T20:04:30.360600Z

By hidden internal state, you mean like some reference of a object that is pass around? or the runtime?

sova-soars-the-sora 2021-01-20T20:14:16.361500Z

hi what's your favorite way to do websockets or ajax in clojureland?

2021-01-20T20:21:55.366800Z

here's a question i've been wondering about for some time. in the java community (and others) folks will often favor so called "clean code". that can mean all sorts of things, so i'l be more specific. stuff like "one level of abstraction per function". decomposing functions into tiny helpers with "descriptive" names that hide the mechanical details of what you're doing. adding in domain concepts, in the form of function or object names, that hide the mechanical details of what you're doing. i'm in a clojure shop right now, having come from a java shop, and i'm finding that there is a high high tolerance for long functions with many levels of abstraction and few, if any, named helpers to express what you might call intent. just big expressions. stuff that would never have made it past code review at my last place. what do folks think? is there a deliberate preference for more inline, nested, low level expressions over extracted helpers with names? or is it just a tolerance thing? i'm very curious

2021-01-20T20:25:38.368900Z

there's a Martin Odersky talk (that i can't find right now) that expresses this well. he says, well, in scala, we have all these great abstractions and general purpose data manipulation functions (a la clojure). and so you could write a dense, multi-step transformation. but, according to Odersky, that would be a mistake because it would be too impenetrable for future maintainers.

seancorfield 2021-01-20T20:26:46.370100Z

@michael740 I would say that Clojure in general encourages small, pure functions, with good names, that isolation mutable state to the edges of the system -- but not all Clojure programmers produce such code, just like not all Java developers write "clean code".

👍 1
dgb23 2021-01-20T20:29:32.372500Z

There is no right and wrong here. http://number-none.com/blow/blog/programming/2014/09/26/carmack-on-inlined-code.html

👀 1
caumond 2021-01-20T20:29:47.373300Z

Clojure advocates dsl which I find very close to language metaphor used in clean code

dgb23 2021-01-20T20:31:20.375900Z

I think there is no point in putting things into defs that don’t need to be referenced. We have reader macros and a repl to evaluate/ignore things at the expression level

dgb23 2021-01-20T20:31:44.376800Z

Structure is essential, but wether that structure is composed of function declarations is almost orthogonal to this

2021-01-20T20:31:46.376900Z

The "clean code" concept comes out of the culture of enterprise consultants. The clojure community is smaller, so it attracts fewer of those.

dgb23 2021-01-20T20:32:40.377300Z

There is also something to be said of the mental overhead of naming things.

dgb23 2021-01-20T20:35:09.381600Z

having said that, i usually write small functions, it comes naturally. But some functions are just large lists of doing things in sequence, explicitly, that are the kind of functions where keeping them large makes sense.

caumond 2021-01-20T20:35:20.382100Z

@denis.baudinot, as told in clean code, I find helpful to tell the intentions. Small functions help to do so

2021-01-20T20:37:07.385300Z

there are also many critiques of the different formulations of "clean code"

caumond 2021-01-20T20:38:13.386500Z

@denis.baudinot, even in the case you mention, I find helpful to explode in small local function, it helps readibility and testability . Maybe im too young in the language ...

dgb23 2021-01-20T20:38:49.387500Z

it’s orthogonal to clojure wether a function/procedure should be large or not

dgb23 2021-01-20T20:39:05.388Z

you can visually structure a large function to make it nicer to read

caumond 2021-01-20T20:41:15.389600Z

Sorry, I dont understand why you say it is orthogonal

dgb23 2021-01-20T20:41:18.389800Z

again, I don’t really write a lot of large functions anyways. I’m rather against the notion that there should be some kind of limitation to this

2021-01-20T20:41:43.390200Z

if you haven't read elements of clojure yet (I am behind the times and haven't finished it yet), I don't think it mentions "clean code" by name, but it functions to some degree as a critique of "clean code" and similar stuff

caumond 2021-01-20T20:41:56.390300Z

Ah ok. So we re on the same page. Rules are made to be broken

dgb23 2021-01-20T20:42:01.390500Z

As in “has no dependency on each other”

Mno 2021-01-20T20:42:27.390700Z

The only thing I’ve ever used for either is cljs-ajax and sente, and I’ve never had a problem, but I also haven’t worked in large projects with multiple people or tried out a lot of libraries… so might as well bring an entire salt lick for this piece of advice 😅

🙂 1
caumond 2021-01-20T20:42:35.390900Z

But this was only a tendancy I was mentionning not a rule

👍 1
2021-01-20T20:42:50.391100Z

i have read elements of clojure, yes. i'll revisit with that in mind

roelof 2021-01-20T20:45:54.391400Z

thanks

2021-01-20T20:51:24.392200Z

ztellman's(author of elements of clojure) last few days of tweets are him being annoyed that bob martin(author of some clean code book) exists and critiquing an exercise bob did

2021-01-20T20:53:34.394400Z

i can see that. bringing up "clean code" is a... hornet's nest, for sure

dgb23 2021-01-20T20:58:44.399900Z

Ok here is a more specific follow up question: To build up application state, aka ‘load’ we usually do a whole bunch of plumbing in sequence, connect to a db, read some files, load in configuration… Should this be a large, single procedure, where the effects are explicit, in your face and the sequence of steps is clear? Or should this be composed of a abstracted, possibly data-driven configuration system? I think the first one is easier to understand and more “honest”, while the second opens up opportunities to build stuff that is about that process, which has operational benefits.

yiorgos 2021-01-20T20:59:34.400600Z

I want to read the attributes of a file and for that I am using https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#readAttributes-java.nio.file.Path-java.lang.Class-java.nio.file.LinkOption...- like this (Files/readAttributes (io/file "file-path") BasicFileAttributes []) but I am getting an exception error: java.lang.IllegalArgumentException: No matching method readAttributes found taking 3 args how can I represent java varargs in Clojure?

alexmiller 2021-01-20T21:00:28.400900Z

https://clojure.org/guides/faq#varargs

2021-01-20T21:01:48.401100Z

i'll check out the twitter.

alexmiller 2021-01-20T21:04:19.401900Z

in this case you might want to use something like: (Files/readAttributes (.toPath (io/file "file-path")) (into-array LinkOption [LinkOption/NOFOLLOW_LINKS]))

alexmiller 2021-01-20T21:04:38.402300Z

note that io/file returns a http://java.io.File but that method takes a Path

yiorgos 2021-01-20T21:05:42.402700Z

sweet! Thank you very much!

2021-01-20T21:08:43.402800Z

this is an interesting essay. a lot of what he seems to be concerned about would be mitigated by pure functions, if i'm understanding this correctly

👍 1
2021-01-20T21:09:49.403Z

> Besides awareness of the actual code being executed, inlining functions also has the benefit of not making it possible to call the function from other places. That sounds ridiculous, but there is a point to it.... > > Strictly functional functions that only read their input arguments and just return a value without examining or modifying any permanent state are safe from these types of errors, and the nice ability to formally speak about them makes them a good ivory tower topic, but very little of our real code falls into this category. quite a lot of the clojure code i work on does indeed fall into that category

phronmophobic 2021-01-20T21:15:19.403200Z

I think it depends. Sometimes you want a dynamic system where capacity and capability can change while the program is running. Sometimes you want a system that is mostly fixed and predictable. Sometimes you want a little of both. It depends on the use case.

👍 1
dgb23 2021-01-20T21:15:38.403400Z

He also advocates for a functional style and its tradeoffs here: https://gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php

seancorfield 2021-01-20T21:17:49.403700Z

Oh, yeah, that thread was pretty funny. Zach challenged Bob on (poor) naming choices and Bob was so condescending in response and then cut the discussion short. Bob doesn't like being challenged -- don't be like Bob 🙂

2021-01-20T21:18:26.404100Z

come to think of it, brian will advocates for inlining helpers. he says it decreases the total surface area of the code base, making it more tractable.

👍 1
2021-01-20T21:52:41.404400Z

I mean that most code relies on objects with stateful internals (clojure has these too, but most of the core language is a set of tools for segregating states from values)

Jorge Tovar 2021-01-20T21:57:05.404700Z

Hello

Jorge Tovar 2021-01-20T21:57:40.405700Z

One question. How can I define a fixed set of values to a map attribute. Like an enum in java

2021-01-20T21:59:30.406100Z

#{}

caumond 2021-01-20T22:04:55.406600Z

Just as concise a clojure. It rocks

Nassin 2021-01-20T22:15:05.406700Z

For clearness, which are those in Clojure?

alexmiller 2021-01-20T22:33:47.407600Z

in general, you usually don't need analogs to Java enums at all

alexmiller 2021-01-20T22:35:38.409600Z

either just use the constant values, or use :keywords for options (instead of public static final String blah). if you really need to collect them (for use of the full set somewhere), put them in a set or sometimes a map if there is ancillary info

alexmiller 2021-01-20T22:36:25.410100Z

or I guess a vector if there is some necessary ordering

alexmiller 2021-01-20T22:37:01.410600Z

you may be tempted to (def my-option :my-option) - resist the urge, just use :my-option

2021-01-20T23:57:16.413300Z

Is there a function in Clojure to check if a number is in a closed-open range? E.g. a &lt;= x &lt; b ? I know I can use the expression (&lt;= a x b), but that's a closed-closed range. I also know that I can use an and to achieve what I want, but I would prefer something more succinct or more math-aligned. Any suggestions? Thanks

2021-01-20T23:58:37.414100Z

I'm considering defining a function &lt;=&lt; that does that, but I'm guessing there should be something like this already done in core?

dpsutton 2021-01-20T23:58:58.414300Z

not that i'm aware