Fork me on GitHub
#clojure
<
2017-01-06
>
sophiago00:01:37

how exactly does for work on nested lists? the language in the docs is not so clear

bfabry00:01:10

nested lists assigned to one of its bindings?

sophiago00:01:54

tbh i'm not sure... i'm trying to create several sets of permutations of nested lists

sophiago00:01:18

here's an example:

bfabry00:01:20

do you know python? it's really really similar to list comprehensions

sophiago00:01:51

no, but i've used them in haskell a bit

sophiago00:01:18

for some reason made it this far in clojure without realizing that's what for was

bfabry00:01:36

oy... that's reaching back too far for me sorry, I haven't done them in haskell in like 6 years

sophiago00:01:44

i think it's about the same

sophiago00:01:16

except for the way bindings work

bfabry00:01:18

cool cool. and yeah I think it's non-obvious what for is for, and it's a lot more powerful than you would expect

sophiago00:01:40

right. i was originally trying map, but i would have needed several passes through the function to build up long one list per element in each nested list

bfabry00:01:40

not sure which doco you're looking at, but clojuredocs has a lot more examples https://clojuredocs.org/clojure.core/for

joshjones00:01:03

it’s pretty straightforward: each element of the list, even if it’s a list, is bound to the variable you name, and it’s substituted in the body as such

sophiago00:01:14

i tried building this up from simpler parts and was confused by this: (for [x '((1 4 3) (8 2 1) (1 0 4) (5 1 0)) v (range (count x))] v) returning (0 1 2 0 1 2 0 1 2 0 1 2)

bfabry00:01:18

sorry I'm maths-stupid. I need a more basic example to help

bfabry00:01:31

ah, well that's simple enough. this explanation should help

bfabry00:01:35

(for [x '((1 4 3) (8 2 1) (1 0 4) (5 1 0)) v (range (count x))] {:x x :v v})
=>
({:x (1 4 3), :v 0}
 {:x (1 4 3), :v 1}
 {:x (1 4 3), :v 2}
 {:x (8 2 1), :v 0}
 {:x (8 2 1), :v 1}
 {:x (8 2 1), :v 2}
 {:x (1 0 4), :v 0}
 {:x (1 0 4), :v 1}
 {:x (1 0 4), :v 2}
 {:x (5 1 0), :v 0}
 {:x (5 1 0), :v 1}
 {:x (5 1 0), :v 2})

sophiago00:01:38

so in that simple case it seems to be counting the list of length 4 as 3 and then iterating through it four times

joshjones00:01:37

every time through x (that’s 4 times), v is given, and v is defined at the beginning to be: (range 4)

bfabry00:01:49

so it takes the two lists you bound to x and v, and combines their values in all possible permutations

joshjones00:01:07

rather, (range (count x)) where every time (count x) is 3, since each element in x is 3 elements long

sophiago00:01:15

@joshjones then shouldn't it be (0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3)?

joshjones00:01:24

yeah see last comment sorry

joshjones00:01:52

the first iteration you have: (range (count [1 4 3]))

joshjones00:01:06

next you have (range (count [8 2 1]))

sophiago00:01:09

ok, so that's what's really counterintuitive to me

bfabry00:01:17

honestly having the list bindings reference each other like that is super confusing. I didn't even know that worked

joshjones00:01:36

though it’s not a for loop as in the for (int i = 0; i .... sense, it still behaves the same way:

(for [x [a b]
      y [c d]]
  (* x y))
; yields [(* a c) (* a d) (* b c) (* b d)]

bfabry00:01:01

it makes sense, because it's like nested old for loops, but yeah

joshjones00:01:11

x iterates more slowly than y

sophiago00:01:28

ok so... count will return the number of elements in each nested list, but then functions like first and nth still work as expected in the :let part?

bfabry00:01:40

(for [x [1 2 3] y (range x)] x y) ==
for(x : [1 2 3]) {
  for(y : range(x)) {
    result.push(x,y);
  }
}

sophiago00:01:41

this is what's really tripping me up

joshjones00:01:12

:let simply creates a binding for any particular value of (in above example) x and y on that “iteration"

sophiago00:01:24

perhaps what i want is to use a map inside the :let? so that way it will map the bindings through the sublists?

sophiago00:01:43

does what i'm saying make sense?

bfabry00:01:24

I'm going to try and translate this to not use the for

joshjones00:01:58

gotta run, will try to look later

sophiago00:01:00

well my other thought was to use a closure and mutate the list...but then i'd need to create copies of it, which seems silly

gfredericks00:01:45

@sophiago any for expression should have a direct translation into haskell do syntax for the list monad

wei00:01:59

what is get doing if you pass in a list? seems like the output is always nil

wei00:01:24

why is that preferable to throwing an error?

bfabry00:01:14

my psuedocode translation

(for [x (first s)]
  (for [v (drop 1 (range (+ (count s) 1))]
       (let [y (if (not= (nth x v) 0)
                 (list (* (first x)
                        (nth x v))
                   (drop-last v (next x))
                   (- (nth x v) 1)
                   (nthnext x v)))]
         (if (< v (count s))
           (.add result y)
           STOP
           ))

gfredericks00:01:48

@wei no reason that I know of

bfabry00:01:38

maybe it will be an error when get has a spec. ie does not match predicate associative? or whatever

bfabry00:01:16

(associative? [])
=> true
(associative? {})
=> true
(associative? #{})
=> false
(associative? '())
=> false

wei00:01:22

how is get related to associative? get does work on sets and that makes sense to me

bfabry00:01:40

well, it returns the value mapped to a key. associative says a thing has keys associated with its values. hence you can get values from a map using the key, and values from a vector using the index

bfabry00:01:19

I didn't know you could get things from a set, that surprises me

bfabry00:01:39

I guess get is slightly more general than I thought. maybe it'll be hard to spec it

sophiago01:01:33

@bfabry it seems if i call (nth s v) then it's indexed on the entire list, but if i call (nth x v) i get an error that i'm trying to index a scalar...but i'm not using two fors in my version

bfabry01:01:12

@sophiago I was trying to translate what your for's were doing for you

sophiago01:01:29

do i really need to use two?

bfabry01:01:09

no, you definitely don't, your code above is the equivalent to having two. I was trying to explain that multiple bindings in a for is like nesting for loop's

sophiago01:01:27

ok, so this example: (for [x '((1 4 3) (8 2 1) (1 0 4) (5 1 0)) v (count x) :let [y (first x)] :while (< v (count x))] y)

bfabry01:01:52

if s looks like '((1 2 3)) then I would expect you to get that error, because (first s) returns '(1 2 3) and then x is bound to 1, then 2, then 3. except that it will error out on 1 because (nth 1 0) is nonsense

sophiago01:01:28

there's no nth in that version

sophiago01:01:48

it should just pick out the first element, right?

bfabry01:01:42

no, because you're trying to iterate over the result of (count x) which is just a number

sophiago01:01:25

because of the :while part?

bfabry01:01:40

(for [x '((1 4 3) (8 2 1) (1 0 4) (5 1 0)) v (count x) :let [y (first x)] :while (< v (count x))] y)
      
for(x : '((1 4 3) (8 2 1) (1 0 4) (5 1 0))) {
    for(v : x.count()) {
      y = x.first();
      if (v < x.count())
        result.add(y)
      else
        STOP

bfabry01:01:47

it comes out something like that

bfabry01:01:07

for(v : x.count()) doesn't make sense, so blows up

sophiago01:01:23

i'm not sure what language your pseudocode is supposed to approximate, but fwiw it's not one that really helps me

bfabry01:01:54

do you know ruby or python?

sophiago01:01:37

i guess i should clarify what :while does? you're saying it defines what the :let binding iterates over? i assumed it was the condition for the loop to stop...

bfabry01:01:39

(doseq [x '((1 4 3) (8 2 1) (1 0 4) (5 1 0))]
  (doseq [v (count x)]
    (let [y (first x)]
      (if (< v (count x))
        (.add result y)
        STOP))))

bfabry01:01:42

how about htat?

sophiago01:01:06

ah wait, i see i just left out the range

sophiago01:01:22

ok, so it's returning (1 1 1 8 8 8 1 1 1 5 5 5)

bfabry01:01:34

ok, so if you put the range back where I think you're going to, it looks like

(doseq [x '((1 4 3) (8 2 1) (1 0 4) (5 1 0))]
  (doseq [v (range (count x))]
    (let [y (first x)]
      (if (< v (count x))
        (.add result y)
        STOP))))

sophiago01:01:38

what if i want it to only return the first element once for each

gfredericks01:01:58

so you want 4 results?

gfredericks01:01:03

why isn't it just (for [x '(...)] (first x))?

gfredericks01:01:50

(or (map first '(...)) for that matter)

sophiago01:01:40

ok, but then what if i actually want to be able to refer to the index of what it's iterating in my :let binding? that's why i had the v part

deactivateduser01:01:26

map-index might help?

gfredericks01:01:29

to do what with it?

deactivateduser01:01:42

Sorry, map-indexed. #n00bwarning

deactivateduser01:01:55

user=> (find-doc "map-index")
-------------------------
clojure.core/map-indexed
([f] [f coll])
  Returns a lazy sequence consisting of the result of applying f to 0
  and the first item of coll, followed by applying f to 1 and the second
  item in coll, etc, until coll is exhausted. Thus function f should
  accept 2 arguments, index and item. Returns a stateful transducer when
  no collection is provided.
nil

sophiago01:01:51

so clearly i don't want that v binding there...but then how do i reference the index?

sophiago01:01:04

and map-indexed does not do this, fyi

gfredericks01:01:03

partial derivative with respect to x? so the third component stays the same?

sophiago01:01:34

no it's a linear map

sophiago01:01:48

that's exactly what the output should be

sophiago01:01:03

one list of the x derivative and one of the y

gfredericks01:01:14

each element in your top-level list is processed independently, right?

gfredericks01:01:46

if so, this conversation would be easier if we write a function to do just one element, and then we can translate it back to a for

sophiago01:01:01

what do you mean?

gfredericks01:01:17

(defn deriv [s] (map deriv-of-one-thing s))

gfredericks01:01:33

it looks like you also need the count

sophiago01:01:45

that's what i've been saying this whole time...

gfredericks01:01:55

so you could (defn deriv [s] (let [c (count s)] (map #(deriv-of-one-thing c %) s)))

gfredericks01:01:06

sorry I haven't been following the whole conversation closely

sophiago01:01:09

what do you mean by "thing"

gfredericks01:01:18

one element of s

sophiago01:01:58

ok, but then they still need to be built up into lists of the actual derivatives

sophiago01:01:28

so you then will have two triples from that function...

gfredericks01:01:47

your output in your example has only 2 elements

gfredericks01:01:59

I guess I missed that since you said earlier you wanted a list of 4 things

gfredericks01:01:09

map doesn't work then

sophiago01:01:25

that's why i tried using list comprehensions...

sophiago01:01:14

but apparently list comprehensions are actually worse than map in that they don't let you even refer to the index?

sophiago01:01:24

at least with map i can take a partial derivative

gfredericks01:01:51

what do the three elements of ((4 3 3) (16 1 1) (5 0 0)) mean?

sophiago01:01:19

that's the partial derivative with respect to x

gfredericks01:01:28

okay and the other half is w.r.t. y?

sophiago01:01:30

in the equation given in the snippet

gfredericks01:01:48

and you want a function for just x/y exprs or for arbitrary number of variables?

gfredericks01:01:20

okay; so if s has lists of N elements, the output has N-1 elements in it?

sophiago01:01:50

i need to apply the if statement in my snippet

sophiago01:01:28

that's the code that will do it...given a list x and index of sublists v

gfredericks01:01:35

the if statement determines how many variables you're making partial derivatives of?

sophiago01:01:33

no...you'll see it takes arguments x and v

sophiago01:01:53

corresponding to the sublist and index while iterating through the sublist

gfredericks01:01:07

I'm just trying to figure out how many elements should be in the output list

sophiago01:01:23

there's no fixed amount...

gfredericks01:01:25

Your example has two because there are two variables

gfredericks01:01:37

If there are three variables it should have three?

sophiago01:01:50

that's not how calculus works

gfredericks01:01:21

I have to put my kids to bed. I'll be back in a bit.

deactivateduser01:01:17

While we take a pause from calculus, I have a potentially dumb question (I'm a n00b, so looking at the source didn't enlighten me) - does clojure.math.combinatorics/combinations guarantee that the results will be returned in the same order as the input? For example, (clojure.math.combinatorics/combinations [:a :b :c :d] 2) produces ((:a :b) (:a :c) (:a :d) (:b :c) (:b :d) (:c :d)), which is ordered as per the input (i.e. it pairs up :a with the other elements first, then :b, then :c), but is that a guarantee or merely a coincidence?

seancorfield01:01:37

That would be a question for @puzzler I think.

seancorfield01:01:49

(he maintains that library)

qqq01:01:53

is this #clojure or #applied-math 🙂

gfredericks01:01:16

I used vectors instead of lists because it made the code a bit easier

adambros01:01:20

@deactivateduser10790 seems to hold true:

(let [position (fn [coll x] (.indexOf coll x))
      LEN 2]
  (tc/quick-check 200 
    (prop/for-all [v (gen/such-that #(<= LEN (count %)) (gen/vector-distinct gen/int))] 
      (every? #(apply < (map (partial position v) %)) 
        (comb/combinations v 2)))))
;=> {:result true, :num-tests 200, :seed 1483667711921}

deactivateduser02:01:18

@adambros Cool! Yeah it seems to in the current version - I guess my question is more about whether that behaviour is guaranteed over time (i.e. in new versions). #paranoidn00b

adambros02:01:44

you could leave that in your tests, and then it’ll then fail if that ever stops being true

sophiago02:01:01

@gfredericks i need lists though... i was just trying nested maps, but didn't get it quite right

deactivateduser02:01:09

What are "tests"? 😜

gfredericks02:01:31

@sophiago what do you need lists for?

deactivateduser02:01:47

But thanks @adambros - that's helpful!

sophiago02:01:47

well they have to be lazy

adambros02:01:03

np, was fun to play with boot & test.check

sophiago02:01:37

plus i have about 1000 lines of code using lists

gfredericks02:01:51

I was only using vectors at the lowest expr level, so if you don't need laziness there then it would be fine

sophiago02:01:04

you don't need vectors. you can just use list and flatten

sophiago02:01:33

sorry, just still picking apart how this works 😄

sophiago02:01:01

first of all, i sort of figured i was going to have to switch to using rest

sophiago02:01:14

otherwise i would have gotten nil thrown in there right?

gfredericks02:01:26

Just extra numbers I think

gfredericks02:01:32

Easy to experiment

sophiago02:01:53

you were just way more clever on lines 7 and 9

gfredericks02:01:34

That's why I like vectors for this kind of work. No need for all that bookkeeping.

sophiago02:01:57

but your code works the same with lists really

sophiago02:01:35

it's more you used laziness and i couldn't think of anything better than that clunky nth and nthnext

sophiago02:01:28

oh i was a little brusk there...i was considering using vectors earlier in case i could swap out parts of this code for the reducers library

sophiago02:01:45

but hard to tell if that would make sense. doesn't seem like a lot of people use it

sophiago02:01:57

and it would only work on certain functions

sophiago02:01:09

the deal with this is i have a series composition function (which i also still need to expand to the multivariate case) and then i map these linear maps with that, in other words the chain rule

sophiago02:01:46

so it creates essentially what's like maclaurin series in vector space

sophiago02:01:10

that wouldn't work with reducers because it's not associative

sophiago02:01:08

but series multiplication (cauchy products) is just convolution so that should

sophiago02:01:16

and addition

sophiago02:01:46

oh, actually maybe not...

sophiago02:01:53

no, i think parallelism is out for anything meaningful now that i think about it. a little fuzzy by this hour tho...

qqq02:01:04

I'm having trouble finding this on Google. Is there a nice Clojure DSL for generating Python code? I'm a big fan of the Tensorflow / Numpy libraries, but I really don't like Python as a language. Ideally, I'd write my math in a Clojure DSL, have it compile down to Python numpy/tensorflow -- and run that. Unfortunately, I can't seem to bind any libraries for generating Python from Clojure. Note: I don't have to compile all of Clojure. "Embedded Clojure DSL => Python" is perfectly fine. I just need something that outputs valid Python, along with all its whitespace indentation fun.

joshjones02:01:07

@sophiago glad you guys worked it out -- in general, ditch lists unless you need them for laziness. vectors are more idiomatic for data representation and have some advantages over lists

sophiago03:01:36

@joshjones yeah i go back and forth often because i do a lot of stream processing, which is the basis for this entire technique really, but oftentimes vectors are much easier to reason about

sophiago03:01:25

ideally i'd like to have both a lazy sequential version and a strict parallel version of this if possible, but not sure how to make the latter work. and at the moment i just need to focus on getting an implementation together rather than adding more features 😛

sophiago03:01:20

and thanks again @gfredericks! i'd love to know what you think of this library when i have to it together. seems like something you might be into? it's an interesting technique for computing partial differential equations i picked up from the haskell folks (although pretty sure the clojure version will be faster...) and i'm interested in how people will use it who have more domain knowledge than i do.

madstap03:01:57

@qqq Is this what you're looking for? Not actually clojure, but close, and compiles to python. https://github.com/hylang/hy

qqq03:01:40

@madstap: good try; but I 'm realyl hopping for clojure dsl -> py, so I can write things like:

(let
  [a ...
  b ...
  c (+ (* a b) + 1)]) ... 
and have it compile down to numpy/tesorflow matrix ops

qqq03:01:18

no #@$#@ way; how was I not aware of a java interface?

henrique.alves03:01:47

unfortunately, java API is experimental... when Google first announced TF they touted multiplataform support a lot, but apparently it feel thru the cracks

qqq03:01:44

it is very multiplatform for: 1) evaluating tensorflow models 2) hardware you can run tensorflow with ... just not so much so with the languages you can train with

dhruv103:01:14

a noob question i posted this as a question on the plumatic/schema github page. any help would be much appreciated https://github.com/plumatic/schema/issues/378

henrique.alves03:01:32

@qqq the first announcement stressed that they wanted to support other languages. the Java API is mentioned on the official page, but we're 1 year in and it's still experimental

henrique.alves03:01:08

adoption was quick so it seems they settled on Python as good enough

qqq03:01:26

(tensorflow conversation moved to #off-topic)

sophiago03:01:07

@hcarvalhoaves i'm working on an automatic differentiation library/dsl rn!

henrique.alves03:01:51

@sophiago 🆒 I'm interested on this topic myself, lets chat in off

jimmy04:01:41

hi guys, is there any good google datastore for clojure ?

puzzler06:01:54

@deactivateduser10790 The ordering of the combinatorics functions such as combinations are guaranteed to remain the same in the future (i.e., I wouldn't accept a patch that was faster but produced the combinations in a different order), BUT before you rely on order make sure you understand the special handling of inputs that have duplicates. When the input has duplicate items, they are grouped together in the order in which they are initially seen from left to right before proceeding with the algorithm, and the algorithm takes duplicates into account so the pattern will be different than if the items were distinct. So (combinations [:a :b :a :c :b] 2) becomes (combinations [:a :a :b :b :c]) which yields ((:a :a) (:a :b) (:a :c) (:b :b) (:b :c)).

qqq08:01:56

when using cljs for spa on client, clj on server; what is a nice way to structure the app so that the server/client code lies in the same directory

qqq08:01:19

so a typical app would have multiple parts, and I'd like, for each part, to have client/server grouped together

qqq08:01:28

lein ring uberwar // where in project.clj do I put the ":omit-source true" ?

qqq08:01:50

false alarm, :omit-source true is working

dottedmag11:01:21

But I can't find it anymore, can anyone remember what was it?

robert-stuttaford11:01:36

probably the recent NYC talk, a vimeo link

dottedmag11:01:12

Thanks. I'm in the middle of it right now, and it does not really ring the bells, but maybe I need to finish it first :)

tbaldridge12:01:18

@dottedmag: pretty sure that was Stu Halloway's talk

karol.adamiec12:01:27

@dottedmag ah, then share a link! 🙂

dottedmag12:01:56

My personal story: I liked Clojure as an idea, but lack of something like spec kept my internal angst saying again and again "you can't do any large systems in it". Then I stumbled upon this talk accidentally, which dissipated this anxiety.

pupeno12:01:07

A bit off topic. Anybody very familiar with ssl? I seem to be getting rst from the server. Is this normal?

carocad12:01:00

question on specs: if you do lein test do the instrumented functions get tested automatically?

tbaldridge12:01:42

@carocad no, only the ones that you put in deftests

carocad13:01:50

@tbaldridge so you mean doing something like this:

(deftest foo (is (:result (:clojure.spec.test.check/ret (first (stest/check `hypobus.conjectures.core/hypothize))))

zane15:01:49

@carocad, there's a pinned message in #clojure-spec that might be of interest to you.

carocad15:01:30

@zane oh nice. Thanks a lot 🙂

baptiste-from-paris16:01:12

hello guys, does anyone knows how can I check that one entry of my map is a fn with specs => e.g:

(s/keys :req-un [::id ::fn])

baptiste-from-paris16:01:42

I don’t want to check args of the fn or :ret but only if it’s a function

baptiste-from-paris16:01:08

I can use`ifn?` but maps also implement it

tbaldridge16:01:05

well a map is a function, so why does it matter if it's a map or not?

baptiste-from-paris16:01:47

it does not indeed

borkdude17:01:17

Repeating this question from yesterday: How do I get rid of a reflection warning in a gen-class when calling the superclass’s method which has been renamed locally by exposes-methods? Example: https://www.dropbox.com/s/59bkl4m9pvi8xni/Screenshot%202017-01-05%2022.44.38.png?dl=0 Should I just ignore it and trust there is no reflection when it’s executed, since I type hinted?

reborg17:01:27

borkdude should the type hints be in the params instead of the method call?

borkdude17:01:14

@reborg the params are of a method of a superclass

reborg17:01:07

I mean (defn -isMyType [^RSS20Parser this...

borkdude17:01:49

I’ll try if that makes a difference

borkdude17:01:25

Maybe it’s a limitation of gen-class

hiredman18:01:40

try type hinting with the full classname

hiredman18:01:14

you are type hinting with the short name, which is same between the class you are defining, and the class you are extending

hiredman18:01:41

and you are importing the class you are extending, so likely the short name is refering to that class

hiredman18:01:14

which of course doesn't have the new method, so the compiler can't find it, so reflection

borkdude19:01:34

@hiredman It doesn’t work. Still says: method parentIsMyType can’t be resolved. Which is true, because there is no such method, it’s just the locally renamed method.

hiredman19:01:25

what full class name did you type hint with?

borkdude19:01:27

Wait, it worked with the name of the class I’m defining

borkdude19:01:52

I hope that still works when I aot this… because isn’t there a bootstrapping problem here?

hiredman19:01:34

it is complicated, the compiler in some cases does some magic to make it work

hiredman19:01:44

generates a stub class

borkdude19:01:16

thanks! 👍

hiredman19:01:19

but that is the way the expose method stuff works, it isn'tsome magic locally renamed method

hiredman19:01:32

it adds a method on your generated class

qqq19:01:10

is there a way to have a (defn-clj which expands out to #+clj (defn ?

qqq19:01:22

i.e. it's something that only expands out in clj mode

captainlexington19:01:46

What would it do in cljs mode?

qqq19:01:56

expand out to (comment)

jr19:01:38

no but you can write a macro that detects the compiler env and emits the code as necessary

qqq19:01:07

is this because reader macros expand before regular macros?

qqq19:01:04

so I copy/paste that, easy enough; thanks!

qqq20:01:05

I'm using compojure. I already have a basic example like:

(defroutes app
  (compojure.route/resources "/public")
  (GET "/" [] (redirect "/public/index.html"))
  (GET "/test" []
       (response (str "This is updated")))
  (GET "/api/qa/list-questions" []
       (println "hit /api/qa/list-questions")
       (response (str {:msg "hello world"})))
  (POST "/api/qa/new-question" req
        (println "POST /api/qa/new-question")
        (println req))
  (GET "/new/:name" [name] (write-db name))

  (route/not-found "<h1>Page not found.</h1>"))
now, what I want to say is all GET requests on '/demos/*" goes to this other defroutes handler -- how do I say that?

qqq20:01:29

so instead of one "defroutes", I'd like "defroutes app" and "defroutes demos-app", and have all of "/demos" gets routed to demos-app routes [end question]

captainlexington20:01:12

@qqq You would defroutes again in another part of code, and do all "/demos/" routes there

qqq20:01:59

@captainlexington : can you point me at an working example? I feel like if there's something I could copy, this would make alot more sense.

captainlexington20:01:13

(defroutes demo-routes
  (GET "/demos/demo-1" [] (demo-1-call)))

captainlexington20:01:36

(defroutes app
  (compojure.route/resources "/public")
  demo-routes
  (GET "/" [] (redirect "/public/index.html"))

qqq20:01:04

@captainlexington : interesting. Would it be correct to say: "You can't nest routes; but you can concat routes" ?

captainlexington20:01:12

Syntactically may not 100% correct, but that's essentially how it works

captainlexington20:01:17

Yeah, that's basically it.

qqq20:01:20

This is basically splicing demo-routes into app right? Cool; thanks!

captainlexington20:01:57

Also in usage it "feels" like nesting them. I'm pretty sure it can go arbitrarily deep

qqq20:01:24

However, even the "inner routes" are defining absolute paths, not relative paths right?

captainlexington20:01:28

But there isn't any routing syntax for nesting them. It just happens automatically when you nest defroutes

captainlexington20:01:45

Yes. There may be some way for defining sub-paths relative to some prefix, but a) I've never had occasion to use it, and b) that behavior would be independent of putting different routes collections together

hiredman20:01:15

there absolutely is

qqq20:01:42

This is going to be a 1-2 knockout with captainlexington providing the concating routes and hireman providing relative paths

hiredman20:01:43

compojure "routes" are ring handlers that return a response if they match and nil if they don't

hiredman20:01:02

(GET ...) and friends are macros that create ring handlers that do that

hiredman20:01:34

and routes or defroutes is function composition with that in mind

hiredman20:01:47

so you can stick and ring handler

hiredman20:01:15

and you can also take a ring request in handler and invoke a set of routes on it

hiredman20:01:33

(GET "/" req (some-other-routes req)) or similar

qqq20:01:59

@captainlexington @hiredman : I'm happy now. Anything else I should know before I jup back into coding?

qqq20:01:18

(ps: thanks for all the explainations)

qqq20:01:22

(defroutes gae-app
  (GET "/" [] (response "from gae demos"))
  (GET "/test" []
       (response (str "This is updated")))
  (route/not-found "<h1>Demos: Page not found.</h1>"))

(defroutes app
  (compojure.route/resources "/public")
  (GET "/" [] (redirect "/public/index.html"))
  (context "/gae/" []
           g.apps.gae-s/gae-app)
  (route/not-found "<h1>Page not found.</h1>"))
localhost:8080/gae/ <-- works fine localhost:8080/gae/test <-- "Demos: page not found." What am I doing wrong?

qqq20:01:21

resolved: the context apparently is not supposed to end with a /

captainlexington21:01:40

FWIW I would also probably put the context in gae-app rather than wrapped around it in app

captainlexington21:01:06

So you don't forget that it's there, and so gae-app works as expected even if you have to reuse it

captainlexington21:01:15

somewhere else in the code

rgorrepati21:01:50

Unhandled java.lang.NoSuchMethodError clojure.core.async$do_alts$fn__12286.<init>(Ljava/lang/Object;Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V

bostonaholic21:01:38

let’s say they’re a java library foo 0.3.0 that I want to write a clojure wrapper for. Should I start my foo-clj library at version 0.1.0 or try to mirror the java library version?

bostonaholic21:01:49

I have pros and cons of each method

jr21:01:27

don't mirror the java library version because you will end up having bug fixes of your own that will prompt a release

bostonaholic21:01:28

when foo 0.3.1 is updated, that would require me to release foo-clj 0.3.1

bostonaholic21:01:46

@jr that was exactly my biggest con

jr21:01:53

(or enhancements)

jr22:01:24

also consider that using your library should not require the user knowing that foo 0.3.1 is the exact dependency

jr22:01:33

the point of a wrapper is to abstract that info

jr22:01:23

dependency conflicts aside that is....

tolitius22:01:18

@bostonaholic: why not use the Java lib?

tolitius22:01:57

there are good reasons not to, I am just curious what yours is

bostonaholic22:01:24

we’re just writing a thin wrapper around it for convenience

bostonaholic22:01:49

and the java library makes use of internal classes when that could be hidden from the app itself

tbaldridge22:01:00

There are very few reasons to write Clojure wrappers, most of the time it's better to just use the java library itself

bostonaholic22:01:19

the benefit I see here is that a function in the java lib requires you send a Props class as an argument. When really I should just as easily send a clojure map and let the wrapp instantiate a new Props class

bostonaholic22:01:36

that’s just 1 benefit I see in this case

bostonaholic22:01:38

and remove the need for stateful (doto thing (.setFoo “foo”) (.setBar “bar”))) etc.

tbaldridge22:01:54

But how many times are you going to make those Props classes? It might be better to just have a ns in your app that handles your app specific interfaces with that library.

bostonaholic22:01:25

sure, a ns in my app or a library that all of our other apps can use as well

tbaldridge22:01:29

A doto is much less to maintain compared to a library

tbaldridge22:01:02

Well many times when writing an app, I'd have this code in a data model layer or something of that nature.

tbaldridge22:01:17

Not sure what this is interfacing with, but I'd assume its some sort of DB/queue/service/etc.

bostonaholic22:01:32

3rd party service

bostonaholic22:01:15

analytics to be specific

tbaldridge22:01:18

Sure, so put the JVM code right in your business logic interface. That way you can still write it once, and yet not end up writing an abstraction that may not save you a ton of time

bostonaholic22:01:19

we’d have to write it multiple times for different apps

tbaldridge22:01:45

What you will find is that writing a wrapper library will get you into too many situations where you'll be thinking "Eh...do I need feature X or not".

tbaldridge22:01:23

Wrappers hide information and they create impudence miss-matches. You'll be tempted to not use a feature if you have to cut a new release of your lib every time you want to enable something the wrapper hides

bostonaholic22:01:03

that makes sense

bostonaholic22:01:18

atm there isn’t plans to add more features

bostonaholic22:01:25

just make it easier to use with clojure

tbaldridge22:01:21

So that's kindof my point...you'll have to maintain an interface with a backend...just make that backend the JVM code, don't make it another backend