This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-12-08
Channels
- # adventofcode (240)
- # beginners (87)
- # boot (4)
- # cider (27)
- # cljs-dev (20)
- # cljsrn (24)
- # clojure (365)
- # clojure-argentina (1)
- # clojure-brasil (4)
- # clojure-dev (12)
- # clojure-greece (65)
- # clojure-india (1)
- # clojure-italy (15)
- # clojure-japan (1)
- # clojure-losangeles (1)
- # clojure-madison (4)
- # clojure-poland (3)
- # clojure-russia (5)
- # clojure-spec (3)
- # clojure-uk (105)
- # clojurescript (27)
- # core-async (1)
- # core-logic (3)
- # cursive (61)
- # datomic (68)
- # devcards (4)
- # docs (27)
- # duct (67)
- # emacs (15)
- # events (1)
- # fulcro (8)
- # graphql (50)
- # lein-figwheel (1)
- # lumo (15)
- # numerical-computing (1)
- # off-topic (77)
- # om (3)
- # onyx (5)
- # parinfer (3)
- # planck (2)
- # portkey (5)
- # re-frame (4)
- # reagent (16)
- # ring (14)
- # rum (3)
- # shadow-cljs (17)
- # vim (1)
@beauxkevin welcome 🙂
I can't find a good alternative, short of writing a when-let*
macro that behaves like when-let
but accepts multiple bindings
@dromar56 I use https://github.com/Engelberg/better-cond and also, consider if you should be using when-let
or whether when-some
is more appropriate
Hey there, I'm totally new to Java and Clojure, sorry. Just trying to use this lib https://github.com/hiteshjasani/clj-dnsjava in repl to get nameservers of a domain name and map
over returned collection
clj.core=> (dns/ns-lookup "" :ns)
[{:type :ns, :name "slack.com.", :ttl 86201} {:type :ns, :name "slack.com.", :ttl 86201} {:type :ns, :name "slack.com.", :ttl 86201} {:type :ns, :name "slack.com.", :ttl 86201}]
clj.core=> (map #(.getTarget %) (seq (dns/ns-lookup "" :ns)))
IllegalArgumentException No matching field found: getTarget for class clojure.lang.PersistentArrayMap clojure.lang.Reflector.getInstanceField (Reflector.java:271
)
getTarget
is the property I found at https://github.com/dnsjava/dnsjava/blob/master/org/xbill/DNS/NSRecord.java
@catlion I would do (map #(:target %) (dns/ns-lookup "
for example. But I don't see :target in this list.
looks like NSRecord isn't extended with the info I need https://github.com/hiteshjasani/clj-dnsjava/blob/master/src/clj_dnsjava/core.clj
Try (.run (Lookup. "
and (.run (Lookup. "
I looked at dnsjava examples.
Just found that (mod -1 60)
returns 59
. Quite useful function when it's needed to have 0..60
repeating sequence.
And the ns declaration:
(ns sample-project.core
(:require [cheshire.core :as json]
[clojure.string :as string])
("gen-class"))
@dromar56 if that project.clj was the full file then it is definitely broken, it is missing the closing )
(def x {})
(var-set x {1 :f})
throws ClassCastException clojure.lang.PersistentArrayMap cannot be cast to clojure.lang.Var
. Isn't x
a variable?
I’m not sure, I think you might need 'x
or #'x
there
What you’re seeing is var-set
attempting to set the value of the value of x
> (var-set 'x {1 :f})
ClassCastException clojure.lang.Symbol cannot be cast to clojure.lang.Var clojure.core/var-set (core.clj:4203)
> (var-set #'x {1 :f})
IllegalStateException Can't change/establish root binding of: x with set clojure.lang.Var.set (Var.java:221)
Cool, I think that second one is closer
var-set
function
Usage: (var-set x val)
Sets the value in the var object to val. The var must be
thread-locally bound.
Added in Clojure version 1.0
It's supposed to work but it doesn't!
I’m a little out of my depth here, but I believe that the second error message is because the var you’re trying to set has not been marked as volatile
(def ^:dynamic x 1)
I guess that's the case. But documentation doesn't say it needs to be volatile
.
I used the wrong word, it’s dynamic
rather than volatile
By the way, I’ve been using Clojure since 2011, and I’ve never written code that modifies the value of a def
ed var.
Just mentioning that in passing because I know some newcomers to Clojure are used to having global variables to work with, and that’s an anti-pattern in Clojure
I use atom
if I need mutable state.
I misunderstood the task requirements I got so I actually don't need to re-`def` a variable. But I guess in some cases global variables are actually needed. For example, to keep a list of currently processed connections (say, when using asynchronous HTTP request handling).
Yeah, you have to have some way to manage/modify state, so either a db connection (for persistent state) or most of the time atoms
for holding data like that
Thank you, @manutter51!
@ghsgd2 Re: global state -- a better pattern is Stuart Sierra's Component library. Having mutable singletons, like a def
'd atom, can lead to a lot of problems.
@sammy.castaneda90 First off, I'd recommend implementing it in functions, not a macro.
(defn converter [map-for-conversion default-fn]
(fn [key]
(if (contains? map-for-conversion key)
(get map-for-conversion key)
(default-fn key))))
and then you can do: (defn my-inc [n] (inc n))
(def my-special-inc (converter {0 -1} my-inc))
and then (my-special-inc 0)
will produce -1
but (my-special-inc 1)
will produce 2
Macros really should be considered a "last resort" -- they don't compose and they have all sorts of subtle corners to be wary of.
Alright, thanks @seancorfield. I know I've used a web library that has the 'defroutes' fn or macro, I was just curious of how to accomplish something similar.
defroutes
is a thin macro wrapper over the routes
function.
(defmacro defroutes
"Define a Ring handler function from a sequence of routes. The name may
optionally be followed by a doc-string and metadata map."
[name & routes]
(let [[name routes] (macro/name-with-attributes name routes)]
`(def ~name (routes ~@routes))))
(and that's clojure.tools.macro/name-with-attributes
which is a built-in helper for defining macros, that takes care of docstrings and attribute maps etc)
Yeah that's kind of what I was going for. Something that defines converters as fns in one swoop.
FWIW, here's our code base stats so you can see how few macros we use
Clojure build/config 43 files 2151 total loc
Clojure source 242 files 55971 total loc,
3097 fns, 713 of which are private,
374 vars, 37 macros, 47 atoms,
440 specs, 11 function specs.
Clojure tests 145 files 19354 total loc,
23 specs, 1 function specs.
and nearly all of those are with-{some-context}
style macros that provide a minimal shim around a function.that’s neat. Thanks for sharing. What are specs
? And vars
are namespace global variables? Or do you pass them around to other namespaces as well?
Thank you very much, @seancorfield! Dependency injection is a good old friend 🙂
@seancorfield Please elaborate what problems could be caused by def
'd atom
@ghsgd2 Well, there's the general mindset of "global mutable state is bad" in FP. Then there's the fact that a def
is "run" each time the namespace is loaded (so if you have a DB connection stored in a Var and you reload the namespace, you'll "leak" connections). You can partially work around that with defonce
but you can still lose the reference if the namespace is cleared (which some "reloading" libraries do in order to remove old, unused definitions). There's also the issue of composition: if you have a global singleton, you cannot have more than one instance of your code running (which can be a deal-breaker for libraries and is inconvenient in your application sometimes). There's also testability: if you have global mutable state, your code is harder to test because you need to ensure the global environment is setup correctly for each test.
When we first got started with Clojure at work, we hadn't taken that message on board and we had several global atoms containing state (caches, DB connections, etc) and it's been a pain in the @$$ to deal with over the years as the code base grew. We use Component for all new code now and that really helps, but we still have quite a bit of code that still relies on those old global atoms -- we've papered over some of it by having the start
function in several Components mirror their internal state to the legacy global state. We expect to get rid of all the legacy global state eventually but hardly a day goes by that I wish we'd never done it that way (despite how very convenient it was at first).
yeah - one thing I like to emphasize is that of all the ways of providing a value to a piece of code, a function argument is the only flexible one
if you decide you need a def, or a dynamic var, or a shared atom, or an environment var, or a value sucked down from an API… - you can easily take a function arg and put it into one of those contexts
but it’s the only flexible one - if you try to convert consistently between the others, you’ll have a bad time (and usually the right solution ends up being…. turn it into a function arg first somewhere)
Yup. This ☝️:skin-tone-2:
what channels would be appropriate to post an idea about a library to improve the readability of a projects?
If it’s about projects in general, I’d put it in #clojure (or #clojurescript if it was cljs-specific)
@drewverlee you can also post on #docs
Thank you a lot, @seancorfield, @noisesmith!
Hey guys, I’m looking for your ideas on how to approach DB rows and Spec.
I have a Postgres DB table with a bunch of columns, name, title, role, etc...
which, when queried come through as :name, :title, :role, etc...
.
I have a spec that requires keys to look like, :reference/name, :reference/title, etc...
.
How would you guys go about converting to matching those keys so that the row (given it’s properly formed) matches the spec?
The first thing that came to mind was to just rename all of the keys and add the :reference
prefix to all of them but that seems forced.
@admay clojure.java.jdbc
has a :qualifier
option so you can get namespace-qualified keys back from queries...
(jdbc/query my-pg-spec ["select * from the_table where ..." ...] {:qualifier "reference"})
No idea. Never used HugSQL.
Got it working! Thanks for the heads up on the :qualifier
option @seancorfield! HugSQL allows you to pass in clojure.java.jdbc
options as a parameter to all of the generated functions allowing keyword qualification!
@seancorfield @admay i think it would work as expected so long as you use the same version of clojure.java.jdbc, see https://www.hugsql.org/#using-advanced > Each underlying database library and corresponding HugSQL adapter may support additional options for the execute/query commands. Functions defined by def-db-fns have a variable-arity 4th argument that passes any options through to the underlying database library. doh just saw the mssage above 😊