This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-08-31
Channels
- # announcements (2)
- # beginners (208)
- # boot (4)
- # calva (5)
- # cider (3)
- # clojars (3)
- # clojure (59)
- # clojure-dev (27)
- # clojure-india (2)
- # clojure-spec (46)
- # clojure-uk (3)
- # clojuredesign-podcast (4)
- # clojurescript (11)
- # cursive (10)
- # emacs (2)
- # figwheel-main (3)
- # off-topic (73)
- # onyx (1)
- # re-frame (8)
- # reagent (3)
- # rewrite-clj (12)
- # shadow-cljs (29)
- # spacemacs (1)
- # tools-deps (19)
When I came to Clojure, I felt like a bit of a fraud since we were doing very boring general purpose stuff with Clojure instead of all the cool stuff it seemed like everyone else was doing...
😆 I'm just trying to do boring general purpose stuff ... just in a way that provides faster feedback loops
I'm a polyglot programmer -- but I'm not particularly good at programming, aside from knowing syntax. I'm particularly good at integration
...we were doing JDBC database stuff, text manipulation, basic stuff.
and so, if Clojure can help me fulfill the principles of SOLID without all the ceremony, that's really what I want (and composition instead of inheritance)
Yeah, we definitely found Clojure helped us do all of that boring stuff faster
I'm still having trouble telling my colleagues why this is superior:
(def people (atom ()))
(defn make-person [name age] {:name name :age age})
(defn add-person [people person] (conj people person))
(defn save-person! [p] (swap! people add-person p))
like ... look at all the getters and setters I didn't have to write
and would use their bodies directly when needed
I'm not following
I want to ... but, no comprendo
Use data, not functions.
And use "core" functions instead of writing wrappers.
😕 I thought I was doing functional programming (more than data programming)
like, if a person is a map with a :name and an :age key, you don't need a function to make one of those
is that better than using a (defrecord
?
is (defrecord
just to ease the transition away from OOP?
there are times when they can be a performance improvement, at the loss of generality(but they try to keep as much map generality as they can)
so .. would you prefer a spec to define a person over a (make-person
if I needed a concise place for someone to know what all goes into a person map?
Don't overthink stuff. Simple data is simple. You don't need types or specs for something that simple and transparent really.
this?
(defn person? [p] (not (nil? #(and (:name p) (:age p)))))
That won't do what you think
#(and ,,,)
is a function literal so it'll never be nil
nice:
(def person? #(and (contains? % :name) (contains? % :age)))
Oooh -- and anything that implements this interface is a person
You only need to write as much code as you "care" about...
ok -- so, when you say data
not functions
, you mean predicates like def person?
instead of (defn is-person? [p] ...)
No, those are both functions.
ok .. then you mean just make {:name "" :age 0}
everywhere instead of making a constructor function
Why would you create an empty/invalid person?
In OOP we're using to creating "empty" objects but Clojure doesn't do that -- nil
is generally "nothing". So data either "exists" (and is usually valid) or it doesn't exist nil
.
well, s/""/name
s/0/age
, but it gets defined in the function instead of going through a constructor function
You define it where you need it.
in the past, I've found that hard to share with others when I define it exactly where I need it
like, it requires a conversation or perusing the code to know what's needed
I'd need more context for that, in order to comment.
instead of just saying here's this api lib, go consume it (also no docstrings)
If you have a data structure that is important to your domain and is shared between a lot of functions then, sure, document it with Spec.
But specs are "just data" too really.
I suspect a person data structure is important in many domains
I also suspect it's an overloaded name and has different map properties at different parts of the business domain
the predicate approach can lead in to spec, which is predicate based with additional features for building large complex predicates, and determining which "sub" predicate failed, and for generating data that matches those predicates
Remember that (:foo person)
isn't going to blow up -- just produce nil
if it doesn't contain :foo
. Clojure code generally doesn't care about anything it doesn't need.
thanks all for your help -- dinner time
Dinner time here too. Friday night at the local brewpub I expect 🙂
Hi, I'm following this guide https://clojurescript.org/guides/quick-start It's not going very well.
$ mkdir -p hello-world/src/hello_world
$ cd hello-world/
$ touch src/hello_world/core.cljs deps.edn
$ cat > deps.edn
{:deps {org.clojure/clojurescript {:mvn/version "1.10.520"}}}
$ cat > src/hello_world/core.cljs
(ns hello-world.core)
(println "Hello world!")
$ clj --main cljs.main --compile hello-world.core --repl
-bash: clj: command not found
$ clojure --main cljs.main --compile hello-world.core --repl
Exception in thread "main" java.io.FileNotFoundException: Could not locate cljs/main__init.class, cljs/main.clj or cljs/main.cljc on classpath.
at clojure.lang.RT.load(RT.java:466)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$load_one.invoke(core.clj:5903)
at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
at clojure.core$load_lib.invokeStatic(core.clj:5947)
at clojure.core$load_lib.doInvoke(core.clj:5928)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$load_libs.invokeStatic(core.clj:5985)
at clojure.core$load_libs.doInvoke(core.clj:5969)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$require.invokeStatic(core.clj:6007)
at clojure.main$main_opt.invokeStatic(main.clj:491)
at clojure.main$main_opt.invoke(main.clj:487)
at clojure.main$main.invokeStatic(main.clj:598)
at clojure.main$main.doInvoke(main.clj:561)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.main.main(main.java:37)
I ran sudo apt install clojure
to install clojure. Running Debian Buster.
$ clojure -r
Clojure 1.10.0
user=>
any ideas please?
@calle that's interesting, it's like the clojure
command is not detecting the local deps.edn
at all.
otherwise, you would see something like the following:
Downloading: org/clojure/clojurescript/1.10.520/clojurescript-1.10.520.pom from
Downloading: org/clojure/clojurescript/1.10.520/clojurescript-1.10.520.jar from
for some reason, the clojure
package on debian does something slightly different than what the official installation script (https://www.clojure.org/guides/getting_started#_installation_on_linux) does (e.g., the clojure
debian package doesn't install the clj
shell script). i would try uninstalling that package and install clojure
/`clj` following the official installation instructions for gnu/linux.
ok, cool
previously there was never an official shell launcher for clojure, and whoever made the debian package included some unofficial one, and debian packages take forever to update so 😞
yeah, i ran into some weird issues (e.g., missing clj
shell script, unexpected ways of handling command line arguments, etc.) while i was working on https://github.com/replit/polygott/pull/28 and https://github.com/replit/prybar/pull/30 and had to resort to the the official installation instructions for gnu/linux.
that seem to work much better, thanks
how do I keep my clojure updated now that it's outside of apt?
well, you may need to check for the cli tools version periodically and run the shell script manually once in a while. 🙂
ok, the script removes the old version of clj and installs the new version?
unfortunately, no, the shell script doesn't overwrite/remove existing jars (in /usr/local/lib/clojure/libexec
) therefore you need to clean that up manually otherwise the presence of multiple jars confuses tools such as cursive (https://github.com/cursive-ide/cursive/issues/2105).
oh, ok
each project you have will have a dependency on some possible different version of clojure
but it does ensure that the clojure
/`clj` shell scripts point at the latest installation of clojure binary and jars.
and whatever tooling you use for your projects will make sure that version is available
so the clojure language dependency is managed just like any other library dependency for a project
Has anyone used cljsjs.aws-sdk-js? https://clojars.org/cljsjs/aws-sdk-js
I can't find any examples of how to actually use it...
like, what's the equivalent of
const s3 = new AWS.S3()
?clojure doesn't have classes, right?
ah, there's a new
thing in the js interop https://cljs.github.io/api/cljs.core/#new
So this...
...generates this.
and when I run it:
$ node main.js
/path/to/out/ls_cfn_resources/core.js:5
var s3_526 = (new AWS.S3());
^
ReferenceError: AWS is not defined
at Object.<anonymous> (/home/kabo/Documents/source/ls-cfn-resources/out/ls_cfn_resources/core.js:5:14)
at Module._compile (internal/modules/cjs/loader.js:701:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
at Module.load (internal/modules/cjs/loader.js:600:32)
at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
at Function.Module._load (internal/modules/cjs/loader.js:531:3)
at Module.require (internal/modules/cjs/loader.js:637:17)
at require (internal/modules/cjs/helpers.js:22:18)
at global.CLOSURE_IMPORT_SCRIPT (/home/kabo/Documents/source/ls-cfn-resources/out/goog/bootstrap/nodejs.js:88:13)
at Object.goog.importScript_ (/home/kabo/Documents/source/ls-cfn-resources/out/goog/base.js:951:9)
@calle have you tried this? https://github.com/cognitect-labs/aws-api
I feel like I don't understand how that cljs.aws-sdk-js thing is supposed to work
hmm, no, I'll have a loo
@calle Not sure if many folks are reading and responding soon at this hour, but the #clojurescript channel might also be a good place to ask your questions.
ok, which timezone are most of you in?
I guess the US is sleeping now, and Europe is coming online. I'm in New Zealand.
I mean, please do go ahead and ask your questions, and interested folks will respond if they are around, of course. Just trying to set expectations that sometimes things may get slightly slower around here on weekends.
ah, ok, thanks 🙂
so just because clojure can be compiled to the jvm or javascript doesn't mean the modules can be compiled to jvm or javascript?
or can I compile clojure to javascript without using clojurescript?
confused Calle is confused...
I dev a lot of stuff in node.js so I figured it'd be easier for me to build stuff to javascript instead of JVM
So Clojure/Java and ClojureScript are two very similar variants of Clojure source code, the former being compilable to JVM byte code, the latter compilable to JavaScript
There is enough similarity in the two variants that it is definitely possible to write code that is legal as both variants at the same time.
Not sure if that answered your questions, though, so feel free to follow up
yeah I think I get it, so aws-api uses some things that aren't available in clojurescript, or just doesn't expose an export that clojurescript can pick up.
I believe aws-api is specifically a Clojure wrapper around Java APIs implemented by Amazon.
So not compilable to JavaScript
hmm, wanted to experiment with AWS Lambdas in clojurescript, as there's less cold start delay in the node lambdas than with java lambdas
and I found a serverless-cljs-plugin I thought would be a good way in, as I'm quite familiar with the serverless framework
ah, but I see that plugin actually support building to jvm, so I can still do that
but I've never used aws-api nor examined its internals, so I may be mistaken in what I have said about it there.
Tis not a wrapper. It is a client generated from API data supplied by Amazon. No AWS Java libs employed
what's the clojure equivalent of the npm/yarn registry?
like if I want to do something with timezones I go to https://yarnpkg.com/ type in timezone, and see that moment-timezone
has over 15 million downloads, that's a solid bet. Done.
how could I have found aws-api
besides it being recommended in this channel?
can't find aws-api
on there though...
yeah, I know, I'm playing with plain clojure now
I got aws-api to work
maven seems the most complete in terms of having all the packages there
but the user experience is horrible
clojars seems to have a good UX, but I can't find aws-api on there for example
same goes for the clojure toolbox
I must be doing something wrong
Anyone has some insight on a project with a lot of interaction between JS and CLJS? To put into context, I found a package in JS which I would like to reuse, but the code heavily exploit mutability and context, and this
in JS. I spent 2 days trying to interact with it through reagent, but some components are just impossible to use in it. I also tried to stay as near to JS as possible, but it just did not work. So I decided to give up and write some part of the solution in JS. I wondered if there were some warnings I should be aware of.
I can't get map to behave the way I expect it too...
ls-cfn-resources.core=> (def my-list [{:n "a"}, {:n "b"}])
#'ls-cfn-resources.core/my-list
ls-cfn-resources.core=> (map #(:n %) my-list)
("a" "b")
ls-cfn-resources.core=> ((map #(:n %)) my-list)
#object[clojure.core$map$fn__5847$fn__5848 0x779fe00d "clojure.core$map$fn__5847$fn__5848@779fe00d"]
ls-cfn-resources.core=> ((comp (map #(:n %))) my-list)
#object[clojure.core$map$fn__5847$fn__5848 0x6067c768 "clojure.core$map$fn__5847$fn__5848@6067c768"]
according to https://clojuredocs.org/clojure.core/map I should be able to leave the coll argument off
You’ll need to use partial to get that - (partial map #(:foo %))
Also note that instead of #(:foo %)
is identical to just :foo
That I can just do :foo
was definitely a missing piece of the puzzle, thanks 🙂
looking at https://clojure.org/reference/transducers it looks to me like map
with no coll argument should return a function that takes a seq and returns a seq with the function applied to all the elements in the seq
No, it returns a transducer, which is a special kind of reducing function. It’s not like a curried form of the higher arities, it’s a totally different thing
... but when used with comp
or ->>
it behaves just like the curried form?
I feel like I need to sit down and spend some time with that transducers doc page. Get to know it a bit better. 🙂
simpler example
ls-cfn-resources.core=> (map inc [1 2])
(2 3)
ls-cfn-resources.core=> ((map inc) [1 2])
#object[clojure.core$map$fn__5847$fn__5848 0x3a3e8320 "clojure.core$map$fn__5847$fn__5848@3a3e8320"]
ls-cfn-resources.core=> ((comp (map inc)) [1 2])
#object[clojure.core$map$fn__5847$fn__5848 0x19ec5bcc "clojure.core$map$fn__5847$fn__5848@19ec5bcc"]
why do I need eduction
?
ls-cfn-resources.core=> (eduction (map inc) [1 2])
(2 3)
eduction delays it’s eager reduction until use
Most often it’s used as a delayed reduction over an external resource, like a file etc
is there some standard way to explicitly say that a function is curried? (i have a lot of predicate functions like, say starts-with?
, that when only called with one of two args return a partially applied version of themselves for easier use in map chains and such.. )
No, and that's fairly unusual in Clojure. Generally people expect a function ending in a ? to return true/false
I guess I'll create my own best practise then, as long as im working alone on this project ;) i really miss partial application (i know partial
exists, but using it feels extremely ineficcent as it's not more readable and considerably longer than lambda..) ill just suffix currying functions with -c
or something
the idiomatic Clojure alternative is anonymous function syntax #(... %)
Yea thats what i currently use, but it still makes already rather crowded Code Look a lot more complex than it Has to be
Hi everyone! Am I right with spec that I cannot use clojure.spec.test.alpha/check
while functions are instrumented?
It seems that the check conforms the inputs before the test. Since I have an
(s/or :case1 (s/keys ...)
:case2 (s/keys ...))
conforming the value will turn these maps into vectors [keyword map] which does not conform to the specAn interesting side-note that this means that conformed values do not themselves conform to the spec... feels weird to me.
That’s not weird, true for many specs
Where are you seeing the conformed values?
When I run stest/check
it's giving me failures because the input does not conform to spec
I think you are misdiagnosing the problem
But you’ll need to share more
Might want to move this to #clojure-spec
There are hundreds of channels -- click Channels in the left nav bar to see them all. Visit #slack-help for assistance in getting around the community here.
Just start a new thread there -- but share your code if you can.
do symbols which begin with @
have some special meaning in clojure or convention around them?
@
is a reader macro. @foo
expands to (deref foo)
. https://clojuredocs.org/clojure.core/deref
aha, I've found something that works as I expect it to.
=> (map inc [1 2])
(2 3)
=> ((map inc) [1 2])
#object[clojure.core$map$fn__5847$fn__5848 0x3a3e8320 "clojure.core$map$fn__5847$fn__5848@3a3e8320"]
=> ((partial map inc) [1 2])
(2 3)
is there a simple explanation why (map inc)
!= (partial map inc)
?
ok, I'm using it in comp
, can I do ((comp (into [] (map inc))) [1 2])
?
hmm, ok, so I need to use partial then
I'm used to node.js and ramda
basically I'm trying to do
R.pipe(
R.prop('k'),
R.map(R.prop('n'))
)({ k: [{n: "a"}, {n: "b"}] })
which would return ['a', 'b']
pipe
was easy to recreate
(defn pipe
[ & args ]
(apply comp (reverse args)))
so there are two key ideas about transducers.
First, and arguably most importantly, is the idea that they separate out the core idea of a some functional transformation, from the mechanics of dealing with input seq and output seq. If you look at the source of the 2+ arity of map
, it's a bunch of code that's looking at what kind of input collection it is and handling chunking it up or not appropriately. But the essential idea of map
is applying a function to values. So the transducing version of map
just does that, and the transducing process itself handles the various types of inputs and output types you might have. For instance, transducers can be applied to streams (eg core.async channels), not just a specific set of clojure seqs.
The other idea is that they can be more efficient when doing multiple steps. When using the seq-based transforms, map
, filter
, partition-all
, etc there are intermediate lazy seqs created between each step.
I think you're looking for this: https://clojuredocs.org/clojure.core/-%3E%3E
(->> thing :k (map inc))
I read that ->>
turns thing
into a list if it's not a list, which I don't want, right?
no, ->>
doesn't do that. It just threads the result of each form into the last arg of the next form
Am I reading the docs wrong? https://clojuredocs.org/clojure.core/-%3E%3E > Inserts x as the last item in the first form, making a list of it if it is not a list already.
ah, the it
was a bit unclear
Although (->> thing :k (map inc))
won't actually do what you want since you're passing a collection of seqs in
so thing you're doing with R.pipe ...
can be done with threaded form like, (->> thing (map :k) (mapcat :n))
, or with transducers like, (into [] (comp (map :k) (mapcat :n)) thing)
yup, trying it out now
thanks!
I suspected there was probably some neater way of doing it
you can also remove nils in the transducer version with (into [] (comp (map :k) (keep :n) cat) thing)
Sweet, I got it working 🙂
To list all AWS resources in all AWS CloudFormation stacks:
(->>
(aws/invoke cfn {:op :ListStacks})
:StackSummaries
(map :StackName)
(map #(aws/invoke cfn {:op :ListStackResources :request {:StackName %}}))
(mapcat :StackResourceSummaries)
(map :PhysicalResourceId)
sort
println)
I have a question regarding writing specs. lets say I have some map that looks like:
{:attribute-a {:penguin 0
:otter 3
:giraffe 5}
:attribute-b {:section-a {:penguin true
:otter false
:giraffe true}
:section-b {:otter 1.0}}}
How am I supposed to write specs for this? :penguin
:otter
and :giraffe
all follow different rules based on where they are inside the map. I heard someone recommend namespaces? (s/def ::attribute-a/penguin int?)
, but when doing this, the attribute-a
namespace “isn’t recognized”. I know I must be missing something, but I’m not sure what it is.I think the problem is that ::attribute-a/penguin
is not a valid keyword. It is a short cut for fully qualifying a keyword based on an aliased namespace. You probably want one of the following:
(s/def :attribute-a/penguin int?)
or
(require '[some.other.namespace :as attribute-a])
(s/def ::attribute-a/penguin int?)
In the second example, ::attribute-a/penguin
is expanded by clojure's reader to :some.other.namespace/penguin
.Thanks for responding! So, if I were defining the map as we have it here. I suppose what I really want to write, and excuse my lack of syntax knowledge.
(s/def :attribute-a/penguin (s/int-in 0 2))
(s/def :attribute-b/otter (s/int-in 0 4))
(s/def :attribute-b/giraffe (s/int-in 0 6))
(s/def ::attribute-a (s/keys :req-un [:attribute-a/penguin
:attribute-b/otter
:attribute-b/giraffe]))
(s/def :section-a/penguin boolean?)
(s/def :section-a/otter boolean?)
(s/def :section-a/giraffe boolean?)
(s/def ::section-a (s/keys :req-un [:section-a/penguin
:section-a/otter
:section-a/giraffe]))
(s/def :section-b/otter (s/double-in :min 0 :max 2))
(s/def ::section-b (s/keys :req-un [:section-b/otter]))
(s/def ::attribute-b (s/keys :req-un [::section-a
::section-b]))
(s/def ::animals (s/keys :req-un [::attribute-a ::attribute-b]))
(def thing (gen/generate (s/gen ::animals)))
(-> thing :attribute-b :section-b :otter)
So……. I just wrote all this out, expecting that the lowest keys would look like :attribute-a/section-b/otter (as one long string)
But…. they don’t. And that last line actually works.
The first clue you gave me (s/def :attribute-a/penguin int?)
was spot on. Thank you!
Ah heck. Now to figure out how to access something like the :section-a/penguin
from another file 😞
you need to require the namespace with the specs in the them if you want to use them outside of that namespace
Yeah, so if the specs file has got this at the top:
(ns some-project.some-dir.specs
(:require [clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as gen]))
From an importing file I’ve been doing:
(ns some-project.some-dir.some-other-file
(:require [some-project.some-dir.specs :as specs])
With this syntax, I can’t just use :section-a/penguin
because I can’t do:
(gen/generate (s/gen :specs/section-a/penguin))
I think I have to :refer
to something like :section-a/penguin
directly in the :require sexpYou shouldn't need to refer the specs to use them. Specs are defined in a global registry (which is why they're namespaced keys). Requiring them should be enough.
If your specs are in namespace d
and you evaluate code in namespace a
which requires namespace b
which requires namespace c
which requires namespace d
, then you're spec definitions will be evaluated and available for use.
I’ve gotta break that down. Let’s check my understanding. Starting with namespace d
(ns a.b.c.d
(:require [clojure.spec.alpha :as s]))
(s/def ::penguin int?)
then in namespace a
the following should be possible and create a generator?:
(ns a
(:require [clojure.spec.alpha :as s]))
(s/gen ::penguin)