beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
kj 2021-05-14T11:38:17.211100Z

I've got a couple of related functions in a file that I'd like to be able to call directly from the command line. What's the recommended way of doing that? Would it just be to parse some command line options and call the appropriate function from -main? If so, are there any Clojure libraries similar to https://github.com/google/python-fire or https://click.palletsprojects.com/en/8.0.x/ in Python, for quickly putting together simple CLIs?

2021-05-14T12:08:04.213700Z

@kiran.joshi If you use Clojure CLI, the -X exec option should be what your looking for. For your second question AFAIK: • https://github.com/clojure/tools.clihttps://github.com/l3nz/cli-matic

👍 1
2021-05-14T12:12:13.214900Z

How do Datalog and Specter queries compare? I don’t fully understand why I would use one or the other.

👀 1
Lukas 2021-05-14T12:30:44.216Z

Hello everyone, I'm trying to convert a list of maps into eav-triplets. There is one catch: some values are in a vector, and they need to get unwrapped. This is my current solution:

(defn map->eav
  ([data]
   (let [ids (atom 0)]
     (map->eav data (fn [] (swap! ids inc)))))
  ([data id-gen]
   (->> data
        (mapcat
         (fn [e]
           (let [id (id-gen)]
             (for [[k v] e]
               [id k v]))))
        (reduce
         (fn [acc [e a v]]
           (if (vector? v)
             (into acc (for [n v] [e a n]))
             (conj acc [e a v])))
         []))))

(map->eav [{:list ["a" "b" "c"]}]) 
;; => [[9984 :list "a"] [9984 :list "b"] [9984 :list "c"]] 
This works, but I wonder if there isn't a more concise solution to the problem?

alexmiller 2021-05-14T12:34:33.217200Z

Well don’t call RT.nextID - that’s implementation stuff you shouldn’t use directly

Lukas 2021-05-14T12:38:19.218Z

😋 okay ty will do

Ivan Koz 2021-05-14T12:49:42.219500Z

@lucas.sousa

(def id-gen
  (let [a (AtomicLong. 1)]
    (fn [] (.getAndIncrement a))))
=> #'user/id-gen
(id-gen)
=> 1
(id-gen)
=> 2
or use UUID

👍 1
sova-soars-the-sora 2021-05-14T15:52:52.221100Z

Datalog is a query language similar to GraphQL and Specter is used to conveniently run up and down nested data structures using keywords specific to specter

Franco Gasperino 2021-05-14T17:00:47.224100Z

good morning. When using java interop, im having difficulty invoking the correct overloaded function based on parameter type.

(. java.nio.file.Paths (get "/tmp"))
=> ; Execution error (ClassCastException) at eventflow.ingress.file/eval11660 (form-init15515817518546427981.clj:62).
; class java.lang.String cannot be cast to class java.net.URI (java.lang.String and java.net.URI are in module java.base of loader 'bootstrap')

(class "/tmp")
=> java.lang.String

<https://docs.oracle.com/javase/8/docs/api/java/nio/file/Paths.html>

Franco Gasperino 2021-05-14T17:01:38.225Z

it sure reads as if it's invoking the static method accepting a URI, not a string

dpsutton 2021-05-14T17:08:39.225600Z

(java.nio.file.Paths/get "/tmp" (make-array String 0)) should work for you. There is no signature that is (String). it's (String, ...String)

Franco Gasperino 2021-05-14T17:09:31.225800Z

thanks, will give that a try

dpsutton 2021-05-14T17:09:46.226Z

https://clojure.org/reference/java_interop#_arrays check out the varargs section

2021-05-14T17:12:31.227Z

I guess my question then would be: when is it more useful to run a datalog query and when should I run a specter query?

2021-05-14T17:13:41.228900Z

I guess Specter might just be more precise? Like I know exactly what I’m looking for and where it is, since I need to manually specify all elements of the tree. But datalog helps me find things without looking at the tree?

2021-05-14T17:14:49.230Z

In other words, I get to skip a few steps I would have needed to take in Specter

alexmiller 2021-05-14T17:28:08.230300Z

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

Adrian Sci 2021-05-14T17:52:31.236500Z

Which clojure function could I use to run a function on each element in a collection? I'm working in core.logic and I'm trying to check a collection of things being in another collection. For example, checking if each individual element in the collection [:one :two] is in [:one :two :three] The current line looks something like this, however everyg won't work because in isn't a collection of lvars (I think), it's something like [:one :two] (everyg #(membero % state) in))

2021-05-14T17:54:36.238800Z

you should be able to just tell what in is

2021-05-14T17:54:56.239500Z

it can't be an lvar

2021-05-14T17:55:05.239800Z

even a ground lvar

2021-05-14T17:55:49.240300Z

what you need is clp(set) which doesn't exist for core.logic

2021-05-14T17:59:09.241900Z

you can maybe try something like:

(defn for-all [goal lst]
  (l/conde
   [(l/== lst ())]
   [(l/fresh [fst rst]
      (l/conso fst rst lst)
      (goal fst)
      (for-all goal rst))]))

(for-all #(membero % state) in)

2021-05-14T17:59:50.242300Z

What would be the idiomatic way to do the equivalent of this in clojure

private Either&lt;Error, Output&gt; Parse(string input) { ; either return an Output or an Error } 

Parse(someInput).Match(left: ProcessError,
                       right: ProcessOutput)
Return a map, something like
{:errors []
 :output []}
then check if errors is empty or nil? And if its not errors trumps output and deal with them, else deal with the output ?

2021-05-14T18:03:44.244400Z

ANy libraries that support stuff like

Foo().Then(Bar).Then(Quax).Match(left: ProcessErrors, right: ProcessFinalResult)
That will fail quick into ProcessErrors if any of Foo, Bar or Quax return the equivalent to a Left Either

2021-05-14T18:04:16.244900Z

Or is this just silly to do in clojure and there are better ways?

2021-05-14T18:04:48.245800Z

I thought maybe (cond->) could help me here?

2021-05-14T18:04:55.246300Z

I do it lots in data processing pipelines, but it is such a small thing to implement I tend to just write it as needed

alexmiller 2021-05-14T18:04:58.246400Z

there are people that do that (and there are some libraries in this vein)

alexmiller 2021-05-14T18:05:23.247200Z

Clojure leans on exceptions for error reporting and that's what I consider idiomatic

alexmiller 2021-05-14T18:06:31.247500Z

https://github.com/adambard/failjure etc

2021-05-14T18:08:24.248300Z

oh, failjure looks really neat, thanks

Adrian Sci 2021-05-14T18:09:10.248500Z

For more context, I was trying to extend a story generator from a core.logic tutorial I found to accept multiple inputs as well as multiple outputs per action, the entire function looks like this ;;function to find an action that can be used, consume the elements in the state, and produce new story state (defn actiono [state new-state action]     `(fresh [in out temp]`     `;;one branch for multi input, the other for a single input.`      `(conda`         `[(all`         `(== (coll? in) true)`         `(everyg #(membero % state) in))`          `(ploto in out)`         `(everyg #(rembero % state temp) in)]`          `[(all`         `(membero in state))`         `(ploto in out)`         `(rembero in state temp)]`     `)`      `(appendo out temp new-state) ;;add the elements to the new state`     `(== action [in out])))`

Adrian Sci 2021-05-14T18:12:45.249800Z

I'm quite new to this so it'll take me some time to go through you little code block hiredman so I can know what it does, but thank you.

2021-05-14T18:12:54.250Z

in is a logic var there, which you cannot pass as the collection to everyg

2021-05-14T18:15:15.251100Z

(== (coll? in) true) will always fail too

2021-05-14T18:15:38.251500Z

because in is not a collection it is lvar

2021-05-14T18:22:30.257300Z

the way core.logic is written as a dsl (and minikaren which it is based on) lets you freely mix clojure and core.logic code, but they are distinct languages, clojure is kind of the meta language for core.logic. So you can't really throw clojure code like coll? into core.logic and have it work, but you can use clojure code to generate core.logic

Adrian Sci 2021-05-14T18:22:30.257400Z

Alright I think that makes sense because in is created as an lvar. I got confused because the input that I'm trying to extend it for is collection

2021-05-14T18:23:12.258100Z

everyg is an example of this, it is actually kind of regular clojure code, it can't handle lvars, but it generates a core.logic goal

Adrian Sci 2021-05-14T18:29:53.259900Z

Yeah I will be careful about when I use clojure functions while writing core.logic. I'll try to rethink that block and see what I can come up with

pithyless 2021-05-14T19:35:50.260Z

@kiran.joshi just in case you're writing command line scripts and you're not aware of https://babashka.org/, I suggest you check it out. Otherwise, sorry for the spam and carry on. :)

👍 1
Todd 2021-05-14T20:20:34.261Z

Does the Clojure community value unit tests? ClojureScript?

dpsutton 2021-05-14T20:30:58.261300Z

i do

seancorfield 2021-05-14T20:32:03.261700Z

Based on our code base at work, I’d say we value unit tests:

Clojure build/config 20 files 233 total loc,
    45 deps.edn files, 774 loc.
Clojure source 355 files 89158 total loc,
    3551 fns, 925 of which are private,
    569 vars, 30 macros, 90 atoms,
    85 agents, 24 protocols, 67 records,
    857 specs, 33 function specs.
Clojure tests 378 files 23747 total loc,
    4 specs, 1 function specs.

👍 1
seancorfield 2021-05-14T20:34:00.263300Z

I tend to develop tests initially via REPL interactions, starting with a Rich Comment Form (RCF — (comment ..)) as I explore a problem space and evolve a solution and the code in the comment gets refactored out into source functions and tests for those functions.

seancorfield 2021-05-14T20:34:43.264100Z

(and, to be clear, I do not type into a REPL: I always edit code in a file and eval expressions into the REPL running in the background)

Todd 2021-05-15T11:53:03.283800Z

Iike that method…I do something similar where I start a test file and put the function right above the test and build the function as I write tests with the REPL going. very similar to me TDDish flow in other languages

Todd 2021-05-15T11:55:57.284Z

I figured the core team values them but I know lots of the devs that use Clojure in the wild seem to be more startupy, full stack apps. Closer the Ruby/Rails startup stuff where I’ve seen fewer tests and more write lots a code, deploy and fix issues found on the fly. Not being necessarily critical of this method as I’m sure in some environments getting slightly buggy code out the door that you fix quickly might be more valuable than spending more time writing tests and exercising a more extensive software engineering disciplined approach.

Todd 2021-05-15T11:58:39.284200Z

I have more interest in working on larger systems that have to work and perform well. Deploying a bug is bad. Deploying slow code is bad. I probably end up writing more test code than actual code. But also realize tests are code that need maintanence so they should be well written. But having some extra tests you don’t need is much easier to remedy (delete them) than missing tests that you DO need.

2021-05-14T20:46:44.264300Z

What tool did you use to generate this report?

seancorfield 2021-05-14T20:48:36.264500Z

It’s a shell script that does a bunch of find/`grep`/`wc` commands.

👍 1
alexmiller 2021-05-14T21:06:45.265100Z

I value useful tests, which come in many forms

👍 3
2021-05-14T21:57:27.265400Z

anecdotally, there certainly are clojure devs that downplay the importance or usefulness of unit tests. i think there's some merit to the idea that the repl gets you much of what a unit test would get you (at least at dev time). i think this gets complicated by the fact that as a dynamic language, clojure code bases stand to benefit /more/ from unit tests

2021-05-14T22:25:30.265600Z

How does the compiler keep atoms from being corrupted, knowing that at the beginning it could change the order of execution of things and now not exactly?

dpsutton 2021-05-14T22:26:51.265800Z

what do you mean?

2021-05-14T22:28:37.265900Z

Well atoms are mutable, how an atom does not get corrupted, since these are mutable, what process should the compiler follow

2021-05-14T22:28:51.266Z

I'm not sure if I'm clear, I may be having issues understand atoms

2021-05-14T22:33:30.266400Z

nothing

seancorfield 2021-05-14T22:33:32.266600Z

@jimmyssj3 Just checking, have you read this? https://clojure.org/reference/atoms

2021-05-15T15:24:43.291400Z

Thanks!

2021-05-14T22:33:39.266700Z

the compiler does nothing

2021-05-14T22:34:30.267300Z

I haven't, I'll give it a look. Thanks

seancorfield 2021-05-14T22:34:56.267500Z

Atoms are operated on via swap! (mostly) and reset! (occasionally), and those functions ensure that the semantics are retained by design.

2021-05-14T22:36:39.268200Z

https://www.youtube.com/watch?v=ScEPu1cs4l0 is good background

dpsutton 2021-05-14T22:42:09.269600Z

if you are familiar with java, the source of swap! might be revealing as well. it's an infinite loop, get the value before, apply the function, compare and set. it the compare and set was successful, done, otherwise loop. <https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Atom.java#L33>

Evan Haveman 2021-05-14T23:53:18.269900Z

are there any good guides on starting a simple clojurescript project that doesn’t need the browser (eg a library of functions). i know i can use node. but i cant seem figure out what my deps.edn should look like, what the command line is to open a repl and play with my code, and what the command line is to run my tests.

simongray 2021-05-16T08:32:49.298700Z

I saw this the other day https://github.com/raspasov/cljs-101

Evan Haveman 2021-05-16T20:19:29.309800Z

yeah - was shared in another thread i started in #clojurescript - i found clj -M -m cljs.main --repl --repl-env node also works.