Fork me on GitHub
#beginners
<
2019-04-17
>
cfeckardt09:04:31

I can only think of one way to get the cartesian product of two infinite seqs, but it's not very fast. You can employ the same trick that is used to show countable cardinality of products of uncountable sets. https://en.wikipedia.org/wiki/Pairing_function#Cantor_pairing_function The image explains the idea: https://en.wikipedia.org/wiki/Pairing_function#/media/File:Diagonal_argument.svg It's basically two nested loops to determine which nth to take from each sequence.

holmes8913:04:10

I'm trying to create an API using compojure and the middleware functions are of the format

:middleware [mw/a mw/b mw/c]
And I was wondering if there is a way to define something like
(def common-mw [mw/a mw/b mw/c])
so I can do
:middleware [mw/common-mw]
? (I've tried this and it doesn't seem to work)

michael.e.loughlin14:04:10

common-mw returns a vec, so your invocation looks like :middlware [[mw/a mw/b mw/c]]. Have you tried :middleware mw/common-mw ?

holmes8914:04:39

I have, but let me try it again

holmes8914:04:52

(I was getting an error but can't remember what it was)

holmes8914:04:24

Don't know how to create ISeq from: clojure.lang.Symbol

michael.e.loughlin14:04:28

do you have a snippet of your method invocation? Are you using metosin/compojure-api ?

holmes8914:04:52

yes, I'm using compojure-api, sorry, new to all of this so I didn't realize the difference between the two.

(def token-backend
  (jws {:secret (env :secret) :options {:alg :hs256}}))

(defn authenticated
  [handler]
  (fn [request]
    (if (authenticated? request)
      (handler request)
      (unauthorized {:error "Not authorized"}))))

(defn token-auth
  [handler]
  (wrap-authentication handler token-backend))
(defroutes profile-routes
           (POST "/" []
             :header-params [authorization :- s/Str]
             :responses {created ProfileRequestSchema}
             :body [create-profile-req ProfileRequestSchema]
             :middleware [token-auth authenticated]
             (create-profile-handler create-profile-req)))

oliver.marshall14:04:47

Because it's a macro which then calls a function, the function is called at macro time which means that the args aren't evaluated yet (or something like that)

oliver.marshall14:04:46

Basically you have to have it as a literal list rather than defining a var

oliver.marshall14:04:10

(Unless you cheat and define your own macro that returns the middleware, but that sounds like a very poor solution)

holmes8914:04:05

(again new) to treat it as a literal list would I do

(def auth-mw '(token-auth authenticated))

michael.e.loughlin15:04:27

yes, that stops the form from being evaluated

holmes8915:04:22

so I get this error: clojure.lang.PersistentList cannot be cast to clojure.lang.IFn

holmes8915:04:53

and if I don't do the [] I get Don't know how to create ISeq from: clojure.lang.Symbol

oliver.marshall15:04:32

Here's a simple example of the problem that you're having:

oliver.marshall15:04:36

(defn my-fn
  [arg]
  (println (first arg)))

(defmacro my-macro
  [arg]
  (my-fn arg))

(my-macro [1 2 3 4])
;; => 1

(def my-var [1 2 3 4])

(my-macro my-var)

;; => IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:542)

oliver.marshall15:04:28

If you swap out the my-macro definition with the following it might make more sense for you:

oliver.marshall15:04:31

(defmacro my-macro
  [arg]
  (println "my-macro arg:" arg)
  (my-fn arg))

oliver.marshall15:04:48

The first example prints my-macro arg: [1 2 3 4]

oliver.marshall15:04:18

The second example prints my-macro arg: my-var

holmes8915:04:08

That makes sense basically it's not interpreting my def as a list but rather just passing the argument through

holmes8915:04:39

so there isn't a way to do what I want to without writing my own macro?

holmes8915:04:34

.... or can I use eval?

holmes8915:04:09

I guess that would be the same issue

oliver.marshall15:04:27

You're right you'd have the same issue there

oliver.marshall15:04:51

There might be some kind of inline macro you could use

oliver.marshall15:04:54

Or you could write one

oliver.marshall15:04:02

That way it would be a little cleaner

oliver.marshall15:04:28

Maybe the takeaway from this is that writing macros is hard, it certainly is for me 🙂

holmes8915:04:01

lol, yeah, that's what I've been trying to avoid 😆

holmes8915:04:22

not something I think I'm ready to tackle, thank you both for all of your help!

michael.e.loughlin11:04:47

I took another look at why the library was behaving this way and I think I tracked it down to a couple evaluations of (if (seq middleware) ... ) in meta/restructure https://github.com/metosin/compojure-api/blob/00d6e3a25c442dcf060f5f6d5aa71e830db142bf/src/compojure/api/meta.clj#L678

michael.e.loughlin12:04:12

I'm interested to know the best way to modify the macro to make it work how we hope. I suspect it's a combination of checking the type of middleware at compile time instead of just assuming it's Seqable

somedude31418:04:50

Just curious if there's a better way to do this: (last (str/split "a/b/cd" "/"))

noisesmith18:04:58

for starters, "/" needs to be #"/"

noisesmith18:04:20

you could use peek instead of last for a minor perf improvement, I don't know if that makes the code any clearer

noisesmith19:04:33

@somedude314 you can also use threading, it may or may not make this code more clear, but with more nested calls it tends to be helpful

user=> (-> "a/b/cd" (clojure.string/split #"/") (peek))
"cd"

somedude31419:04:11

Yeah, I am familiar with threading. It's just I am still hesitant writing Clojure code because I often find there's a shorter and cleaner way to do what I came up with 😄

alexmiller19:04:21

there are various regex pattern tricks for things like this - generally googling/so'ing for Java solutions will find compatible answers

alexmiller19:04:47

like you can probably use some kind of negative lookahead with capture to match only the last, although I can't say I have the details at hand

noisesmith19:04:58

this site is excellent quality for general regular expression features and tricks, it breaks things down per implementation https://www.regular-expressions.info/

alexmiller19:04:45

if you do use something like this, leave a comment so the next person that wanders along has a clue what you did :)

lennart.buit19:04:56

You certainly can use some fancy regex, but whether its worth is is a different story. Code golfing got me to (re-find #"(?<=/)(?!.*/.*).*$" "a/b/cd"), which matches a / but doesn’t consume it followed by anything that is not a slash and a line terminator. Please don’t do that, the next one reading that code will … have a funny look in his/her face

lennart.buit19:04:19

So thats (?<=/), match a slash but don’t consume it. (?!.*/.*) lookahead whether there is no following slash and .*$ match anything else followed by an end of line

lennart.buit19:04:33

again; you probably don’t want to

lennart.buit19:04:29

… I just got carried away by “can this be regex’ed”, which ends up being “yes” most of the time.

lennart.buit19:04:35

(I mainly wanted to share to show the incredible power of regex ^^!)

dorab22:04:55

Would (re-find #"^,*/([^/]*)$" "a/b/cd") work?

lennart.buit06:04:03

That produces two matches, one for the entire string and one for your capture group

lennart.buit06:04:20

So it would work if you do a non-consuming match like this: (re-find #"(?<=^.*/)[^/]*$" "a/b/cd")

somedude31420:04:37

Thanks for the tips. I think I am going to stick with the Clojure version.

lennart.buit20:04:44

Yes! please do

noisesmith20:04:53

I don't think you'd ever need this version, but it avoids allocating strings and collections, and is more readable than the pure regex version

(defn lastmatch 
  [string re]
  (let [m (re-matcher re string)]
    (loop [start nil
           end nil]
      (cond (.find m) (recur (.start m) (.end m))
            start (subs string start end)
            :else nil)))) 

noisesmith20:04:49

but that asks for the inversion of the splitting regex (`#"[^/]+"`) rather than the splitting regex itself

lockdown-20:04:39

Is there a specific clojure idiom for naming functions that make DB calls?

lockdown-20:04:30

ex: like adding ! to the end

noisesmith20:04:58

there's a loose idiom of suffixing ! to name things that do side effects, and a db call is a side effect; it's inconsistently followed

lockdown-21:04:53

having them ns prefixed is one way, but don't want to create a new one just yet

noisesmith21:04:47

I wouldn't use an ns to indicate something touches the db - most namespaces will have a mixture of various levels of code plus helper code and useful constants.

lockdown-21:04:30

true, a strict separation can create more trouble than it is worth

lockdown-21:04:11

I'll just split this thing when I reach 500LoC heh

noisesmith21:04:22

I've never even seen clojure code try to segregate things that way

lockdown-21:04:09

yeah, seems like "premature orgnization" 🙂

lockdown-21:04:59

@noisesmith just found this https://gist.github.com/noisesmith/ebe8b3f185e34a7de04b1189b21ba59b - do you still use it? how did it work out?

noisesmith21:04:26

checking if I modified it since...

noisesmith21:04:41

that's the version I still use, it's pretty good (imperfect since it's regex based of course)

noisesmith21:04:03

iirc mine was extended from that one

noisesmith21:04:11

with some help from various irc users

noisesmith21:04:45

I have a hunch that vladh gist misses a bunch of valid (though semi-rarely-used) identifiers, and if I'm reading it correctly it also matches invalid identifiers like 1foo

noisesmith21:04:58

wow, they both lack deftype :/

noisesmith21:04:21

and defrecord!

noisesmith21:04:29

so yeah, this could clearly use improving

noisesmith21:04:01

another thing the vladh one misses that mine doesn't is metadata eg (defn ^:foo bar [] ..) - mine recognizes that as defining bar, the other doesn't

lockdown-21:04:05

going to try ag and swiper (emacs) to see how it goes

noisesmith21:04:10

cool, since mine is already a community effort I should make a github repo for it

lockdown-21:04:06

yep, I'll complement it with other emacs stuff, do you rebuild the index on every save?

noisesmith21:04:11

I typically rebuild before doing tag queries (I'll often do a few different ones in a row), the latency of rebuilding the full tags is indistinguishable from the latency on normal editor commands on any computer I use for development. It would be smart to hook tag building into save though.

noisesmith21:04:50

@lockdown- hard mode: define a tag regex that matches a protocol method definition but not its implementation(?) - or maybe it's worth it to catch both

noisesmith21:04:38

that might in fact be impossible given line oriented regex based definitions

noisesmith21:04:56

OOOOOH - the "pattern" is actually an ex command, so with some work you could probably find and match protocol methods inside a protocol... I misred, it only uses ex commands to find the tag on lookup, not to parse code for tags

lockdown-22:04:25

I still haven't studied clojure's protocol but sounds worthy 😉, you don't use some grep tool? ag, ripgrep, grep, git grep, etc...

nathantech200522:04:50

I’ve been using grep inside the repl to search for methods

nathantech200522:04:08

how is the swiper emac plugin? Is it a code auto complete plugin?

lockdown-22:04:08

no, with something like ag, it lets you search for names project wide with a nice overview

nathantech200522:04:16

ahh, similar to grep?

lockdown-23:04:33

yeah, but gives suggestions, something like a fuzzy finder and grep

lockdown-22:04:53

in the editor I mean

noisesmith22:04:33

I typically use the default tag lookup in neovim, or the built in :grep ...

noisesmith22:04:51

then I use :cope to browse and follow the list of matches

noisesmith22:04:42

@lockdown- a more reliable method to find implementations uses the source metadata that the compiler collects and puts on vars, this requires integrating a repl client into the editor (eg. vim/fireplace intellij/cursive emacs/cider)

noisesmith22:04:42

@lockdown- a more reliable method to find implementations uses the source metadata that the compiler collects and puts on vars, this requires integrating a repl client into the editor (eg. vim/fireplace intellij/cursive emacs/cider)

lockdown-23:04:21

yeah, was looking how cider does it, looks it relies on some nrepl middleware

noisesmith00:04:57

between these two calls in a vanilla repl, you have all the info needed to open a file to a specific line:

(cmd)user=> (pprint (meta #'clojure.core/reduce))
{:arglists ([f coll] [f val coll]),
 :doc
 "f should be a function of 2 arguments. If val is not supplied,\n  returns the result of applying f to the first 2 items in coll, then\n  applying f to that result and the 3rd item, etc. If coll contains no\n  items, f must accept no arguments as well, and reduce returns the\n  result of calling f with no arguments.  If coll has only 1 item, it\n  is returned and f is not called.  If val is supplied, returns the\n  result of applying f to val and the first item in coll, then\n  applying f to that result and the 2nd item, etc. If coll contains no\n  items, returns val and f is not called.",
 :added "1.0",
 :line 6810,
 :column 1,
 :file "clojure/core.clj",
 :name reduce,
 :ns #object[clojure.lang.Namespace 0x13f95696 "clojure.core"]}
nil
(cmd)user=> ( "clojure/core.clj")
#object[java.net.URL 0x66746f57 "jar:file:/Users/justin.smith/.m2/repository/org/clojure/clojure/1.10.0/clojure-1.10.0.jar!/clojure/core.clj"]

noisesmith00:04:19

that's a resource (even works inside a jar), plus a line and column

lockdown-00:04:00

Wow nice, I'm getting some weird line/columns numbers though.

lockdown-00:04:28

connection is a function in dev.clj

lockdown-01:04:46

dev=> (clojure.pprint/pprint (meta #'connection)) {:arglists ([]), :line 1, :column 1, :file "NO_SOURCE_PATH", :name connection, :ns #object[clojure.lang.Namespace 0x4c39ad4a "dev"]} nil dev=> (http://clojure.java.io/resource "dev") nil dev=>

noisesmith17:04:15

this is a result of loading via an nrepl editor integration rather than require iirc

noisesmith17:04:51

NO_SOURCE_PATH generally means "this was defined directly in a repl and the compiler didn't know of any file it might have come from"

nathantech200522:04:35

wow… I want to share my favorite vim command, ever… for alphabetize sorting… https://thoughtbot.com/blog/sort-lines-alphabetically-in-vim

nathantech200522:04:27

I just alphabetized my css/sass imports in 2 commands

markx23:04:24

Hi, how do I kill an blocking loop in lein repl? ctrl-c doesn’t work for me.

markx23:04:06

a loop like (repeatedly read-line)