Fork me on GitHub

how do i make the api of a namespace directly accessible through another namespace?


@veix.q5 There are various hacks and "here be dragons" libraries for doing that, but it's not generally considered good practice (if I'm understanding what you're asking).


I think you're asking if there's a "standard" way for a namespace to expose as (part of) its API some or all of the public functions from some other namespace (presumably that uses)...?


If I understand correctly, maybe Potemkin's import-vars would work for you.

✔️ 4

That's definitely in the "here be dragons" category 🙂


@seancorfield that's exactly what I meant


so you mean I should avoid this altogether? It sure feels dangerous and hacky... I have two databases with the same interface as candidates, and I need to hide this choice for the time being


I've only ever seen import-vars used in cases where someone just wanted to break a file up into different files due to size/complexity - but not to differentiate between different code paths. IMO it's fine to use it for code organization, but only if the functions being defined in the group of files are only accessed from the one file (where import-vars includes them).


I'm curious what you mean by the same interface? They have the same functions available to them? Do the functions behave differently behind the scenes?


datomic | datahike


the functions look the same


@veix.q5 Sounds like you might want to define a protocol and have one or more implementations of it -- that would be a non-hacky way of doing this.

👍 4

huh! :thinking_face:


has anyone managed to use Apache CXF to setup a SOAP service with Clojure before ? ( I feel ashamed to asked about SOAP :-s )


(defn foo [s] (-> s (clojure.string/trim) (fn [s] [s]))) throws a spec error, but I have no idea why


(defn foo [s] (->> s (clojure.string/trim) (fn [s] [s]))) does not


(repeated s isn’t the issue)


(defn foo [s] (-> s (clojure.string/trim) (fn [a] [a]))) also blows up


you should macro expand the -> and ->> forms


particularly, the question is: if (-> a (b)) = (a b) then (-> a (fn [])) = ?


(-> a (b)) should be the same as (b a)


(defn a [s] 
  (-> s
      (fn [other] other)))


sorry, it's 3:40am, yes (b a)


go to bed 😉


my problem can wait!


thank you though


so then what is (-> a (fn []))


macroexpanded, not evaluated


that’s not what im doing in this case though


It’s not an empty fn


doesn't matter


I’m wrapping the argument in a vector and returning


that's not the problem, the empty fn is the minimal case


also booms


run (macroexpand '(-> a (fn [])))


(defn a [s] 
  (-> s
      (fn [_])))


with macroexpand on the defn


spec error


the threading form on its own seems ok


also boom


look at the macro expansion for the form I gave you, and note where the a ends up


I don't understand why the (reduce concat ...) can't apply on doseq results?


-> and ->> are very simple syntactic transformations that don't understand semantics

👍 4

doseq by definition always returns nil


it’s for side effectful iteration, @stardiviner


thanks for helping @hiredman


@stardiviner switch doseq to for


and you don't need the outer reduce concat either you can do it inline in for


(for [n (range ..) el (map .. ..)] el)


Hey where I can find some beginner friendly material about reagent/cljs?


@souenzzo the reagent tutorial is great if you haven't go through it yet :


Hey everyone, I've got some questions about clojurescript. I've been a clojure hobbyist for about 3 years now, but I've never had a chance to dive in. Now, I've got a possible opportunity coming up at work to start a project from scratch, and I'd really like to use clojure(script). What with jvm start up times, not knowing java, and having a strong background in javascript, I'm leaning toward going no-jvm if possible. I've been fiddling with the clojurescript compiler and lumo this morning, and (I hate to say it), I'm as frustrated as I have always been with clojure, which is to say, "somewhat". I really want to make this work, but documentation and blog posts on various topics are really sparse (coming from python/javascript) so when rough edges and magic crop up, I pretty much have to go straight to the source. What I'm dealing with right now is *command-line-args* working properly (prints [x]) when I run lumo src/cljs_playground/core.cljs x, but showing nil when I run lumo -c src build.cljs && node src/main.js x. What's the reason for this? How can I get it to work? I'm able to get arguments using process.argv, but they show up inconsistently between the two (the javascript file is argument #1 post-build). I'll attach my core.cljs and build.cljs.


Hold on, just discovered things...


Ok sorry, cljs-playground/core.cljs:

(ns cljs-playground.core
  (:require [cljs.nodejs :refer [process]]))


(println cljs.core/*command-line-args*)
(println (into [] (drop 2 (.-argv process))))


lumo is useful, but the jvm implementation of the cljs compiler is definitely the standard. If you are using lumo remove java from your build toolchain, be sure that this is actually the expedient choice (java to build and pure js on deploy sometimes makes sense), and if it is, you'll likely find interop to node will be more reliable than cljs constructs


and build.cljs:

(require '[ :as b])

(b/build "src"
  {:main 'cljs-playground.core
   :output-to "out/main.js"
   :output-dir "out"
   :optimizations :advanced
   :target :nodejs
   :infer-externs true})


Hey @noisesmith, the main use case that I struggle with with the jvm is short-lived processes like command-line scripts. I know specific tools exist for them, but I was thinking if I could use cljs everywhere, I'd just learn one toolchain (based on something I know well), with a fast start-up time. Is there a story for developing command line applications on the jvm? My underlying frustration here has to do with the unix philosophy; clojure/the jvm don't seem to compose well in the small (though they're great in the large).


I'm not talking about using java to run your tool, I'm talking about using it to compile your tool


the java cljs compiler is the one that is officially maintained


Sure, so lumo to run, clojurescript compiler to build, node to run?


I had the same problems with the cljs compiler re: *command-line-args*


you wouldn't even need lumo, you get a js file and node can run it


What would you do for a fast development feedback loop?


Do note that shadow-cljs is probably the “easy” tool that might be helpful to your use case. It supports Node scripts as first class and also nice npm integration. See #shadow-cljs for more.


I usually do something like find src | entr python


with a browser, figwheel gives a great incremental compilation / live reload experience, I'm not sure about anything that good with node though


and yeah, that would be an advantage for lumo


ok, thanks


figwheel can give you a cljs repl with fast feedback and auto-loading


so that might be your best bet


figwheel looks very promising because it is invoked almost (or the same?) as the regular cljs.main entry point


in my experience live dev with figwheel is much nicer than with lumo


figwheel main that is. if i were to start something completely new i would have ways to run my app with figwheel main and with cljs.main


(but I only used it with a browser)


I've used figwheel for browser development before, and it was nice, although still magic to me so I struggled


Any existing full-stack cljs projects I could look at as an example?


#figwheel-main is very helpful for when it gets to magicky. and somehow bruce is able to develop so much and also help out seemingly everyone with questions


Any idea what's going on with *command-line-args*? They're nil even if I use cljs compiler:

  (:require [ :as b]))

(defn -main [& args]
  (b/build "src"
    {:main 'cljs-playground.core
     :output-to "out/main.js"
     :output-dir "out"
     :optimizations :none
     :target :nodejs
     :infer-externs true}))


Building with clj -m


I don't really use clojurescript, but looking at the code it looks like command-line-args is only set via the code that launches a clojurescript repl


Oh, interesting


Well dang I guess I'm stuck with process.argv then?


Broader question (and I want to be clear that I'm not complaining, no one who maintains free software needs that): does this strike anyone else as broken? I would expect very little difference between two ways of running the same program, with the same tool, with the same config. The clojure ecosystem seems like it's full of this sort of thing. Is it just because it's a less mature ecosystem than javascript and python? Because there are so many smart people who work on/with clojure that this kind of thing has to be solved, and I must be missing something. Or is everyone so much smarter than me that I'm just left in the dust? Or maybe I've just forgotten what it feels like to learn something new.


there are multiple compromises made where clojure / clojurescript chooses power and access to the lower layer it's implemented on over intuitive behavior


My guess is that *command-line-args* is/was only meant at the time as a convenience for people using the REPL. Any other program can use the standard process.argv functinality.


I dunno, I like clojure a lot, but I am not very fond of clojurescript, in part because it seems like so many compromising decisions have been made there


I've been using clojure since before there was any language specific tooling for it (a coworker at my first clojure job start lein after he got frustrated with the maven multi-module build we had), and tooling in other languages (pip, npm) drives me nuts


I guess simple/easy is a real tradeoff


I've been debating whether to do this new project in elm, which is firmly in the easy camp (as far as build tooling goes)


Elm is nice, but it's browser-only, right? there's nothing for Node as far as i'm aware


Yeah, exactly, I lose the code-sharing aspect, which is something I value a lot with node/clojure


Elm also doesn't have proper interop with JS, which is a shame


on the other hand, PureScript and ClojureScript take interop seriously


I actually like elm's story for interop. You always have a very strong idea of which language you're working in, and you can freely use the proper idioms depending on where you are. The functional/immutable world of elm is so different from javascript's mutable/procedural conventions that it doesn't make sense to mash them together. Clojure obviously takes a different tack, which suits it equally well.


i was under the impression you could only interop with a limited subset of approved packages, which sort of defeats the purpose imo


Last I used it (about 2 years ago), that was the case for elm dependencies, but once you use a port to escape out to javascript, you're in weakly-typed mutable javascript land, complete with script tags etc. I had elm working well together with commonjs modules. If you're interested, my repo is here:


ports do make sense from the purely-functional perspective i suppose, but i've never seen that as worthwhile, evidenced by my use of Clojure :p


for what it's worth, i only see Lein as magical. Boot and tools.deps are oriented around using Clojure to do your builds, which is my preference


It definitely seems like tools.deps is the way to go.


it's pretty great


I think Elm's "port" approach to interop is very cool. It took a long time to evolve (I was part of the Elm community for quite a while during its early days -- I think Evan has done an incredible job with both Elm and its community and overall mindset!).


Also, I'll +1 @U61HA86AG’s comment about Boot/clj vs Leiningen in terms of "magic" 🙂


oh yeah, for sure. Elm is a very well-designed language - i agree with many of Evan's choices in regards to it, just not all of them :~)


Thought I'd give clojure a real go of it


@hiredman do you think lein/boot/deps/cli are generally better than pip/npm or is it a familiarity thing? pipenv/pyenv are pretty ok, and I'm used to npm (webpack is the devil). Clojure tooling feels like a never-ending rabbit hole of magic to me, even though it has a lot of the positive characteristics of pip with virtual environments


Also, is deps/cli too bleeding edge to use on a new project? Should I stick with lein/boot? Any recommendations there?

Livnoor Brar18:09:02

apologies in advance.. This is out of context for the current discussion but a quick question... Has anyone tried reitit? It is a router library. ( .. need some help in request-coercion.


@UC58KQH9A maybe try the #reitit channel?

Livnoor Brar09:09:01

Sure.. thanks a lot @U61HA86AG


forget about lein/boot and just use tools.deps IMO


it doesn't matter, flip a 3 sided coin


I have some ancient version of lein on my laptop which still works great so I've never updated it


at work we use a pretty complicated boot build, but there is work underway to replace that with something clj based that is hopefully simpler


Great, thanks everyone. I think that's all my questions for now, I'll give tools.deps a shot and see where that goes

Denis G18:09:45

How can I efficiently take x numbers from lazy sequence without dropping them from very beginning. Meaning. e.g. Query is, get 5th and 10th fibonacci number. While taking 10th number, I don’t want to recompute the 5 fibonacci terms, since I already have computed them, because I wanted to get the 5th term. Any solutions for this?


When you drop, you are making a new sequence. Just keep around the old sequence and you won't recompute anything.


lazy seqs are caching


or memoized, or however you want to call it

Denis G18:09:06

so in my case I need to make (drop 4 lazy-s) then (take lazy-s), then (drop 5 (drop 4 lazy-s)), then take 1 again, right?


if you have a lazy seq of fibs, and want the 10th, why not use nth?


(nth fibs 10)

Denis G18:09:16

if n is 1000

Denis G18:09:17

like query

Denis G18:09:21

1000 and 2000

Denis G18:09:27

why to recompute first 1000 terms


it will only compute them if they haven't already been computed


lazy-seqs compute the value the first time they are realized


generally lazy-seqs of fibs are not smart


because of the caching

Denis G18:09:47

so if I define a lazy-fib as a func and call two times nth it should recompute the vals again


you will retain in memory every fib


if you don't build your fib lazy seq in such a way that you use the previous entries to compute the next, then you will do duplicate work though

Denis G18:09:55

perfect. exactly what I wanted to know


There's also a closed form to compute Fibonacci numbers as well. Not sure if helpful here though

Denis G18:09:27

It’s again me solving some random hackerrank challenge.;playlist_slugs%5B%5D=interview-preparation-kit&amp;playlist_slugs%5B%5D=dynamic-programming Here the point is that they need to compute some number sequence. Given k queries. I it was just an optimization I wanted to have in mind. Having 500th element. There is no need to compute nothing at all if query is for 200th or smth


nth definitely does

Denis G18:09:52

sorry. mixed val with coll 😅 false alarm 😄

Mario C.19:09:07

I am using Boot to build a project. And in the from-lein task in the build.boot file there are commands to set environment variables. (set-env! :key "example value"). Are these environment variables only pertaining to the build process or are these also the application environment variables?

Mario C.19:09:36

Because in the dev box it doesn't look like that is the case.

Mario C.19:09:07

The environment variables I have set in my project.clj are not being processed at all. If I (println env) (Using environ) non of those the env variables are there.

Mario C.19:09:41

I know this is a long shot but I am pretty clueless right now


Boot's set-env! affects the build process (only).


no environment variable from build time will be in the production runtime unless you are generating something that sets the environment before starting the jvm - the jvm lacks the ability to portably set env vars after startup


(I mean, if you run your code via Boot, I think it will take effect -- but that's not using a production artifact)

Mario C.19:09:22

In my project.clj I have :profiles {:dev {:env {:foo "bar"}}} and when I run my app locally via lein with-profile clj,dev run I can see the correct output of (println (get env :foo))

Mario C.19:09:56

But on the dev server those environments are not being picked up, for a lack of a better term.

Mario C.19:09:13

Where should I be looking at getting those environment variables set via the project.clj file?


project.clj is a build tooling config, it's not meant to control runtime behavior of an app


(you can use your build tool to run your app, but that's usually a bad idea)

Mario C.19:09:27

Where do you set your environment variables then?


the same way you would set environment for any production task - usually via the run script or daemon config


(or docker config, or tomcat container config or...)


sometimes a shell script that sets a bunch of env vars then runs java on your uberjar suffices


but most people have something more specialized, and often there's a separate ops team deciding how it needs to work

Mario C.19:09:46

Thanks @noisesmith @seancorfield, I guess I need to gain a little more understanding about all this


@mario.cordova.862 a very simple script could look like this

export FOO='foo for prod'                                                       

JVM_OPTS='jvm opts for prod'                                                    

java $JVM_OPTS -jar $JAR
- the app would be able to see whatever value for FOO the script sets here

Mario C.20:09:45

So the env variables set in project.clj is really only for local development?


Yes. Just to control the build or whatever else lein does directly for you.


For example, when you run your project via lein with-profile clj,dev run. But once you've built an uberjar, lein is not involved so any env vars set by lein won't apply.


and same goes for any other build tool - the configuration is there to make your artifact, and doesn't set the runtime config the artifact sees


(unless you are configuring something that gets baked in while compiling of course)

Mario C.20:09:17

Thanks for clearing that up guys! I see the error in my reasoning now


I have a vector that is ordered by


I am trying to filter this vector. Fore each location I want to have for each algo the algo with the highest accuracy.


Is it possible that I use a filter and the predicate of the filter internally is keeping a list location/algo combinations that were already used?


Can you do a group-by :location first, then filter each group’s array, then flatten everything?


for each group,


I really would then have to create a group of algos,


and then take the first.


and all this making sure that the sort order does not get losts.


In filtering sometimes I foudn that the order gets lost.


This solution is sort of what I would do in my old imperative style.


No, filtering will never change the order.


group-by (juxt :location :algo) to get map from [<location> <algo>] [..]?

juxt 4
👍 4

If ordering is important, I would use reduce.


Yes, group-by can certainly cause reordering. Using reduce might be a bit complicated, though.


user=> (doc group-by)
([f coll])
  Returns a map of the elements of coll keyed by the result of
  f on each element. The value at each key will be a vector of the
  corresponding elements, in the order they appeared in coll.


or my other idea was I first create a list of all location/algos,


you want two things, not one thing


Yes... but the groups themselves won't be ordered, so once you flatten...


and then I do for each of this groups a "some"


And my last idea was that I somehow process the list sequentially, and when I add a new element to the new vector, then I check if I already have a element with a prediccate


I think the problem is really simple,


for each group that is defined in a predicate,


take the first element in the list.


(map #(some algo-predicate %) (group-by group-predicate) )


But still, there are so many functions in clojure, I thought I can do it easier and more elegant.


As long as order doesn't matter, yes, that's the way to do it.


the order does matter...


@bill.kempf group-by does not change the order


From the group-by doc... it creates a map, which isn't ordered.


The items in the groups are ordered, the groups are not.


So that algo won't be ordered.


the items in each group are ordered by the order in the original input though


Yes, the items in the group are... but he's going to pull out single items from each group, and the groups are not ordered, so the end result isn't ordered.


then it seems to me that a functional approahc is more complicated than an imperative approach for this problem 😞


Harder to think about, maybe, but the final result I bet is better than the imperative approach.

Digital Baboon21:09:51

How does one get data out of a #object[Promise [object Promise]]? Into a vector, for example? Any example I've tried thus far ends with the exact same thing printed in my console 😞


is this in cljs?


in cljs a Promise is a js object representing a value that will be available later, and you can chain a callback onto it to be called when the value is available


So, you need an implementation of a group-by that produces an array-map instead of a map. Then the algo works.


@bill.kempf yes. that is a good idea!


And in the end, the functional approach beats the imperative 😉


Actually... with this idea it should be fairly easy to implement this with reduce.


I've not looked at how group-by is implemented in closure, but it's easy enough to implement it using reduce. You could combine some steps so you don't need to implement a "stable-group-by".


in clojure, reduce is the idiomatic way to do an imperative algorithm that consumes a collection in order while building arbitrary results


Yes, and reduce can be used to produce another list, and thus can be used to implement filter. Or it can produce a map, and thus can be used to implement group-by.


It's all the sequence monad. Everything can be built from the bind function out.


thatnks guys! I will read some tutorials on advanced reduce use cases...

Denis G21:09:40

how to make a version of conj, that returns coll if val is nil, else conj, but without if else, like with using of fnil, but dunno how


@hoertlehner (source group-by) gives us the implementation of group-by

(defn group-by 
  "Returns a map of the elements of coll keyed by the result of
  f on each element. The value at each key will be a vector of the
  corresponding elements, in the order they appeared in coll."
  {:added "1.2"
   :static true}
  [f coll]  
    (fn [ret x]
      (let [k (f x)]
        (assoc! ret k (conj (get ret k []) x))))
    (transient {}) coll)))
Replacing the {} with (array-map) should do the expected. So ordered-group-by would be
(defn ordered-group-by 
  "Returns an array-map of the elements of coll keyed by the result of
  f on each element. The value at each key will be a vector of the
  corresponding elements, in the order they appeared in coll."
  [f coll]
    (fn [ret x]
      (let [k (f x)]
        (assoc! ret k (conj (get ret k []) x))))
    (transient (array-map)) coll)))
Assuming your original vector is v, the solution now becomes
(->> v
  (ordered-group-by (juxt :location :algo))
  (mapv (fn [[[location algo] trials]]
          (assoc (apply max-key :accuracy trials)
            :location location
            :algo algo))))


@hoertlehner Nevermind here is a counterexample to my solution

(def v [{:location "a" :algo "lqs1" :algo-params { :x 34 :y 34} :accuracy 1 :comment "remove this"}
   {:location "a" :algo "nn1"  :algo-params  { :x 34 :y 34} :accuracy 3}
   {:location "b" :algo "lqs1" :algo-params { :x 34 :y 34} :accuracy 5}
   {:location "a" :algo "lqs1" :algo-params { :x 4 :y 20} :accuracy 999}
   {:location "a" :algo "nn1" :algo-params { :x 34 :y 34} :accuracy 1 :comment "remove this"}
   {:location "b" :algo "nn1"  :algo-params  { :x 34 :y 34} :accuracy 3}
   {:location "b" :algo "lqs1" :algo-params { :x 4 :y 20} :accuracy 2 :comment "remove this"}
   {:location "b" :algo "nn1" :algo-params { :x 34 :y 34} :accuracy 1 :comment "remove this"}
You can assoc in the index of each record into it, then use group-by then get the max values with max-key, sort-by the index then dissoc the index out of all the records.


This is what I ended up with:

(->> v
  (map-indexed #(assoc %2 :index %1))
  (group-by (juxt :location :algo))
  (mapv (fn [[[location algo] trials]]
          (assoc (apply max-key :accuracy trials)
            :location location
            :algo algo)))
  (sort-by :index)
  (mapv #(dissoc % :index)))
3:45 AM here. Time to sleep 😅


beautiful solution! many thanks!!!