beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Darin Douglass 2020-10-07T00:13:02.395900Z

You can always create a single test resource with input and expected output per task, then run a test for each task. Something like (typos are expected, I did this on my phone):

deftest test-tasks
  (doseq [[task {:keys [input expected]}] my-test-resource]
    (testing task
      (is (= expected (run-task task input))))))

seancorfield 2020-10-07T00:54:31.397Z

@artem00298 ^ that would be my recommendation too: put all the data together and have a single test that runs each set of data through the (is (= ...)) test.

Matias Francisco Hernandez Arellano 2020-10-07T04:30:09.399900Z

Not sure why but this piece of code is failing by not printing anything to the console

(defn parse-item [data]
  (let [
        title (get-in data [:attributes :title])
        ]
    (println (format "Titulo: %s" title))
    )
  
  )

(defn success [response]
  "Performs the items parsing if the request was successfully"
  (let [items (get-items response)]
    (map parse-item items))
  )
where items used in (map parse-item items) is an structure like this (but bigger)
`
[{:id 'Some id', :attributes {:title 'Some title', :tags [] } } {:id 'Some id 2', :attributes {:title 'Some title', :tags [] } } ]```

seancorfield 2020-10-07T04:31:31.400400Z

Because map is lazy and you're not realizing the sequence I suspect @matiasfh

seancorfield 2020-10-07T04:32:16.400900Z

Rule of thumb: do not use map for anything with side-effects (like println).

seancorfield 2020-10-07T04:33:39.402800Z

(run! parse-item items) would be eager and run parse-items for each item, so it would print lines. But that won't help you when you change parse-item to actually produce transformed data.

Matias Francisco Hernandez Arellano 2020-10-07T04:33:41.403Z

make sense.. I'm printing there to a way of "debugging" not try to see what is happening.. what could be a clojure way of doing this (very bad) debugging?

seancorfield 2020-10-07T04:34:13.403500Z

Separate the transform from the print.

Matias Francisco Hernandez Arellano 2020-10-07T04:34:17.403700Z

the idea is to just return the (format) form

seancorfield 2020-10-07T04:34:51.404600Z

if success really is just map over items, then (run! println (success ...)) would be the way to print results.

dpsutton 2020-10-07T04:34:57.404800Z

(def items my-items) (parse-item (first items)) should be enough

dpsutton 2020-10-07T04:35:16.405200Z

just call your function on one of the items. when it works, it should work on all of the items

seancorfield 2020-10-07T04:36:07.405900Z

Yeah, if you're working tightly in the REPL, you would test parse-item by calling it on a single item.

seancorfield 2020-10-07T04:37:23.407500Z

(defn success [response]
  (map #(format "Titulo: %s" %) (get-items response)))
would probably be simpler/more idiomatic.

seancorfield 2020-10-07T04:37:43.408Z

If you want a sequence of strings?

Matias Francisco Hernandez Arellano 2020-10-07T04:39:02.409300Z

make sense.. now since you are online xD.. I just tried another thing (connected with this) . This code is for a discord bot, so the bot connect to discord and receive a command. in this case the command triggers a http request using clj-http and cheshire. Then parse the response with that function that we were talking. but this error is fired up

SEVERE: Exception in dispatch-http
java.lang.Exception: Don't know how to write JSON of class org.apache.http.impl.nio.client.FutureWrapper
        at clojure.data.json$write_generic.invokeStatic(json.clj:389)
        at clojure.data.json$write_generic.invoke(json.clj:386)
        at clojure.data.json$eval9381$fn__9382$G__9372__9389.invoke(json.clj:290)
        at clojure.data.json$write_object.invokeStatic(json.clj:339)
        at clojure.data.json$write_object.invoke(json.clj:323)
        at clojure.data.json$eval9381$fn__9382$G__9372__9389.invoke(json.clj:290)
        at clojure.data.json$write.invokeStatic(json.clj:479)
        at clojure.data.json$write.doInvoke(json.clj:428)
        at clojure.lang.RestFn.invoke(RestFn.java:425)
        at clojure.lang.AFn.applyToHelper(AFn.java:156)
        at clojure.lang.RestFn.applyTo(RestFn.java:132)
        at clojure.core$apply.invokeStatic(core.clj:669)
        at clojure.core$apply.invoke(core.clj:660)
        at clojure.data.json$write_str.invokeStatic(json.clj:486)
        at clojure.data.json$write_str.doInvoke(json.clj:481)
        at clojure.lang.RestFn.invoke(RestFn.java:410)
        at discljord.messaging.impl$eval11931$fn__11933.invoke(impl.clj:132)
        at clojure.lang.MultiFn.invoke(MultiFn.java:239)
        at discljord.messaging.impl$make_request_BANG_$make_request__13636.invoke(impl.clj:788)
        at discljord.messaging.impl$make_request_BANG_.invokeStatic(impl.clj:793)
        at discljord.messaging.impl$make_request_BANG_.invoke(impl.clj:767)
        at discljord.messaging.impl$step_agent$fn__13660.invoke(impl.clj:838)
        at clojure.core$binding_conveyor_fn$fn__5754.invoke(core.clj:2033)
        at clojure.lang.AFn.applyToHelper(AFn.java:154)
        at clojure.lang.RestFn.applyTo(RestFn.java:132)
        at clojure.lang.Agent$Action.doRun(Agent.java:114)
        at clojure.lang.Agent$Action.run(Agent.java:163)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
        at java.base/java.lang.Thread.run(Thread.java:832)
based on my basic knowledge of Java this is clearly a problem with the async function

Matias Francisco Hernandez Arellano 2020-10-07T04:39:17.409500Z

`

Matias Francisco Hernandez Arellano 2020-10-07T04:39:33.409900Z

(defn get-on-brd-message [query]
  (let [url (get-url query)]
    (client/get url {:accept :json :async? true }
      (fn [response] (success response))
                  ;; raise callback
      (fn [exception] (println (.getMessage exception)))
    )))

dpsutton 2020-10-07T04:40:44.410400Z

this is an error in writing json, not reading json

dpsutton 2020-10-07T04:41:02.410800Z

but you're trying to serialize some asynchronous value which can't work

Matias Francisco Hernandez Arellano 2020-10-07T04:41:53.411200Z

Just by asking I figured that should try with a sync version of the request

1
Matias Francisco Hernandez Arellano 2020-10-07T04:41:57.411600Z

😄

Matias Francisco Hernandez Arellano 2020-10-07T04:43:19.411800Z

Thanks folks!

Ricardo Cabral 2020-10-07T06:45:06.413900Z

Hello all, I am now in the Clojure world, and I am really enjoying learning it. I have a very dumb question but I am not able to find it by my self. Why in the sample code I cannot have two (assoc-in…) ? I am trying to print both values but nothing happens. Thank you very much in advance.

;; interceptor for authorization
(def auth
  {:name :auth
   :enter
         (fn [context]
           (let [token (-> context :request :headers (get "token"))]
             (if-let [uid (and (not (nil? token)) (get-uid token))]
               (assoc-in context [:request :tx-data :user] uid)
               (assoc-in context [:request :my-key :my-value] "Hello Again")
               (chain/terminate
                 (assoc context
                   :response {:status 401 :body "Auth token not found"})))))
   :error
         (fn [context ex-info]
           (assoc context
             :response {:status 500 :body (.getMessage ex-info)}))
   })

seancorfield 2020-10-07T06:49:32.416100Z

@ricardozcabral Welcome! Clojure is an expression-based language with immutable data. In the code above you seem to have three expressions inside your if which I don't think should even compile. Each expression needs to have a single return value.

Nemo 2020-10-07T06:49:40.416400Z

(def auth
  {:name :auth
   :enter
         (fn [context]
           (let [token (-> context :request :headers (get "token"))]
             (if-let [uid (and (not (nil? token)) (get-uid token))]
               (->
                 (assoc-in context [:request :tx-data :user] uid)
                 (assoc-in [:request :my-key :my-value] "Hello Again"))
               (chain/terminate
                 (assoc context
                   :response {:status 401 :body "Auth token not found"})))))
   :error
         (fn [context ex-info]
           (assoc context
             :response {:status 500 :body (.getMessage ex-info)}))
   })
This might help..

1
🙌 2
seancorfield 2020-10-07T06:49:54.416600Z

Can you explain what you are trying to do in that if?

seancorfield 2020-10-07T06:50:21.417400Z

@prayaganeethu Could you indent that code to show the grouping better?

👍 1
Ricardo Cabral 2020-10-07T06:51:40.419500Z

I was just trying to understand how assoc-in works, if I could include more than one value on it. I have also tried to put that before the if and it did not work. But I believe you are correct @seancorfield I missed the if part. Thank you very much. and thank @prayaganeethu to show me how to fix it

👍 1
seancorfield 2020-10-07T06:51:43.419600Z

Perhaps:

(def auth
  {:name :auth
   :enter
         (fn [context]
           (let [token (-> context :request :headers (get "token"))]
             (if-let [uid (and (not (nil? token)) (get-uid token))]
               (-> context
                 (assoc-in [:request :tx-data :user] uid)
                 (assoc-in [:request :my-key :my-value] "Hello Again"))
               (chain/terminate
                 (assoc context
                   :response {:status 401 :body "Auth token not found"})))))
   :error
         (fn [context ex-info]
           (assoc context
             :response {:status 500 :body (.getMessage ex-info)}))
   })

seancorfield 2020-10-07T06:52:03.420Z

(thanks for the edit to show the grouping)

👍 1
seancorfield 2020-10-07T06:52:54.420800Z

It definitely takes a bit of getting used to if your background is in statement-based languages that rely on mutation 😐

Ricardo Cabral 2020-10-07T06:53:22.421200Z

My background is 99% Java, so I am struggling a bit with it 😄

seancorfield 2020-10-07T06:53:44.421800Z

Hey, at least you're used to the JVM and stacktraces! 🙂

🙂 1
Ricardo Cabral 2020-10-07T06:56:52.423100Z

This is one of the reasons I am trying to learn Clojure, it helps a lot to be used to the JVM. Just so you know the sample above still doesn’t work I am getting clojure.lang.ArityException in Interceptor :auth - Wrong number of args (4) passed to: clojure.core/assoc-in%

Nemo 2020-10-07T07:09:59.423200Z

(->
  context
  (assoc-in [:request :tx-data :user] uid)
  (assoc-in [:request :my-key :my-value] "Hello Again"))
Please do recheck the arguments in both the assoc-in - might have left context in there still as an arg, hence

❤️ 1
Ricardo Cabral 2020-10-07T07:20:37.423800Z

You are a life saver. Not it woks Thank you very much again 🙂

😊 1
Jim Newton 2020-10-07T12:05:24.429Z

I have a certain reduction function which I need to write. I don't know what to call it or whether something similar exists. Does this sound like any kind of standard algorithm or standard iteration function? The function takes an object representing an expression, such as a mathematical expression, or boolean expression etc. And the function takes a set of reduction operations. Each reduction operation is a function which is given the opportunity to simplify the expression, thus each option takes the expression as argument and either returns the same expression or a simplified form. The algorithm keeps calling reduction functions in-turn until one successfully reduces it, i.e. until one fails to return the same object it received.

Jim Newton 2020-10-07T12:11:47.429300Z

it's sort of a cousin of fixed-point

jsn 2020-10-07T12:24:10.430600Z

so basically (->> your-vector-of-operations (cons identity) (map #(% your-expression)) distinct second) ?

Jim Newton 2020-10-07T12:24:43.430800Z

sorry, I don't understand that logic. Does that expression halt the computation as soon as one operation returns something different than it is given?

Jim Newton 2020-10-07T12:26:45.431800Z

Here's what I have, untested:

(defn find-simplifier [obj simplifiers] ;; BAD name, need better name
  (if (empty? simplifiers)
    obj
    (loop [[f & fs] simplifiers]
      (let [new-obj (f obj)]
        (cond
          (not= new-obj obj)
          new-obj

          (empty? fs)
          obj

          :else
          (recur fs))))))

jsn 2020-10-07T12:28:13.432600Z

my map is subject to chunking, though; but perhaps it's fixable

Matias Francisco Hernandez Arellano 2020-10-07T12:28:55.433800Z

Is there any video/tutorial about how to use doom emacs with clojure, kind of a workflow how to?. I'm completely ignorant about how to work with the code and the repl in a "dynamic" way

Jim Newton 2020-10-07T12:29:31.434500Z

what is chunking?

rdgd 2020-10-07T12:30:17.435300Z

This is not related to refactoring your code, however since I'm assuming you want to test for an empty rest of your simplifiers collection where you call (empty fs), what you are looking for in that case, just an FYI, is the predicate empty? which would read (empty? fs) instead

1
Jim Newton 2020-10-07T12:30:58.435400Z

oh you mean it might calculate too many elements only to throw away unused ones?

jsn 2020-10-07T12:32:06.435800Z

yes.

Jim Newton 2020-10-07T12:32:19.436100Z

Thanks, yes you're right. As I said I haven't tested it yet.

rdgd 2020-10-07T12:32:30.436500Z

actually, a bit of clarification there (seq fs) would be more idiomatic

Jim Newton 2020-10-07T12:33:41.437Z

Yes, I think that discussion has been had many times. I organised my code to avoid calling (not (empty? ...)) to avoid the dispute yet again.

alexmiller 2020-10-07T12:34:36.438400Z

I think you need a termination check on f first (and then you can remove the fs check), but seems like the right path to me

Jim Newton 2020-10-07T12:34:42.438600Z

Yes, that's something I want to avoid. as any of the function might have high complexity. I'd like to avoid calling a tree reduction if a previous simplification gave me a good result.

jsn 2020-10-07T12:38:39.439100Z

(some #(let [expr' (% expr)] (and (not= expr' expr) expr')) your-funcs) should do, if neither expr nor expr' can legally be falsey

vlaaad 2020-10-07T12:38:57.439300Z

argh, I was about to post it 😄

vlaaad 2020-10-07T12:39:05.439500Z

(defn simplify [x fs] 
  (or (some #(let [x' (% x)] (when-not (= x' x) x')) fs) 
      x))

Jim Newton 2020-10-07T12:47:20.440200Z

@vlaaad, does that work if one of the functions reduces the expression to nil or false ?

Jim Newton 2020-10-07T12:49:14.440400Z

it is very likely, and desired sometimes that an expression simply to nil

Jim Newton 2020-10-07T12:50:17.440800Z

I fixed the code. BTW.

Jim Newton 2020-10-07T12:51:20.441Z

@alexmiller don't I have to assure that the sequence is non-empty before restructuring it into [f & fs] ?

vlaaad 2020-10-07T12:55:30.441200Z

it does not 🙂

practicalli-john 2020-10-07T13:01:40.441400Z

I dont have exactly what you ask, but this book may help https://practicalli.github.io/spacemacs/clojure-projects/ There may be differences in keybindings (I found doom a bit lacking in that area) The workflow I take is essentially this: • create a project on the command line (or eshell) • Open the project in emacs • cider-jack-in-clj to start a repl for the project • edit and evaluate code in the src buffers (I rarely use the REPL buffer directly) • cider-undef and cider-ns-refresh or just stop start the repl if I have stale vars (usually renaming functions or deftest functions without undef'ing the old name • Put experimental code in rich code blocks http://practicalli.github.io/clojure/repl-driven-devlopment.html#rich-comment-blocks---living-documentation • write test to start solidifying the design and run all tests with the cider runner https://practicalli.github.io/spacemacs/testing/unit-testing/running-tests.html • run a command line test runner, eg. kaocha, before committing to run the tests from scratch

👀 1
practicalli-john 2020-10-07T13:02:26.441600Z

Also take a look at http://www.parens-of-the-dead.com/ Its not a tutorial, but you can see repl driven development in action

practicalli-john 2020-10-07T13:03:05.441800Z

There is a general #emacs channel here. Doom has a discord community, you may find some help there also.

💪 1
vlaaad 2020-10-07T13:05:42.442100Z

you can augment the code to return (reduced x') instead of x'

vlaaad 2020-10-07T13:06:44.442300Z

and then check if result of (some ...) is reduced and then deref it if it is reduced and return x if it is nil

Matias Francisco Hernandez Arellano 2020-10-07T13:10:55.443100Z

I did a thing: https://github.com/matiasfha/JenkinsBot A discord bot written in Clojure (first app) If you can take a look it will be very helpful

jsn 2020-10-07T13:12:24.443300Z

at this point just using reduce instead would be simpler (i think)

alexmiller 2020-10-07T13:17:17.443600Z

no

alexmiller 2020-10-07T13:17:32.443800Z

you'll just nil for both if it's nil

vlaaad 2020-10-07T13:36:58.444500Z

more complex, actually, because you will be dealing with 2 moving parts instead of 1 (item and accumulator instead of item)

jsn 2020-10-07T13:37:58.445800Z

well, the first part is not actually moving all that much 🙂

vlaaad 2020-10-07T13:38:03.446Z

but probably easier since it unpacks reduced for you

jsn 2020-10-07T13:38:27.446400Z

yeah, that was the idea

tugh 2020-10-07T13:39:56.447800Z

I’m curious about why there is no def- ? Is using metadata dictionary the only way to declare private vars? :thinking_face:

Jim Newton 2020-10-07T13:42:16.448100Z

but you won't be able to distinguish that from a real sequence [nil] , ok in this case that can't happen as I have a sequence of unary functions, which I suppose nil is not.

vlaaad 2020-10-07T13:43:29.448400Z

yes

vlaaad 2020-10-07T13:43:46.448800Z

in short, defn- was a mistake, metadata on a name is preferred

Jim Newton 2020-10-07T13:45:10.448900Z

ouch, that's another difference apparently between function application and restructuring ((fn [a & as] 12)) throws an error but (let [[a & as] nil] ...) binds a to nil.

😡 1
Jim Newton 2020-10-07T13:46:05.449200Z

Because this is true about functions, I supposed it would be true about functions. 😞

tugh 2020-10-07T13:49:24.452Z

but why defn- was a mistake? it is pretty useful IMHO

2020-10-07T13:51:09.454Z

If defn- leads to requests for def- , defmacro- , ..., then it is a mistake. Adding metadata :private to any/all of them works, including on defn, and leads to fewer overall names needing defining

👍 1
Day Bobby 2020-10-07T13:52:29.454100Z

I have a threading macro: (-> a b c d) I want to make c conditional, so only if something-happened is truthy the pipeline will go through c. What is the best way to achieve this?

Day Bobby 2020-10-07T13:59:25.454900Z

(-> a b (cond-> something-happened c) d) oh this works

👍 2
practicalli-john 2020-10-07T14:18:53.455500Z

Consider why you wish to make things private. As Clojure uses immutable data, there is less reason to hide data. So the private concept is not as relevant in Clojure. It is mostly used as documentation rather than a constraint on accessing certain parts of code (which can still be accessed when private) Metadata forms part of the function documentation, so is more useful that creating a macro (e.g. defn- macro) to wrap each of the many ways to create a var. Hope that helps.

practicalli-john 2020-10-07T14:24:06.455700Z

I tend to just define helper functions that contain code that would otherwise be shared by several functions. This is the main reason I would consider using private. If there are several helper functions, these can then be moved into a separate namespace. I test shared functions through the functions that use them, minimising the number of tests whilst still getting effective test coverage.

NoahTheDuke 2020-10-07T14:24:42.456400Z

if i have a string ":name", what's the best way to turn it into the keyword :name?

alexmiller 2020-10-07T14:27:25.456800Z

clojure.edn/read-string

👍 3
alexmiller 2020-10-07T14:27:54.457200Z

or clojure.core/read-string (but the former is better from a security perspective)

kennytilton 2020-10-07T14:42:33.457400Z

Since clojure is lazy, you can map over all the simplifiers then pick out the first result not= using some (so you stop at the first):

(let [x '(+ 2 2)
      simplifiers [identity reverse identity]]
  (some #(when (not= % x) %)
    (map (fn [s]
           (s x)) simplifiers)))
…or roll it all up into one:
(let [x '(+ 2 2)
      simplifiers [identity reverse identity]]
  (some #(let [y (% x)]
           (when (not= y x) y)) simplifiers))
Me, I would have simplifiers return nil if they choose not to transform, but it’s your app! 🙂

NoahTheDuke 2020-10-07T14:45:05.457800Z

ah of course, thank you

2020-10-07T14:53:11.458600Z

Say I have a script xx.clj which contains a main function. How can I run that main function with clj ?

2020-10-07T14:53:29.459100Z

clj -M xx.clj runs the whole script. I need run the main function.

alexmiller 2020-10-07T14:53:46.459300Z

clj -M -m xx

alexmiller 2020-10-07T14:54:10.459900Z

(but the script will need to be on the classpath)

alexmiller 2020-10-07T14:55:11.460600Z

although that will also load the clj file (which is the same as "running the script")

alexmiller 2020-10-07T14:55:24.460900Z

are you trying to avoid doing something else in the file?

2020-10-07T15:00:07.461200Z

yes. only run the main function.

2020-10-07T15:04:01.462200Z

I am thinking is there something similar to python’s: if name_ == ‘ main _’ : main()

dpsutton 2020-10-07T15:04:29.462800Z

do you have top level forms that "do" work? ie (println "HI") and you are trying not to run those?

2020-10-07T15:06:23.463700Z

Take the case of clj -M xx.clj. I don’t want to write a (main) in the top level, which will run when the namespace is first loaded.

dpsutton 2020-10-07T15:07:52.465400Z

i'm not sure i follow. presumably a (defn -main [...] ...) function won't be run when loaded, but the definition will be created

alexmiller 2020-10-07T15:08:08.465700Z

main is not run unless you invoke it (or use clj -M -m foo)

2020-10-07T15:09:45.465900Z

Yes. That’s is why I have to explicitly write a (main) in the toplevel. Mind the parens.

2020-10-07T15:11:42.466100Z

can I add the foo.clj to the classpath with the command line ?

alexmiller 2020-10-07T15:13:34.466300Z

you can clj -Sdeps '{:paths ["path/to/foos/dir"]}' ...

alexmiller 2020-10-07T15:13:53.466500Z

that's the directory containing foo.clj, not the path to foo.clj

vncz 2020-10-07T15:36:12.466700Z

Oh about this, why does cli require -M when running main? Can't it "figure it out" automatically when doing -m?

vncz 2020-10-07T15:36:21.466900Z

(It's just a curiosity, not really a request or something else)

alexmiller 2020-10-07T15:37:28.467100Z

-M (now) means "run clojure.main" and we are moving towards a point where that will be the only way to run clojure.main (and -m may at some future point have some other meaning)

vncz 2020-10-07T15:38:13.467300Z

So can I just do cli -M foo ?

alexmiller 2020-10-07T15:39:03.467600Z

that will run foo as a script

vncz 2020-10-07T15:39:29.467800Z

Got it, so -M is to tell the CLI to run the clojure.main, while -m is to tell it to run the main function in the namespace I'm indicating

alexmiller 2020-10-07T15:39:45.468Z

yes

vncz 2020-10-07T15:39:54.468200Z

Ok so question — what happens when I just do cli -m namespace ? Why does it work anyway? Does it do something differently?

alexmiller 2020-10-07T15:40:55.468400Z

it does the same thing (for now), but will warn you to use -M - we just deprecated that behavior so this is a transitionary period

alexmiller 2020-10-07T15:41:05.468600Z

eventually clj -m namespace will not work

alexmiller 2020-10-07T15:41:30.468800Z

but it may be months before we make that next step

vncz 2020-10-07T15:41:34.469Z

Ok fantastic, that explains everything. Thanks @alexmiller

alexmiller 2020-10-07T15:41:43.469200Z

np

NoahTheDuke 2020-10-07T16:50:24.472500Z

i'm experimenting with using edn instead of json for sending data in a map from my server to the client. some of the values in my map are functions, and the edn reader in the client is throwing this error {:message "Invalid symbol: game.cards.basic/fn--16461/fn--16462.", :data {:type :reader-exception, :ex-kind :reader-error}}. This was handled when I used json by just turning that into a string: "game.cards.basic/fn--16461/fn--16462" which is harmless

NoahTheDuke 2020-10-07T16:50:36.472800Z

i'm using the sente library, if that helps at all

NoahTheDuke 2020-10-07T16:52:58.474Z

i tried putting a {:default (fn [tag value] (str value))} option in the edn/read-string call, but that's not helped

alexmiller 2020-10-07T16:54:01.474500Z

there is no edn syntax for function instances

alexmiller 2020-10-07T16:54:36.475200Z

so you either need to pass a form and eval on the far side (danger will robinson) or use a symbol that can be resolved to code that's available on the far side

alexmiller 2020-10-07T16:55:39.475900Z

or there are some libs that handle function serialization

NoahTheDuke 2020-10-07T16:56:10.476600Z

no way to just ignore it? i don't need the function on the other side, but writing a parser to selectively remove the function values feels like a lot of work

NoahTheDuke 2020-10-07T16:56:30.476800Z

either way, thanks so much

alexmiller 2020-10-07T16:58:54.477300Z

well it's just data, you can dissoc whatever you want out of the map :)

alexmiller 2020-10-07T16:59:32.478500Z

I mean you can clean the whole tree with a clojure.walk/postwalk using a function like #(if (ifn? %) nil %)

NoahTheDuke 2020-10-07T17:00:21.479200Z

holy crap, okay, that's pretty easy. i'll give that a whirl, thank you

dpsutton 2020-10-07T17:02:05.479400Z

beware: (ifn? :bob) is true

👍 4
alexmiller 2020-10-07T17:22:22.480100Z

right, fn? might actually be better here but use whatever discriminator is appropriate

Tāhā 2020-10-07T21:09:48.484100Z

Clojure noob here 👋 What should the contents of my project.clj look like if I want a project structure that looks like this

my-project-dir/
   project.clj
   main.clj     (I only want to work with this one Clojure file)
   ...          (Other non-Clojure files)   

Tāhā 2020-10-07T21:10:34.484500Z

(I'm using Leiningen)

dpsutton 2020-10-07T21:14:28.486400Z

by default lein uses a classpath root of src. so if you require a namespace called main, it will look for src/main.clj. that being said, its usually preferable to have some kind of disambiguating namespace. often this is your org, github username, or perhaps even some domain notion if its truly one-off code. perhaps a namespace like task.main and have it at src/task/main.clj

Tāhā 2020-10-07T21:15:49.486900Z

So how do I change the default root?

Tāhā 2020-10-07T21:16:05.487400Z

I couldn't seem to find a project.clj reference online

dpsutton 2020-10-07T21:16:51.488Z

https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L309 by setting :source-paths

phronmophobic 2020-10-07T21:17:32.488900Z

for this setup, I probably wouldn't recommend leiningen which is focused on projects. you might find https://clojure.org/guides/deps_and_cli easier

phronmophobic 2020-10-07T21:19:16.489700Z

otherwise, I would really recommend adopting the standard project layout

phronmophobic 2020-10-07T21:19:51.489900Z

if you're just writing scripts, try https://github.com/borkdude/babashka

👍 1
Tāhā 2020-10-07T21:37:01.492500Z

Shouldn't (:import java.time.format DateTimeFormatter) successfully pull DateTimeFormatter into the current namespace? I've seen people do (:import [java.util ArrayList HashMap]) so what am I doing wrong here? I get a ClassNotFoundException

Tāhā 2020-10-07T21:37:52.492800Z

This is inside the ns macro

borkdude 2020-10-07T21:42:50.493300Z

@tahaahmedrasheedpk

(ns foo (:import [java.time.format DateTimeFormatter]))

Tāhā 2020-10-07T21:43:45.493900Z

Oh :woman-facepalming:

Tāhā 2020-10-07T21:51:21.494700Z

Syntax error (ClassNotFoundException) compiling at (main.clj:12:50).
DateTimeFormatter.RFC_1123_DATE_TIME
This is my main.clj

borkdude 2020-10-07T21:53:50.495200Z

@tahaahmedrasheedpk Try:

(.format DateTimeFormatter/RFC_1123_DATE_TIME limit)

Tāhā 2020-10-07T21:53:59.495400Z

ah

Tāhā 2020-10-07T23:30:40.499100Z

From the http://Clojure.org spec guide:

(ns my.domain (:require [clojure.spec.alpha :as s]))
(def email-regex #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$")
(s/def ::email-type (s/and string? #(re-matches email-regex %)))

(s/def ::acctid int?)
(s/def ::first-name string?)
(s/def ::last-name string?)
(s/def ::email ::email-type)

(s/def ::person (s/keys :req [::first-name ::last-name ::email]
                        :opt [::phone]))
Do I have to define map keys as specs to use them in s/keys ? Isn't there something like (s/keys :req [:first-name string? :last-name string? :email ::email-type]) ?

alexmiller 2020-10-07T23:46:58.499400Z

you have to define them as specs

Tāhā 2020-10-07T23:48:39.499700Z

Why is that :thinking_face:

alexmiller 2020-10-07T23:53:17.000600Z

one of the main philosophical ideas in spec is that attributes, not maps (concretions of attributes) should be the primary unit of specification

alexmiller 2020-10-07T23:54:33.001300Z

https://clojure.org/about/spec is probably the best written page about this and other ideas, but it's discussed in several of Rich's talks as well