Fork me on GitHub
#clojure
<
2019-08-24
>
Quest00:08:55

Anyone have an example of a cljc library/template? Looking for an example to help write my own. Bonus points if it uses command-line clj

seancorfield00:08:11

@quest Can you clarify what you're actually looking for? I mean, don't you just write Clojure with a .cljc file extension and avoid doing Java interop?

seancorfield00:08:39

(and avoid macros 🙂 )

seancorfield00:08:08

This project uses @cgrand’s macrovich library to make macro handling a lot simpler https://github.com/jkk/honeysql -- but it's currently a Leiningen project (although it has deps.edn too).

seancorfield00:08:22

Also tools.cli is a .cljc project that uses deps.edn for running both Clojure and ClojureScript tests https://github.com/clojure/tools.cli

Quest18:08:24

I modded this to to work as a basis for my own library, but there was an extra gotcha that took me a bit to figure out: The clj+cljs runner tooling itself uses tools.cli to initialize. When I tried stripping parse-opts out of the tools.cli namespace it caused the test runners to recognize zero tests (clj) or immediately stop with no output (cljs). Once I stripped tools.cli out entirely & cut :override-deps {org.clojure/tools.cli {:local/root "."}}, tests worked as expected. I almost gave up & went to use the lein honeysql as a base, but I'm glad to finally have some working deps.edn. Thanks again!

seancorfield00:08:49

(it doesn't contain any macros tho')

seancorfield00:08:33

At some point I'll switch honeysql completely over to CLI/`deps.edn` but that's not terribly high on my priority list right now.

👍 4
Quest00:08:21

I'd like to publish a library artifact for both -- the main thing I'm looking for in a template is a nice way to run tests on both sides

Quest00:08:23

tools.cli looks like a great simple example. I'll take from that, thanks 🙂

Lone Ranger01:08:39

I love Clojure but I didn't come from a Java background, can someone explain to me what the letters impl imply? like something.something.impl.something

Lone Ranger01:08:38

Is it just a Java thing for concretions or does it have a broader CS implication?

jumpnbrownweasel01:08:29

impl is usually a package for classes that implement an interface or an API of some kind, that is defined elsewhere.

Lone Ranger01:08:26

do you think it would be fair to use for higher order functions also? i.e.

(ns foo)

(defn foo-generic [a] (fn [b] (a + b)))

(ns foo.impl (:require [foo]))

(defn foo (foo/foo-generic 1))
?? or am I doing this wrong?

Lone Ranger01:08:43

Or is this purely an OO/Java thing?

jumpnbrownweasel01:08:13

I was referring to the typical use of impl in Java code. I don't know whether it's typically used for clojure code in any particular way.

Lone Ranger01:08:34

yeah, naming things is hard, I'll steal whatever conventions I can get ...

jumpnbrownweasel01:08:01

just a reaction: instead of foo.impl for me it would be more understandable to see a name that described the type of implementation, whatever is non-generic about it.

Lone Ranger02:08:14

that's true. I wonder if there's a "generic" version. And I think it's better if the adjective comes first, like, adjective.domain, i.e., impl.domain or impl.foo vs foo.impl

Lone Ranger02:08:25

I wonder what the convention is :thinking_face:

Lone Ranger02:08:18

I think adjective first. i.e., org.clojure or com.cognitect

jumpnbrownweasel02:08:04

i think it's usually category.sub-category.sub-sub-category in some sense.

Lone Ranger02:08:25

yeah that makes sense, broad to specific

jumpnbrownweasel02:08:16

yeah, that's a better way to say it

dpsutton01:08:11

I believe it’s just short for implementation

Lone Ranger01:08:13

Yeah I'm just trying to figure out what it "means" and how I would use it ... like, does that also imply there's an abstract-er version of the same code somewhere else?

Lone Ranger01:08:42

And it is only Java/OO or can it apply to higher order functions and/or denormalized data or what

dpsutton02:08:16

Do you have a project where you see it?

Lone Ranger02:08:12

no but I do have a namespace sprawl issue in my own project 😐

deleted03:08:58

is there a function where no matter what it's called with it returns true?

mpenet06:08:18

any? works too

dpsutton03:08:21

(constantly true)

emccue05:08:55

@goomba the impl thing usually implies that its a namespace/package for "implementation details" you don't want consumers to use

emccue05:08:07

consumers probably being "the rest of your project"

emccue05:08:16

(and i give up on finding more - im just trusting that they exist)

emccue05:08:56

so if you had some subsystem that you described with a protocol/set of functions

emccue05:08:37

then it would be a design decision you could make to move details about constructing and managing backing bits to an impl namespace

emccue05:08:00

its equally as valid to put all that in the regular ns and mark it private or "dont use"

emccue05:08:42

personally - i lean toward the pythonic "flat is better than nested"

emccue05:08:09

does that make sense?

😮 4
Lone Ranger09:08:23

yes it does!! That makes a ton of sense.

seancorfield05:08:00

@goomba If it helps, consider the choice between a single namespace like foo.bar which contains a bunch of public functions and a bunch of private functions that are implementation details for the public functions. Or you can have just the public functions in foo.bar and then move all the private functions into foo.bar.impl (and make them public so they can be called from foo.bar).

Lone Ranger09:08:47

Ahaaa it's all so clear now

dominicm08:08:19

It can be handy to have an impl namespace with publics in because then macros can refer to them

Silenced Ego08:08:04

I see in FP there are two ways to transform a sequence into a single value, using reduce or using a custom fn that recursively processes the list. Just curious, are there any other ways or are those the main two?

Silenced Ego08:08:18

also assuming that it's some sort of calculation on the whole of the list, not simple like getting head or tail

andy.fingerhut08:08:52

It seems likely that there are other things one could possibly do to a list to get a single value as a result, but my mind is not jumping to an example off-hand. reduce and a custom function that recursively (or iteratively) processes the list are certainly the main two ways.

andy.fingerhut08:08:37

I mean, sorting a sequence produces "a single value" from a certain perspective, where that single value is also a sequence, but probably not included in what you mean in your question.

andy.fingerhut08:08:17

The "single value" that the reduce function returns can be "small", like an integer, but it can also be a collection that is even larger than the original sequence.

Lone Ranger09:08:51

@thomasskinner76 reduce, accumulate, and fold, et al., depending on your language background, just means to successively apply a binary operation (like +) to items in a sequence until there's nothing left in the sequence. It's more of a conceptual thing (I think). From a purely pedantic perspective, count, first, and last also take a sequence and return a scalar value (but I know that's not what you meant).

Lone Ranger09:08:31

Going back to SICP, sometimes it's more useful to think of it in terms of '`selector`' functions, '`constructor`' function, and '`constraint'` functions. The idea of a selector is to get a view into some data... i.e., count gives a selection of the data of a sequence. What you're pointing out though is that sometimes it's confusing because technically count is also a constructor for an integer. So it comes back to intentionality. reduce is just applying a binop to a sequence repeatedly -- but was your intention to get a view into your sequence, or to construct a scalar? Depends on the application

thiru15:08:09

I'm having some strange behavior when trying to run ripgrep from the repl. The following always gives me no results with an error code of 1 (which means nothing was found). But when I run it from the terminal I always get results. I'm sure I'm doing something stupid.. any ideas?

noisesmith17:08:32

should your two terms actually be separate args rather than one arg with a space in it?

noisesmith17:08:39

sh does no arg parsing

noisesmith17:08:54

(it doesn't actually invoke any shell - it is using OS level exec directly)

thiru15:08:26

(sh "rg" "search terms")

andy.fingerhut16:08:51

@thiru Do you know what the current directory is of the JVM when that (sh ...) form is evaluated? Perhaps it is different than when you are running ripgrep from the terminal?

thiru16:08:09

@andy.fingerhut It seems like the directory is correct. E.g. when I run (sh "pwd") it is what I expect. I also tried specifying the directory to sh like (sh "rg" "search terms" :dir "/search/dir") ... but no cigar

andy.fingerhut16:08:53

When you type it at the terminal and succeeds, is it multiple terms separated by spaces, like this? rg search terms ?

andy.fingerhut16:08:10

If so, and you are not doing any special quoting in the terminal to combine those into one argument, then the equivalent in Clojure would be (sh "rg" "search" "terms")

thiru16:08:18

Actually I'm not even using multiple search terms.. I used that to make the example clear I'm actually just running (sh "rg" "test")

st3fan16:08:31

this is a bit odd and maybe a feeling i have as a result coming from other languages, but it feels things are going a bit too easy - i get a lot done with little code

andy.fingerhut16:08:53

Take that feeling and run with it! 🙂

st3fan16:08:04

one example is this: coming from Go, i’m used to checking a lot of error conditions - in Clojure i’ve just been writing straightforward code that works, but it is mostly happy path - i don’t know yet how to deal with errors/exceptions

seancorfield17:08:35

For expected errors we tend to either return nil (if that isn't otherwise a valid result) or a hash map with either :result (for success) or :error (for failure -- a message or whatever your app needs).

seancorfield17:08:21

For unexpected errors we tend to rely on the platform's native exception system with try/`catch` and throw (and ex-info if you want to convey data with the exception).

st3fan23:08:49

Thank you that is good guidance

slipset10:08:11

I think it’s also worth doing as much error-checking as possible up front. That way, there is more happy-path. One might also argue that it might be a good separation of concern.

slipset10:08:58

As a fun example, consider the Clojure string functions. They all throw on nil strings because Java. But if you combine these fns with fnil, you can worry about this, but at a different level.

st3fan13:08:33

@U04V70XH6 something like this?

st3fan13:08:35

(defn create-task [token content]
  (let [r (client/post (str rest-api "/tasks") (create-task-options token content))]
    (if (= (:status r) 200)
      {:result (:body r)}
      {:error (select-keys r [:status :body])})))

seancorfield17:08:26

@st3fan Yup, that's exactly what I mean. We "expect" an HTTP operation could fail so it's an "error" case, not an "exception" case. @U04V5VAUN Interesting point about clojure.string functions and fnil!

andy.fingerhut16:08:32

@thiru0130 I tried installing ripgrep on my Mac and am seeing similar behavior. My next guess would be that ripgrep is accessing the terminal settings for color highlighting or perhaps interactive keyboard use in a way that the REPL environment causes it to fail.

thiru16:08:45

Yeah I thought that too. I tried setting the option --color never but that didn't help

thiru16:08:59

What does work is if you specify the file to search (e.g. (sh "rg" "test" "file.txt")). But this won't work for me since I need to search an entire tree and have the results sorted by ripgrep..

David Pham16:08:56

@st3fan Clojure has a crazy debugger mode :) I can’t say the same for CLJS though xD

andy.fingerhut16:08:31

@thiru0130 My only other current guess is that the environment variables passed into the rg process are different when run from terminal vs. Clojure's sh function, but off hand I'm not sure how to test that -- Oh, one way would be to make a several line long shell script that only prints its environment, then calls rg.

thiru16:08:03

@andy.fingerhut by printing environment what do you mean exactly? The system PATH or all environment variables? Not sure how to do the latter

andy.fingerhut16:08:17

The output of the command printenv

andy.fingerhut16:08:42

all environment variables and their values

andy.fingerhut17:08:42

I saw a warning in rg's man page that it can abort unexpectedly in the default scenario when --mmap is given, which is enabled by default. But even when I disabled that via --no-mmap, it did not help.

thiru17:08:32

Ok cool thanks I'll see if I can find a difference between the environments.. such a weird issue. Yes I tried the mmap option too. I looked through the entire man page and looked at everything that has a difference when rg detects it's running from a terminal but none of them helped

andy.fingerhut17:08:55

I also saw in ripgrep man page the existence of --debug and --trace options, but enabling those didn't give me much of any clues.

thiru17:08:40

.. yup same here

thiru17:08:58

It's ok thanks for the help Andy. What I'm going to do is just read all the content into postgres and search from there.. maybe better since I can leverage FTS as well

st3fan17:08:49

What is a good resource for Clojure patterns?

dominicm17:08:56

What kind of thing do you mean?

dominicm17:08:07

http://mishadoff.com/blog/clojure-design-patterns/ This article is a good example of translating oo ones

Silenced Ego20:08:52

If I want an associative container like a map, but I want to be able to use integer keys (keyword constructor doesn't allow plain integers) which data structure would I use?

Mark Addleman20:08:00

The following works

{1 "hello" 2 "goodbye"}

Mark Addleman20:08:56

Although clojure maps often have keyword keys, any clojure value is allowed to be a key

Silenced Ego20:08:03

oh wow, thanks

🙂 4
jahson20:08:23

Yeah {{:a 1} 2, {:b 3} 4} is legit too

potetm21:08:43

@thomasskinner76 Also consider a vector. (If your keys are contiguous.) e.g. (get ["hello" "goodbye"] 0) works fine.

nulligor21:08:22

does anyone know of a good codebase to study for a back-end event-based clojure architecture?

nulligor21:08:37

not really caring if it runs on open source or specifically on a particular cloud provider, just wanna get some insights into what the architecture might give to me

slipset09:08:34

Also, I think you should have a look around the various resources available on Datomic Ions.

borkdude21:08:35

if I want row and col metadata on my EDN values after reading from a string, I guess I'll have to use tools.reader or rewrite-clj, since there is no built-in support for this right?

andy.fingerhut23:08:26

Hmm, I have a vague half-memory that such support was added to Clojure itself .. will check in a moment. tools.reader definitely has it