Fork me on GitHub
#beginners
<
2021-01-20
>
joe smith00:01:58

anybody know if there is a #crux channel

joe smith00:01:29

nvm it exists already! sweet

joe smith00:01:39

does #datomic exist?

seancorfield00:01:41

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

seancorfield00:01:14

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

seancorfield00:01:19

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

DG00:01:36

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?

seancorfield01:01:44

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

Yang Xu02:01:26

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?

seancorfield02:01:26

@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.

seancorfield02:01:23

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.

zackteo03:01:45

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?))

zackteo03:01:19

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:

alexmiller03:01:11

there is both s/merge and s/or

alexmiller03:01:41

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

alexmiller03:01:22

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

zackteo04:01:30

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

alexmiller04:01:32

in what way does it not work?

alexmiller04:01:38

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

zackteo04:01:13

Let me try that

zackteo04:01:27

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

alexmiller04:01:07

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

zackteo04:01:57

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)))
?

seancorfield05:01:32

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

seancorfield05:01:05

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

alexmiller05:01:31

yeah, wrap the nonconforming around the s/or

alexmiller05:01:01

it conforms with the spec but returns the original value

seancorfield05:01:16

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"

👍 3
alexmiller05:01:38

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

seancorfield05:01:06

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

alexmiller05:01:12

doesn't prevent, just ignores

alexmiller05:01:38

it just conforms, then returns the original value

seancorfield05:01:39

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)

seancorfield05:01:17

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

roelof07:01:15

good morning 😪

Nadav Benyamini08:01:48

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?

pavlosmelissinos08:01:29

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 Benyamini08:01:29

Got it, thanks 👍

alexmiller13:01:06

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

👌 3
alexmiller13:01:47

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

noisesmith16:01:51

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

noisesmith16:01:13

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

noisesmith16:01:13

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

didibus23:01:37

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

didibus23:01:25

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

didibus23:01:09

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

didibus23:01:52

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

Piotr Brzeziński10:01:37

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.

raspasov10:01:13

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ński10:01:02

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

👍 3
raspasov10:01:45

You’re welcome 🙂

andy.fingerhut10:01:27

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ński10:01:51

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

andy.fingerhut10:01:13

repeat has 1-arg and 2-arg variants

roelof15:01:56

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]])

roelof15:01:13

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

dgb2315:01:12

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.

dgb2315:01:15

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

roelof15:01:39

sorry, tried it without any luck

manutter5115:01:40

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

roelof15:01:09

yep, that looks like this :

(defproject paintings "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url ""}
  :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})

manutter5115:01:11

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

roelof15:01:44

just public/css` in the root directory

manutter5115:01:25

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

manutter5115:01:49

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

manutter5115:01:20

(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.)

roelof15:01:59

Maybe I need to change something then at my routes

roelof15:01:24

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

dpsutton15:01:19

is public on your classpath?

roelof15:01:10

yes, it working

roelof15:01:25

I had to make the directory resources

noisesmith16:01:17

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

roelof16:01:37

did make it

roelof15:01:12

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

Michal16:01:00

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.

Michal16:01:22

BTW. I will have to create this list first.

Mno16:01:10

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

Mno16:01:19

lemme check if that works though 😅

dpsutton16:01:19

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

Michal16:01:30

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

dpsutton16:01:13

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

Mno16:01:57

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

alexmiller16:01:55

vector, not vec

alexmiller16:01:02

vec takes a coll

Mno16:01:18

ah good shout. Thanks!

Michal16:01:22

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

🎉 3
dpsutton16:01:52

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

Christian16:01:54

Thank you, I'll try it there

Scott Starkey18:01:00

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-> (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?

pyry18:01:28

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.

tws18:01:08

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

pyry18:01:38

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.

pyry18:01:18

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 Starkey18:01:19

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

pyry18:01:18

Then, perhaps something like

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

Scott Starkey18:01:23

Yeah! I think doseq is what I needed!

Scott Starkey18:01:24

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

pyry18:01:53

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

Scott Starkey18:01:15

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

pyry18:01:06

`

(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))))

pyry18:01:51

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 Starkey18:01:27

Thanks! I will try something like that!

hiredman18:01:51

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

hiredman18:01:24

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

Scott Starkey18:01:07

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

Johan Tjelldén18:01:19

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))

hiredman18:01:34

or

👍 3
😎 3
Nassin19:01:35

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

hiredman19:01:30

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

hiredman19:01:54

> Conventional programming languages are growing ever more enormous, but not stronger. Inherent defects at the most basic level cause them to be both fat and weak: their primitive word at a time style of programming inherited from their common ancestor - the von Neumann computer, their close coupling of semantics to state transitions, their division of programming into a worlds of expressions and a world of statements, their inability to effectively use powerful combining forms for building new programs from existing ones, and their lack of useful mathematical properties for reasoning about programs.

notbad 3
👍 9
phronmophobic19:01:06

thanks for sharing!

noisesmith19:01:42

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

noisesmith19:01:14

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

matt sporleder19:01:15

I think the main problem is "fragility"

noisesmith19:01:02

@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.

noisesmith19:01:29

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

👍 6
caumond19:01:14

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

noisesmith19:01:54

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)

Nassin19:01:00

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

noisesmith19:01:31

@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

noisesmith19:01:10

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++

Nassin20:01:30

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

noisesmith21:01:41

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)

Nassin22:01:05

For clearness, which are those in Clojure?

noisesmith18:01:52

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...)

noisesmith19:01:09

@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)

dgb2319:01:04

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.

Nassin19:01:33

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

noisesmith19:01:03

@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

noisesmith19:01:06

@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

caumond19:01:54

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

hiredman19:01:54

> Conventional programming languages are growing ever more enormous, but not stronger. Inherent defects at the most basic level cause them to be both fat and weak: their primitive word at a time style of programming inherited from their common ancestor - the von Neumann computer, their close coupling of semantics to state transitions, their division of programming into a worlds of expressions and a world of statements, their inability to effectively use powerful combining forms for building new programs from existing ones, and their lack of useful mathematical properties for reasoning about programs.

notbad 3
👍 9
dgb2319:01:01

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.

👍 3
Nassin19:01:11

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

hiredman19:01:08

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

hiredman19:01:32

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

roelof19:01:33

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

hiredman19:01:29

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

sova-soars-the-sora20:01:16

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

Mno20:01:27

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 😅

🙂 3
Michael Stokley20:01:55

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

Michael Stokley20:01:38

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.

seancorfield20:01:46

@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".

👍 3
Michael Stokley21:01:43

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

👍 3
Michael Stokley21:01:49

> 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

dgb2321:01:38

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

Michael Stokley21:01:26

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.

👍 3
caumond20:01:47

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

dgb2320:01:20

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

dgb2320:01:44

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

hiredman20:01:46

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

dgb2320:01:40

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

dgb2320:01:09

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.

caumond20:01:20

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

hiredman20:01:07

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

caumond20:01:13

@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 ...

dgb2320:01:49

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

dgb2320:01:05

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

caumond20:01:15

Sorry, I dont understand why you say it is orthogonal

dgb2320:01:01

As in “has no dependency on each other”

dgb2320:01:18

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

caumond20:01:56

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

caumond20:01:35

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

👍 3
hiredman20:01:43

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

Michael Stokley20:01:50

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

hiredman20:01:24

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

Michael Stokley20:01:34

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

Michael Stokley21:01:48

i'll check out the twitter.

seancorfield21:01:49

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 🙂

dgb2320:01:44

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.

phronmophobic21:01:19

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.

👍 3
yiorgos20:01:34

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?

alexmiller21:01:19

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

alexmiller21:01:38

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

yiorgos21:01:42

sweet! Thank you very much!

Jorge Tovar21:01:40

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

Jorge Tovar22:01:29

Use set + spec and thats all? Nice

caumond22:01:55

Just as concise a clojure. It rocks

Jorge Tovar22:01:29
replied to a thread:`#{}`

Use set + spec and thats all? Nice

alexmiller22:01:47

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

alexmiller22:01:38

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

alexmiller22:01:25

or I guess a vector if there is some necessary ordering

alexmiller22:01:01

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

DG23:01:16

Is there a function in Clojure to check if a number is in a closed-open range? E.g. a <= x < b ? I know I can use the expression (<= 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

DG23:01:37

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

hiredman00:01:23

I would recommend giving the function a name, and not using those symbols

hiredman00:01:58

people have enough trouble with prefix notation using < and >, then the way >= works for multiple arguments leads you into a philosophical crisis

dpsutton23:01:58

not that i'm aware