Fork me on GitHub
#beginners
<
2019-04-17
>
flefik09: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.

Joel Holmes13: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)

mloughlin14: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 ?

Joel Holmes14:04:39

I have, but let me try it again

Joel Holmes14:04:52

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

Joel Holmes14:04:24

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

mloughlin14:04:28

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

Joel Holmes14: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)

Joel Holmes14:04:05

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

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

mloughlin15:04:27

yes, that stops the form from being evaluated

Joel Holmes15:04:22

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

Joel Holmes15: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

Joel Holmes15:04:08

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

Joel Holmes15:04:39

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

Joel Holmes15:04:34

.... or can I use eval?

Joel Holmes15: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 🙂

Joel Holmes15:04:01

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

Joel Holmes15:04:22

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

mloughlin11: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

mloughlin12: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

adam18: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

adam19:04:20

Ok thanks.

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"

adam19: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 😄

Alex Miller (Clojure team)19:04:21

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

Alex Miller (Clojure team)19: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/

Alex Miller (Clojure team)19: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")

adam20: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

johnj20:04:39

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

johnj20: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

👍 4
johnj21: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.

johnj21:04:30

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

johnj21: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

johnj21:04:09

yeah, seems like "premature orgnization" 🙂

johnj21: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

👍 4
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

johnj21: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

johnj21: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

johnj22: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...

hipster coder22:04:50

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

hipster coder22:04:08

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

johnj22:04:08

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

hipster coder22:04:16

ahh, similar to grep?

johnj23:04:33

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

johnj22: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)

johnj23: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

johnj00:04:00

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

johnj00:04:28

connection is a function in dev.clj

johnj01: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"

hipster coder22:04:35

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

hipster coder22: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)