This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-09-26
Channels
- # 100-days-of-code (3)
- # announcements (2)
- # beginners (237)
- # bitcoin (2)
- # boot (5)
- # cider (15)
- # cljs-dev (9)
- # cljsrn (6)
- # clojure (75)
- # clojure-estonia (1)
- # clojure-italy (8)
- # clojure-losangeles (1)
- # clojure-nl (1)
- # clojure-spec (68)
- # clojure-uk (80)
- # clojurescript (89)
- # cursive (31)
- # datomic (22)
- # emacs (2)
- # events (3)
- # figwheel-main (184)
- # fulcro (28)
- # graphql (1)
- # hyperfiddle (2)
- # jobs (1)
- # jobs-discuss (64)
- # luminus (5)
- # off-topic (16)
- # om (2)
- # onyx (1)
- # pedestal (12)
- # portkey (1)
- # re-frame (13)
- # reagent (56)
- # reitit (13)
- # ring-swagger (13)
- # shadow-cljs (145)
- # slack-help (2)
- # specter (6)
@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 foo.bar
to expose as (part of) its API some or all of the public functions from some other namespace (presumably that foo.bar
uses)...?
If I understand correctly, maybe Potemkin's import-vars
would work for you.
https://github.com/ztellman/potemkin#import-vars
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?
@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.
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
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
itโs for side effectful iteration, @stardiviner
@stardiviner switch doseq
to for
@souenzzo the reagent tutorial is great if you haven't go through it yet : http://reagent-project.github.io/
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.
Ok sorry, cljs-playground/core.cljs:
(ns cljs-playground.core
(:require [cljs.nodejs :refer [process]]))
(enable-console-print!)
(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 '[lumo.build.api :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
you wouldn't even need lumo, you get a js file and node can run it
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.
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
you might find this useful https://github.com/bhauman/lein-figwheel/wiki/Node.js-development-with-figwheel
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
#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:
(ns cljs-playground.build
(:require [cljs.build.api :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}))
Here's my full repo https://github.com/staab/cljs-playground/tree/634863cac7b5183b00a1baec778152d566d97b73
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
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'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
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: https://github.com/staab/tetroggle-elm
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
https://github.com/seancorfield/dot-clojure see here for some inspiration
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 :~)
@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?
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. (https://github.com/metosin/reitit) .. need some help in request-coercion.
@UC58KQH9A maybe try the #reitit channel?
Sure.. thanks a lot @U61HA86AG
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
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.
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?
so if I define a lazy-fib as a func and call two times nth it should recompute the vals again
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
There's also a closed form to compute Fibonacci numbers as well. Not sure if helpful here though
Itโs again me solving some random hackerrank challenge. https://www.hackerrank.com/challenges/decibinary-numbers/problem?h_l=interview&playlist_slugs%5B%5D=interview-preparation-kit&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
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?
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.
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)
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))
But on the dev server those environments are not being picked up, for a lack of a better term.
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)
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
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
#!/bin/sh
export FOO='foo for prod'
JVM_OPTS='jvm opts for prod'
JAR='/path/to/uploaded.jar'
java $JVM_OPTS -jar $JAR
- the app would be able to see whatever value for FOO the script sets hereYes. 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)
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?
Yes, group-by can certainly cause reordering. Using reduce might be a bit complicated, though.
user=> (doc group-by)
-------------------------
clojure.core/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.
nil
user=>
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
But still, there are so many functions in clojure, I thought I can do it easier and more elegant.
@bill.kempf group-by does not change the order
Oh.. right
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.
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
this might help https://gist.github.com/pesterhazy/74dd6dc1246f47eb2b9cd48a1eafe649
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!
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.
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]
(persistent!
(reduce
(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]
(persistent!
(reduce
(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 ๐