Fork me on GitHub
#beginners
<
2021-08-30
>
finchharold05:08:43

Hello, I am trying to pass data that is of a list (or a seq?) format kinda.

(let [info (if is-correct?
             {:Name "Wags", :age 55, :working true}
             {:Name names, :age ages :working (case ages -1 true -2 false)})]
     (println "info" info))
The values of names and ages is (Mafee) and (-1) respectively

finchharold05:08:10

What's going on?

dpsutton05:08:55

Your case statement has no matching clause. You have a clause for -1 and for -2. It got some lazy sequence as a value not those two numbers you handle

finchharold05:08:29

How to use lazy seq then?

dpsutton05:08:12

Don’t really think you can

dpsutton05:08:22

Case is dispatching on compile time literals

dpsutton05:08:50

What values are in that seq?

finchharold05:08:00

Then, if we are using lazy seq then what's the alternative to using case?

finchharold05:08:13

(-1 -2) or reverse

finchharold05:08:22

but many of them but again only -1 and -2

dpsutton05:08:01

(let [x [-1 -2]]
  (case x
    [-2 -1] :asc
    [-1 -2] :desc))
:des

finchharold05:08:31

Have to convert it to vector?

noisesmith17:08:12

no conversion is needed:

user=> (let [v (list 1 2 3)] (case v [1 2 3] :OK))
:OK

dpsutton05:08:08

i don't know how to help with your particular problem needs here. But i'm just helping you understand the illegal argument exception. It said there was no matching clause. you provided two clauses, one for -1 and one for -2. Giving it a sequence of values cannot work

dpsutton05:08:26

(vec (list 1 2))

noisesmith17:08:10

this vec call isn't needed, case already considers (list 1 2) and [1 2] to be a match

dpsutton17:08:40

oh nice. wasn't aware of that

Bobbi Towers05:08:30

Maybe you just need to grab the first item in the list, i.e.

(let [info (if false
             {:Name "Wags", :age 55, :working true}
             {:Name (first names), :age (first ages) :working (case (first ages) -1 true -2 false)})]
  (println "info" info))

;=> info {:Name Mafee, :age -1, :working true}

πŸ‘ 2
Lukas08:08:02

Hi, I'm trying pedestal and I wonder if there is a way to reload routes (for example after changing an interceptors) without restarting the server every time?

finchharold08:08:28

Hi all, I'm trying doall and don't know why the true condition, in this case, isn't working at all.

(doall (map (fn [age working exit]
                (let [info (if present
                             {:name "Wags", :age 50, :working true, :exit nil}
                             (if-not (nil? working)
                               {:Name "Mafee", :is-left (cond (= exit -1) true (= exit -2) false)
                                :working working, :age age}
                               {:name "Wags", :age 50, :working true, :exit nil}))]
                  (println "info" info)
                  )) age working exit))

Lukas09:08:23

I'm not sure but I guess the problem is your 3 arity function

Lukas09:08:42

(def present false)
(doall (map (fn [[age working exit]]
                (let [info (if present
                             "present"
                             (if-not (nil? working)
                               "not present working nil"
                               "present and working"))]
                  (println "info" info)))
            [[10 nil -1]]))

Lukas09:08:26

=> (nil) prints "info present and working"

Lukas09:08:11

(def present true) => (nil) prints "info present"

Lukas09:08:50

(def present true)
(doall (map (fn [age working exit]
                (let [info (if present
                             "present"
                             (if-not (nil? working)
                               "not present working nil"
                               "present and working"))]
                  (println "info" info)))
            [10 21] [nil nil] [-1 -2]))
your function fn [age working exit] expects to get 3 collection passed

Lukas09:08:41

glad I could help πŸ™‚

delaguardo10:08:15

(defn process-f [age working exit]
  (let [info (if present
               {:name "Wags", :age 50, :working true, :exit nil}
               (if-not (nil? working)
                 {:Name "Mafee", :is-left (cond (= exit -1) true (= exit -2) false)
                  :working working, :age age}
                 {:name "Wags", :age 50, :working true, :exit nil}))]
    (println "info" info)))

(doall (map process-f age working exit))
I rewrite slightly the code snippet to highlight that it is not the function expecting three collection to be passed but map function. process-f is still expects three arguments (not collections!)

Lukas11:08:11

Thank you for clarifying this.

finchharold08:08:20

Only the when present is false it returns as per the false condition but when the present is true, it doesn't return anything

Adam Stokes14:08:45

Hello! I'm working with a small cli tool and attempting to support sub-subcommands in the form of:

rbt cluster query -q "/_cat/indices" -n mycluster
So far this is what I've come up with "borrowing" from various sources:
(ns rbt.main
  (:gen-class)
  (:require
   [clojure.tools.cli :refer [parse-opts]]
   [rbt.cmd.cluster :as cluster]))

(def cli-opts [["-h", "--help", "Display help"]])

(defn -main
  [& args]
  (let [opts (parse-opts args cli-opts :in-order true)
        config (:config (:options opts))
        subcmd (first (:arguments opts))
        subcmd-args (rest (:arguments opts))]
    (cond
      (:help (:options opts)) (prn "You do need help.")
      (= "cluster" subcmd) (cluster/parse subcmd-args)
      (= "help" subcmd) (prn "You need some help?")
      :else (prn "No idea.")))
  (shutdown-agents))

(ns rbt.cmd.cluster
  (:require
   [clojure.tools.cli :refer [parse-opts]]
   [rbt.cluster.query :as query]))

(def cli-opts
  [["-q", "--query ENDPOINT", "Endpoint to query"]
   ["-n", "--name CLUSTER", "Name of cluster to query"]
   ["-h", "--help", "Display help"]])

(def usage
  "Use -q query and -n name to define the query and name parameters

Usage: rbt cluster query -q \"/_cat/indices\" -n k8s-prod-zone-1")

(defn parse
  "Access cluster subcommand"
  [& args]
  (let [opts (parse-opts args cli-opts :in-order true)
        config (:config (:options opts))
        subcmd (first (:arguments opts))
        subcmd-args (rest (:arguments opts))]
    (cond
      (:help (:options opts)) (println usage)
      (= "help" subcmd) (println usage)
      (= "query" subcmd) (query/parse subcmd-args)
      :else (println usage))))
The error I am attempting to resolve is:
Exception in thread "main" java.lang.ClassCastException: class clojure.lang.PersistentVector$ChunkedSeq cannot be cast to class java.lang.CharSequence (clojure.lang.PersistentVector$ChunkedSeq is in unnamed module of loader 'app'; java.lang.CharSequence is in module java.base of loader 'bootstrap')
	at clojure.core$re_matcher.invokeStatic(core.clj:4838)
	at clojure.core$re_seq.invokeStatic(core.clj:4863)
	at clojure.core$re_seq.invoke(core.clj:4863)
	at clojure.tools.cli$tokenize_args.invokeStatic(cli.cljc:34)
	at clojure.tools.cli$tokenize_args.doInvoke(cli.cljc:15)
	at clojure.lang.RestFn.invoke(RestFn.java:464)
	at clojure.tools.cli$parse_opts.invokeStatic(cli.cljc:755)
	at clojure.tools.cli$parse_opts.doInvoke(cli.cljc:564)
	at clojure.lang.RestFn.invoke(RestFn.java:464)
	at rbt.cmd.cluster$parse.invokeStatic(cluster.clj:17)
	at rbt.cmd.cluster$parse.doInvoke(cluster.clj:14)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at rbt.main$_main.invokeStatic(main.clj:17)
	at rbt.main$_main.doInvoke(main.clj:9)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at rbt.main.main(Unknown Source)
This is using borkdude/sci so that I can compile it down to a single executable/startup time speed. From what I can tell in the exception is possibly the second invocation of parse-opts is failing to find any arguments?

semperos14:08:12

what does (rest (:arguments opts)) look like in your rbt.main/-main function? i.e., the value of subcmd-args

Adam Stokes14:08:16

java -jar rbt.jar cluster query -q "/_cat/indices" -n k8s-prod-zone-1
("query" "-q" "/_cat/indices" "-n" "k8s-prod-zone-1")

semperos14:08:04

cool; when you pass that collection to your parse function, the [& args] signature of that function means that it expects a variable number of arguments, and so args is itself a collection, so you're passing [["query" "-q" ,,,]] to the underlying parse-opts function

semperos14:08:22

either don't make parse varargs, or use apply

Adam Stokes14:08:16

So apply will take it out of that collection then and I can still use [& args]

Adam Stokes14:08:23

is that typically more flexible?

semperos14:08:52

if you control all the functions, I find using varargs as little as possible to be the least surprising, since it's collections in, collections out, without any auto-wrapping like you have with varargs

Adam Stokes14:08:02

Gotcha, thank you for the pointers!

1
Dan Lucas15:08:24

Afternoon all. As I started writing Clojure for the first time yesterday I think I qualify as a beginner and this is very much a beginner task. Using Emacs, Cider, and Leiningen on Windows 10, I'm extracting text for processing from a one-page purchase order in PDF format. I can achieve this with pdfboxing by putting this in the ns form of my core.clj, like so: (:require [pdfboxing.text :as text]) I can also put that require statement in the body of core.clj rather than in the ns form, but I'm still fuzzy on the difference between the two. Then I can use it like this: (def jtext (text/extract "./test.pdf")) ...which just works, and leaves me with a string representing all the text on the page, with "\r\n" delimiting the various fields. These fields do not always appear in the same order, but because it is mostly boilerplate I can use a regex. If I want to extract the purchasing manager's name (which could be one of several different people), I know that this comes after the name of the company issuing the order and before a brief description of the order, and this description itself comes before the name of the company receiving the order. Something like this: BigCompany Inc.\r\nJane Smith\r\nStainless steel materials\r\nSmallCompany Ltd. So I have a field with a known structure, two fields of variable text (the manager's name and the order description), followed by another field with known structure. Though I'm still not familiar with Clojure/Java's flavour of regex it appears that I can apply a regex with groups like this: (def manager-text (re-find #"BigCompany Inc.\r\n(.+)\r\n(.+)\r\nSmallCompany Ltd" jtext)) ...and I can then take the element of the vector containing the name: (def manager (manager-text 1)) This seems to work, but feels a bit clunky and fragile. Is there a more idiomatic way to approach this in Clojure (destructuring...?), or is this just one of those times where something crude is good enough and I should just move on? Dan

Ed15:08:52

I feel like I would reach for either instaparse or clojure.spec for this sort of thing. I'm afraid I'm on my phone and away from a REPL at the moment. But have a look at both of those libraries and see if you think they'll help.

πŸ™ 1
pavlosmelissinos16:08:34

@U02CVCL55N0 I've actually raised a PR https://github.com/dotemacs/pdfboxing/pull/62 that allows you to selectively extract parts of the page by specifying sets of coordinates. It hasn't been merged yet but you might want to give it a try. I've been using it for quite some time in another project of mine. No problems so far πŸ™‚

πŸ‘€ 1
sheluchin15:08:44

The style guide recommends vertically aligning function arguments with the second argument whenever there is one on the same line as the function: https://guide.clojure.style/#one-space-indent I find this recommendation a little awkward for functions that have a "generic" first argument like app or this. Recommended:

(comp/transact! this
                [(save-thing {:foo :bar})])
My preferred:
(comp/transact! this
 [(save-thing {:foo :bar})])
A third alternative:
(comp/transact!
 this
 [(save-thing {:foo :bar})])
How important is it to stick to the recommended style?

sheluchin15:08:27

heh, maybe I'm a bit shook over style violations after reading some comments on the orange website about how Clojure newbies often produce the ugliest codebases.

Joshua Suskalo15:08:27

If you use cider, then the "correct" way to do this is to tag your comp/transact! with a {:style/indent 1} metadata, which will produce indentation like this:

(comp/transact! this
  [(save-thing {:foo :bar}])

dpsutton15:08:24

make ugly code. as you code more you will develop your own sense of style and how important it is. feel free to evolve and make your own style choices as you go

Joshua Suskalo15:08:49

^ this The only caveat I have to this is that I usually try to not fight my auto indenter, which is cider, so I've learned cider's indent metadata syntax and include it even in libraries I release so that my indentation can be consistent across not just my code, but anyone who uses cider, and it'll be better than the default for my usecases.

Joshua Suskalo15:08:36

As an example, I wrote a macro restart-case which indents like this:

(restart-case
    (some code)
  (::a-restart [some args]
    (some code)))
If I used "default" indentation, then it would have indented like this:
(restart-case
 (some code)
 (::a-restart [some args]
              (some code)))
I think this is much less readable, which is why I put the effort into making this better.

sheluchin15:08:05

I haven't looked at adding manual configs for Cider at all. Good tips, I'll explore the options there.

Adam Stokes16:08:41

With code organization and defining namespaces, is it acceptable to have a file and directory named the same but can still pull out the functionality from each? For example, I wanted to break down my subcommands under rbt.cmd.cluster.query which would be src/cmd/cluster/query.clj but I also wanted to have some syntatic sugar in rbt.cmd.cluster which would be src/cmd/cluster.clj. If I organize code like this is that acceptable?

ghadi16:08:00

Yes @adam.stokes not uncommon

ghadi16:08:04

be mindful of not weaving circular dependencies

Adam Stokes16:08:18

Great! Thank you, will keep the cyclic deps in mind, I've hit that a few times with similar naming schemes in python as well

noisesmith17:08:52

@adam.stokes another thing to keep in mind is that there's no hierarchical relationship between nested directories / namespace fragments, beyond what gets imposed by convention / idiom. there's no reason that foo.bar.baz can't require foo.bar for example (ie. putting protocol / multimethod definitions in foo.bar and putting an implementation in foo.bar.baz...)

Adam Stokes17:08:21

Gotcha, that makes a lot of sense. I think that is also the difference with how Python does it, there seems to be a direct relation with the directory itself for resolving the namespaces for module imports