beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
sova-soars-the-sora 2021-04-19T00:55:23.215100Z

Amazing. Thank you!

2021-04-19T02:36:23.215300Z

Ctrl-C does not interrupt the loop for you when you run lein repl? What OS are you using, and what is the output of lein version ? Ctrl-C interrupts evaluation for me with macOS 10.14.x and lein version 2.9.5

Sibelius Seraphini 2021-04-19T15:37:50.223600Z

Sibelius Seraphini 2021-04-19T15:38:02.224Z

how can I add a (print) in this code to inspect balances value?

walterl 2021-04-19T15:59:09.224200Z

Short answer: (if-let [balances (doto (b/list-balances (:account context)) println)]

walterl 2021-04-19T16:01:34.224400Z

Generally I use tap> rather than println, with Reveal's tap hooked up

walterl 2021-04-19T16:01:45.224600Z

https://github.com/vlaaad/reveal

grazfather 2021-04-19T16:30:55.225300Z

is there a quick and easy way to get a relative path and generate a full path?

grazfather 2021-04-19T16:32:33.225600Z

(absolute path)

Sibelius Seraphini 2021-04-19T16:34:13.225700Z

reveal looks awesome tks

Sibelius Seraphini 2021-04-19T16:34:29.225900Z

where can I put this `

:reveal {:extra-deps {vlaaad/reveal {:mvn/version "1.3.206"}}
         :ns-default vlaaad.reveal
         :exec-fn repl}
in my project.clj?

Sibelius Seraphini 2021-04-19T16:34:44.226100Z

grazfather 2021-04-19T16:34:59.226600Z

(.getAbsolutePath (io/file filename)) works

alexmiller 2021-04-19T16:55:58.227Z

also consider https://docs.oracle.com/javase/8/docs/api/java/io/File.html#getCanonicalPath-- which will resolve . and .. etc

Sibelius Seraphini 2021-04-19T16:57:06.227500Z

Sibelius Seraphini 2021-04-19T16:57:12.227700Z

I’m trying to output this log/debug on my repl

[clojure.tools.logging :as log]
any ideas of how to fix it?

Sibelius Seraphini 2021-04-19T16:57:46.227800Z

Sibelius Seraphini 2021-04-19T16:57:49.228Z

I’ve tried this config

Franco Gasperino 2021-04-19T16:58:09.228200Z

look above, it looks like you're working inside pedestal?

Sibelius Seraphini 2021-04-19T16:59:22.228400Z

it is pedestal the code is here https://github.com/runopsio/sequence

Franco Gasperino 2021-04-19T17:02:16.228700Z

(ns your.project.ns
  (:require [io.pedestal.log :as log]))

(log/info "This is a test" {:key-1 "my-value})

Franco Gasperino 2021-04-19T17:02:30.228900Z

that will send to log4j configured w/ pedestal

Sibelius Seraphini 2021-04-19T17:06:49.229100Z

this is a good trick

Sibelius Seraphini 2021-04-19T17:06:50.229300Z

tks

Franco Gasperino 2021-04-19T17:07:39.229800Z

also, the tap> function is pretty useful if you want to send a message to a backend queue, and provide different models to handle it

Pablo 2021-04-19T17:07:54.230100Z

Hello everybody I am using some vars like:

(def status.scheduled 1)
(def status.canceled 2)
But when I’m trying to use them into a function I’m getting a syntax error (`Syntax error (ClassNotFoundException) compiling at (src/clj/awp/services/appointment.clj:3:8).` status.scheduled).
(def scheduled-or-scheduled
  [{:keys [status] :as appointment}]
  (some #{status} [status.scheduled status.canceled]))
I found that I need to use the fullnamespaced name awp.services.appointment/status.scheduled in order to get it work but, is there a nicely way to call this vars?

walterl 2021-04-19T17:11:14.231600Z

I have {:reveal {:dependencies [[vlaaad/reveal "1.3.206"]]}} in my ~/.lein/profiles.clj, and use with lein with-profile +reveal repl and then in the REPL (do (require '[vlaaad.reveal :as reveal]) (add-tap (reveal/ui)))

2021-04-19T17:13:50.233900Z

I thought maybe you were just using a bad variable name, but now I’m not sure I understand what you’re doing. Is status.scheduled something defined by a Java lib you are using?

2021-04-19T17:15:50.234700Z

If not, you might be better off using a namespaced keyword like :status/scheduled instead of using a symbol with a dot in the name.

Pablo 2021-04-19T17:16:31.235100Z

No, they are constants from my DB. I want to group them by some way becase I also have

(def type.interview 1)
(def type.ordinary-session 2)
(def type.extraordinary-session 3)

2021-04-19T17:17:38.236500Z

Ok, you may be running into problems because the '.' in the middle of a symbol has special meaning to the Clojure compiler.

2021-04-19T17:18:59.237100Z

We normally used namespaced keywords for that sort of thing, so it’s not usually a problem.

Franco Gasperino 2021-04-19T17:20:43.237700Z

The (tap>) can be consumed by adding (add-tap f), where f is a function which will be called with the arguments sent to the (tap>)

Franco Gasperino 2021-04-19T17:21:28.238100Z

thus, the function passed to add-tap can be a pedestal log, another with a console print, or into some future endpoint

Franco Gasperino 2021-04-19T17:21:52.238300Z

decoupling the produce/consume

Pablo 2021-04-19T17:23:15.239100Z

Ok, I see the problem. Thanks a lot!

👍 1
Sibelius Seraphini 2021-04-19T17:25:27.239300Z

I’m gonna read more about tap

Sibelius Seraphini 2021-04-19T17:25:32.239500Z

I’m pretty new to clojure in general

Franco Gasperino 2021-04-19T17:27:48.239700Z

me as well

Franco Gasperino 2021-04-19T17:28:49.239900Z

Once you're aware of tap, you'll see it used often in really useful areas

Franco Gasperino 2021-04-19T17:28:53.240100Z

for example:

Franco Gasperino 2021-04-19T17:29:07.240300Z

https://vlaaad.github.io/reveal/#give-it-a-try

Franco Gasperino 2021-04-19T17:32:04.240500Z

https://clojureverse.org/t/introducing-shadow-cljs-inspect/5012

grazfather 2021-04-19T17:49:04.240900Z

thank you. That’s exactly what I need

2021-04-19T18:07:34.241100Z

(.getAbsolutePath (io/as-file “your path”))

2021-04-19T18:08:07.241300Z

http://clojure.java.io/as-file

grazfather 2021-04-19T18:20:19.241500Z

Why as-file over file?

Sibelius Seraphini 2021-04-19T18:25:54.241700Z

I’m stuck in how to run reveal from this lein project

Franco Gasperino 2021-04-19T18:32:37.241900Z

what is your project.clj?

Franco Gasperino 2021-04-19T18:35:51.242100Z

and also what editor are you using to invoke lein?

Franco Gasperino 2021-04-19T18:38:22.242300Z

Here is how i'm using tap> with pedestal logging, while keeping the door open to additional tap consumers going forward:

Franco Gasperino 2021-04-19T18:41:23.242500Z

(ns my.logging
  (:require [io.pedestal.log :as log]))

(defn tap->logback [content]
  (let [{:keys [level message context]} content]
    (cond
      (= level :info) (log/info message context)
      (= level :debug) (log/debug message context))))

;; Configure this handler to consume from the tap.
(add-tap tap->logback)

(ns my.handler)

(def my-handler {
  :name :my-handler
  :enter
    (fn [context]
      (let [request (:request context)]
        (tap> {:level :debug :message "" :context request})))})

Franco Gasperino 2021-04-19T18:41:35.242700Z

thats a simple example

Franco Gasperino 2021-04-19T18:42:34.243Z

when you want to disable that pedestal logging, you may remove-tap.

Franco Gasperino 2021-04-19T18:42:52.243200Z

and you may add/remove it at runtime

Sibelius Seraphini 2021-04-19T18:47:33.243400Z

I’m gonna try this inspect

Franco Gasperino 2021-04-19T18:48:11.243600Z

inspect is part of shadow-cljs, and is for clojurescript

Franco Gasperino 2021-04-19T18:49:05.244100Z

uses the same tap mechanism though, but the implementation is something akin to javascript websocket -> browser display

grazfather 2021-04-19T18:49:22.244600Z

would anyone be willing to take a look at a babashka script I wrote? Just looking for ideas where I could have done things better https://pastebin.com/Qa5JSeGy

grazfather 2021-04-20T13:31:55.268100Z

thank you!

piyer 2021-04-19T19:18:34.245800Z

Cider question: Is there a way to jump to repl from the buffer?

dpsutton 2021-04-19T19:19:57.246200Z

m-x cider-switch-to-repl-buffer. i think it already has a default key binding

piyer 2021-04-19T19:20:21.246400Z

ah nice. Thank you

Darrell 2021-04-19T19:38:05.248Z

I’m trying to create a set based on lines from a file. Is this a valid approach?

(def co_set #{})
(with-open [rdr (reader "customer_numbers.txt")] (doseq [line (line-seq rdr)] (def co_set  (conj co_set line))))

piyer 2021-04-19T19:39:26.249100Z

you are def co_set on every seq.

Darrell 2021-04-19T19:40:39.249900Z

I’m assuming that wouldn’t replace co_set?

Darin Douglass 2021-04-19T19:41:23.250800Z

i'd reach for into here: (into #{} (line-seq rdr))

Darin Douglass 2021-04-19T19:42:35.252500Z

it would, but in my experience non-top-level defs are frowned-upon

Prabu Rajan 2021-04-19T19:43:03.252600Z

Hi, I have the following spec for the below function. I want to validate that the passed in menu contains the item requested in the order item. My goal is to remove the nil checks and throw exception in my function code. My state looks like the following. If I pass in invalid input, lets say I pass in an order-item with a name that does not exist in my menu, the fdef does not validate that. Any idea what I am doing wrong in the fdef?

{:orders {:e4d55743-c964-48e8-9cd1-cb1cf9075617 [#:helloworld.restaurant{:name "chilly parotta",
                                                                                                :quantity 3}
                                                                        #:helloworld.restaurant{:name "masal dosa",
                                                                                                :quantity 2}]},
                        :menu {:kothu-parotta #:helloworld.restaurant{:name "Kothu Parotta", :price 9.5, :quantity 50},
                               :chicken-biriyani #:helloworld.restaurant{:name "Chicken Biriyani",
                                                                         :price 10.5,
                                                                         :quantity 50},
                               :plain-dosa #:helloworld.restaurant{:name "Plain Dosa", :price 9.5, :quantity 50},
                               :butter-naan #:helloworld.restaurant{:name "Butter Naan", :price 3, :quantity 50},
                               :paneer-butter-masala #:helloworld.restaurant{:name "Paneer Butter Masala",
                                                                             :price 9.5,
                                                                             :quantity 50},
                               :mutter-paneer #:helloworld.restaurant{:name "Mutter paneer", :price 10.5, :quantity 50},
                               :masal-dosa #:helloworld.restaurant{:name "Masal Dosa", :price 8.5, :quantity 48},
                               :chilly-parotta #:helloworld.restaurant{:name "Chilly Parotta", :price 9.5, :quantity 47},
                               :mutton-biriyani #:helloworld.restaurant{:name "Mutton Biriyani",
                                                                        :price 10.5,
                                                                        :quantity 50}}}
(defn update-menu-item-for-order-item [menu order-item]
  (let [name (::name order-item)
        item-id (keyword (slugify (::name order-item)))
        order-quantity (::quantity order-item)]
    (update-in menu
               [item-id]
               (fn [item]
                 (if (nil? item)
                   (throw (IllegalArgumentException. (str "Sorry, we don't have " name " in our menu")))
                   (if (< (::quantity item) order-quantity)
                     (throw (IllegalArgumentException.
                              (str "Not enough " (::name item) ", we are short by "
                                   (- order-quantity (::quantity item))
                                   " . Please correct the order and re-submit")))
                     (assoc item ::quantity (- (::quantity item) order-quantity))))))))

(s/def ::name string?)
(s/def ::price number?)
(s/def ::quantity number?)
(s/def ::order-item (s/keys :req [::name ::quantity]))
(s/def ::order-items (s/coll-of ::order-item))
(s/def ::menu-item (s/keys :req [::name ::price ::quantity]))
(s/def ::menu-item-id keyword?)
(s/def ::menu (s/map-of ::menu-item-id ::menu-item))

(s/fdef restaurant-save-menu :args (s/cat :menu-items (s/coll-of ::menu-item)))

(s/fdef update-menu-item-for-order-item
        :args (s/and (s/cat :menu ::menu :order-item ::order-item)
                     #(contains? (:menu %) (keyword (slugify (::name (:order-item %))))))
        :ret ::menu)

(stest/instrument `update-menu-item-for-order-item)

Prabu Rajan 2021-04-20T07:10:15.262800Z

“If this is something that you would previously — before Clojure, before Spec — written as explicit checks in production with either error handling or exceptions, then that’s how you should write it now, in Clojure — with Spec if you want to leverage its ability to validate data.” > Lets say I want explicit validation of function arguments in some edge function in production. Is it recommended to still define specs and then use s/conform or s/valid? s/explain-data to write the validation logic instead of regular clojure code?

seancorfield 2021-04-20T15:46:42.268400Z

If you have a non-trivial data structure and you use it in multiple functions, yes, I’d say that’s a good case for Spec.

seancorfield 2021-04-20T15:47:15.268600Z

We use Spec to validate API input arguments, so that’s a case where each function might have a different set of :params from a Ring request.

seancorfield 2021-04-20T15:48:23.268800Z

(we used to use coercions in our Specs but that is bad practice so we’ve shifted to using exoscale/coax to do the coercions — it deduces them from a Spec — and then use a plain old Spec to do the actual conforming/validating part)

seancorfield 2021-04-20T15:51:14.269Z

Re: 1. above — we don’t have anything we can share but this is the bulk of how it works:

(defn invalid-argument
  ([req spec fail-code-map]
   (forbidden req "Invalid Argument"
              (when fail-code-map
                (->> (:params req)
                     (s/explain-data spec)
                     (problems->fail-codes fail-code-map)))))
  ([req fail-code]
   (forbidden req "Invalid Argument" (if (vector? fail-code) fail-code [fail-code])))
  ([req]
   (forbidden req "Invalid Argument")))

(defmacro with-validated-params
  [[spec fail-code-map] & body]
  `(let [~'params (s/conform ~spec (:params ~'req))]
     (if (s/invalid? ~'params)
       (invalid-argument ~'req ~spec ~fail-code-map)
       (do ~@body))))
forbidden just constructs a 403 HTTP response, problems->fail-codes is a set of proprietary heuristics that maps Spec failures to entries in the fail-code-map.

seancorfield 2021-04-20T15:51:49.269200Z

Re: 2 — there are plenty of resources out there about test.check.

Prabu Rajan 2021-04-20T17:18:45.274500Z

Thanks @seancorfield you would need to use coercions to coerce strings in API requests (from json) to clojure types, correct? Thanks for the code on usage of s/explain-data. Will try it out

seancorfield 2021-04-20T17:25:04.274700Z

Depends on whether you’re dealing with JSON or plain URL/form parameters but, yes, mostly you need something to coerce strings to numbers, booleans, dates, etc — and then conform with Spec.

Prabu Rajan 2021-04-20T17:34:43.274900Z

sure, makes sense

Darrell 2021-04-19T19:46:54.252800Z

Thanks!

Darrell 2021-04-19T19:49:57.255Z

So, next question: I have two files-- File A has 24435 unique sorted lines and File B has unique sorted 22391 lines. I’m reading the lines of each file into a sequence using the method I asked about previously so that I can get a difference between the two with (count (set/difference A_set B_set)). However, when I do that, I get “22391” which I’m certain isn’t correct. Is there a better way to get the difference between the two files?

Darin Douglass 2021-04-19T19:58:57.257Z

since they're sorted it'd probably be worth to take, say, the first 10 lines of each file to get a better understanding of your problem. this problem feels like a "fix one small thing and the rest will settle out"

Darrell 2021-04-19T20:08:04.258Z

@ddouglass I guess I’m going to have to. Something is really throwing this off. I just tried @alexmiller’s suggestion and that gives me a count of “3”, which isn’t right either. I’m expecting a few hundred.

2021-04-19T20:46:43.258100Z

using def inside other code is very rarely a good idea

👍 1
seancorfield 2021-04-19T20:58:00.258300Z

@prabu.rajan This works as expected for me. Did you perhaps redefine the function and then forget to re-run the instrument code?

seancorfield 2021-04-19T20:58:10.258500Z

Here’s what I get:

{:path []
                                                                :pred (clojure.core/fn
                                                                       [%]
                                                                       (clojure.core/contains?
                                                                        (:menu
                                                                         %)
                                                                        (clojure.core/keyword
                                                                         (helloworld.restaurant/slugify
                                                                          (:helloworld.restaurant/name
                                                                           (:order-item
                                                                            %))))))
                                                                :val {:menu {:kothu-parotta {:helloworld.restaurant/name "Kothu Parotta"
                                                                                             :helloworld.restaurant/price 9.5
                                                                                             :helloworld.restaurant/quantity 50}
                                                                             :chicken-biriyani ...

seancorfield 2021-04-19T20:58:39.258700Z

(when I request something that is not on the menu)

2021-04-19T21:42:15.259Z

I think your extract-host-project function could be a little simpler with the power of destructuring

Prabu Rajan 2021-04-19T21:56:17.259200Z

Thanks @seancorfield, for some reason the same code works fine for me now. I added the other validation too now in the fdef (to validate the order quantity should be lesser than the quantity in the inventory) and it works fine. I am experiencing the awesomeness of clojure and spec!

(s/fdef update-menu-item-for-order-item
        :args (s/and (s/cat :menu ::menu :order-item ::order-item)
                     #(contains? (:menu %) (keyword (slugify (::name (:order-item %)))))
                     #(< (::quantity (:order-item %))
                         (->> (:menu %)
                              ((keyword (slugify (::name (:order-item %)))))
                              (::quantity))))
        :ret ::menu)

Prabu Rajan 2021-04-19T21:57:43.259400Z

With the function spec in place, I simplified my function’s logic to remove all nil and inventory checks and throwing exceptions and all that stuff like below

(defn update-menu-item-for-order-item [menu order-item]
  (let [item-id (keyword (slugify (::name order-item)))
        order-quantity (::quantity order-item)]
    (update-in menu
               [item-id]
               #(assoc % ::quantity (- (::quantity %) order-quantity)))))

seancorfield 2021-04-19T21:59:12.259600Z

Just bear in mind that you would normally only use instrument in dev/test, not in production so you need to think about what should happen if your code violates that contract in production with no instrumentation in place.

Prabu Rajan 2021-04-19T22:03:45.259800Z

So, should I still have the original checks in my code so that it gets validated in production? What is the recommended approach? Maybe is it ok that only external facing API functions have such checks in production and all internal functions use spec instrumentations at dev/test?

Darrell 2021-04-19T22:25:33.260500Z

Turns out it was a Byte Order Mark hiding out at the beginning of one of the files.

seancorfield 2021-04-19T22:31:30.260600Z

It depends.

seancorfield 2021-04-19T22:32:30.260800Z

If this is something that you would previously — before Clojure, before Spec — written as explicit checks in production with either error handling or exceptions, then that’s how you should write it now, in Clojure — with Spec if you want to leverage its ability to validate data.

seancorfield 2021-04-19T22:33:02.261Z

If you only want these checks in place while you develop & test your code, instrument is a reasonable way to approach it.

seancorfield 2021-04-19T22:33:33.261200Z

I can’t remember if I’ve linked you to this before? https://corfield.org/blog/2019/09/13/using-spec/

seancorfield 2021-04-19T22:34:55.261500Z

If you watch some of the talks about Spec, you’ll see there’s often a recommendation to use Spec around system boundaries and not for internal functions, on the grounds that it is not a type system.

seancorfield 2021-04-19T22:36:34.261700Z

But while you are developing functions that manipulate complex data (or have complex relationships encoded in the data) using Spec can be very valuable to help you get the functions correct, both via instrumentation and via generative testing (such as stest/check).

alexmiller 2021-04-19T23:15:23.262100Z

Classic :)