This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-09-02
Channels
- # announcements (21)
- # bangalore-clj (1)
- # beginners (122)
- # calva (29)
- # cider (9)
- # cljdoc (1)
- # cljs-dev (7)
- # cljsrn (1)
- # clojure (84)
- # clojure-dev (11)
- # clojure-europe (2)
- # clojure-houston (2)
- # clojure-italy (31)
- # clojure-nl (5)
- # clojure-uk (37)
- # clojuredesign-podcast (3)
- # clojurescript (14)
- # cursive (66)
- # data-science (5)
- # datavis (1)
- # datomic (6)
- # fulcro (16)
- # graphql (4)
- # jobs (2)
- # music (1)
- # off-topic (20)
- # pedestal (1)
- # re-frame (2)
- # reagent (2)
- # shadow-cljs (155)
- # spacemacs (5)
- # tools-deps (5)
- # vim (8)
- # yada (1)
@seancorfield @hiredman @alexmiller
I've found the recent discussion about "data not functions" (`make-person` et al.) interesting - I'd call it a "constructors and selectors" debate ๐
I've been grappling with this idea for a while and while it's true I haven't seen this very often to be used in Clojure code I'm not still sure if and when to use that approach.
Especially since some people suggest to use this to build "abstraction barriers":
- SICP chapter 2.1 suggests using constructors (like make-rat
) and selectors (like numer
, denom
) to abstract away from the concrete representation of rational numbers (cons cells)
- @ericnormand (https://purelyfunctional.tv/lesson/card-functional-abstraction/) mentions a problem when you originally chose incorrect data representation and you hardcoded it everywhere in the code base
- In https://www.infoq.com/articles/in-depth-look-clojure-collections/ (which I suspect comes from the first ed. of Joy of Clojure) they also show the build-move
function which is basically (apply hashmap pieces)
Is this perhaps more specific to other Lisps than Clojure which has richer data types and sequence abstraction?
Imo, the idea that a function barrier is going to save you from making the wrong choice is a false one
Changing the name of an attribute is going to leak everywhere, including through a barrier as itโs in the data and the data is first class and visible
Thus using an extra layer of functions only makes your problem worse by creating another set of places to change
If this is all in code you own, just lean on the attributes and change the attribute
If itโs in code that interacts with others, take an additive approach - add new attributes that take over and let the old ones languish. Or if you need to change/remove, then create new aggregates
Perfect, thanks! How about the idea of "deferring decisions"? When they show an example how to postpone an implementation decision like computing GCD on rational numbers. You can compute GCD either in constructor (make-rat) or in selectors; in either case clients don't need to worry about that because they only use selectors to access rational number's components.
@U06BE1L6T That feels like a very artificial problem to me. And also one that is, IMO, inherently present in OOP where data hiding means you need to make those decisions.
If you have "just data", and "just computation" then it becomes transparent where it gets called -- and if you wanted to cache some (potentially expensive) computation, you build a wrapper function, and code paths that care about that caching can use that instead of the original function.
As for "originally chose incorrect data representation" -- yes, that can happen, even if you spend plenty of hammock time designing the data (but it should be fairly rare). Still, you can add a v2 representation and API and deprecate the old one. Such changes would be driven either by performance or functionality requirements so having a v1 API that is slow and/or limited and a v2 API that is faster and/or more powerful is a reasonable approach -- users will switch over pretty fast but keeping the old API around does (almost) no harm. And that v1 API could eventually be discarded once you know it has no clients ("break none" shall be the whole of the law, to appropriate and misquote ๐ )
@gisf You may be interested in https://re-find.it/ if you want to find a function for an example to an example:
e.g. 1
=> "1"
https://re-find.it/?args=1&ret=%221%22
hi. how can i use http request between clojure as a server and clojurescript as a client?
have you tried with: https://github.com/r0man/cljs-http If you're more used to ajax maybe: https://github.com/JulianBirch/cljs-ajax I'm not great at cljs, so if someone recommends something take their opinion first.
if it isn't what you're looking for you can also check in or post the question on: https://ask.clojure.org ๐
thanks for your answer. I tried the first link for client but it did't sync with clj-http library for clojure.
just using the javascript fetch api from cljs isn't too bad either
this is how you fetch https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API and this is how you translate js to cljs https://clojurescript.org/guides/faq-js
and hereโs a small example of wrapping fetch in core.async https://bitbucket.org/vnorilo/veneer/src/default/src/veneer/cloud/request.cljs
if it isn't what you're looking for you can also check in or post the question on: https://ask.clojure.org ๐
back to my macro-question from yesterday:
it worked! but there is one big problem:
i now can only use concrete values in my forms, and nothing that needs to be calculated (especially nothing that is dependent on locals stuff)
is it even possible to avoid this problem? i tried things like eval-ing the rest of each form and quoting the first, but then i get cannot eval locals
errors, which obviously make sense. any ideas?
(my problem was the following:
i need a macro that can get called like this:
(foo
[+ 1 2]
[str 123]
)
(vararg)
and return a structure like this:
['(+ 1 2) '(str 123)]
without giving me the function-objects, but the actual symbols of the functions.
i know realized that i might need a different return structure...)wait i think I got it... altough it seems ugly:
(defmacro foo [& forms]
(let [lists# (for [[func & args] forms]
(list 'apply 'list
(apply vector
(list 'quote func)
args)))]
`[~@lists#]))
okay what could cause:
Caused by: java.lang.RuntimeException: Unable to resolve symbol: apply in this context
?
getting it somewhere related to my macro-definition from before, but the line-number doesn't help at all...
it could be related to me using that macro within a generated namespace or something, maybe i need to somehow first include core stuff in generated namespaces?
haha wow that seems to have been it.... i just added [clojure.core :refer :all] to my requrie thing in the namespace and now it seems to work
I updated my java recently and started to get this warning in one of my projects:
ARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by clojure.lang.InjectedInvoker/0x0000000801368040 (file:/C:/Users/felix/git/smvtrcviz/target/uberjar/smvtrcviz-0.1.0-SNAPSHOT-standalone.jar) to method com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.parse(org.xml.sax.InputSource,org.xml.sax.helpers.DefaultHandler)
WARNING: Please consider reporting this to the maintainers of clojure.lang.InjectedInvoker/0x0000000801368040
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
After digging around a bit I found a way to debug it and managed to pinpoint the source of the warning to clojure.xml$startparse_sax.invokeStatic(xml.clj:76)
. Is this an issue that is worth reporting? And if so: Is it right to do it here https://groups.google.com/forum/#!forum/clojure? Or is there something I could do about this.There is a ticket for this, really itโs just a warning from use of reflection in clojure.xml (which really no one should use anyways)
So I just should wait and pray? ๐
To understand more, this faq entry is helpful https://clojure.org/guides/faq#illegal_access
In this particular case, the Clojure code in clojure.xml is intentionally reflective in ways that are difficult to fix. clojure.xml is very old and really superseded by libraries like clojure.data/xml
This warning is just a warning - it can be safely ignored
There are various flags you could pass to make this particular reflective access valid but Iโm not sure itโs worth doing so
I presume the usage is in smvtrcviz - that lib could be changed to use a better api
Well, just changed the xml parser to clojure.data.xml but that also has an illegal reflective access. Thanks for all the information, though!
Are you using the latest? I thought these issues were fixed in clojure.data.xml
0.2.0-alpha6 is latest
No, I was using stable. Will try latest as well.
despite the name, latest is very stable
(and prior is pretty old now)
Works fine with latest! Thanks for the hint on that library ๐
this.tempWorkingPath = this.cfg.autoImport.tempWorkingDir.replace('$MANDANT$','M' + mandantNr);
whoops
Hey everyone, i'm using Rum to generate a japanese grammar quiz. It's cool, a fragment is hidden from you, it shuffles the fragment correct response with some faulty ones and renders them as buttons
And I thought I had a problem with react keys but... i just fixed it ๐
amazing what fresh eyes can do
Hi friends.
I just came by the function eduction
that takes some transducers
, a list
and apply them.
My question is: when should I prefer eduction
over ->>
?
eduction is pretty specialized in that it is a delayed eager evaluation
the most common use case is to set up a reduction over an external resource (file, db result, etc) and hand that to a caller, who can a) decide when to invoke it and b) know that since it's eager the resource use is complete
vs lazy sequences which are also delayed (but throughout, not just at the beginning) but are harder to catch and handle the "end" for resource control
I'd say that's wrong on both counts. ->>
creates a lazily computed sequence which will produce values as needed. eduction
creates a pending computation. when you ask for the first value, it will compute the entire collection eagerly at that point.
@alexmiller I got confused by your statement that eduction
computes the whole collection eagerly when you ask for the first value.
Trying something like this shows that it only consumes a "chunk" of a lazy seq:
(def cnt1 (atom 0))
(let [res (eduction (map #(do (swap! cnt1 inc) %)) (range 100))]
(conj (rest res) (first res))
@cnt1)
;; 66 (2 x 33 - chunked seqs?)
Am I misinterpreting you?Anyone coming across this thread and wondering about similar things as @U06BE1L6T can find Alex's explanation in the thread at https://clojurians.slack.com/archives/C03S1KBA2/p1681383034191669 Bottom line: eduction itself is iterator-based, not inherently eager, but is often used with reduce, which is eager.
it's a real word. transduce means "to lead across". educe means "to draw out" or "to lead out"
hi guys, I'm having hard time to figure out how to go from this :
(def test-a [{:tag :categories}
{:tag :associations
:content [{:tag :categories
:content '()}
{:tag :products
:content [{:tag :product}
{:tag :product}
{:tag :product}]}]}])
to this:
[{:tag :categories}
{:tag :associations
:content [{:tag :categories
:content '()}
{:tag :products
:content []}]}]
@schmee i'm trying to use the same for a lazy seq , but it don't seem to work , what is the alternative for lazy seqs?
you canโt access lazy-seq elements by index (only using first
/`rest`) but you can convert the lazy seq to a vector with vec
(let [remote-categories (remote-get categories)
cat1 (remote-get (str categories "/12"))
xml (->xml cat1)]
(as-> (:content xml) v
(vec v)
(update-in v [0 :content] vec)
(update-in v [0 :content 16 :content] vec)
(update-in v [0 :content 16 :content 1 :content] vec)
(update-in v [0 :content 16 :content 1 :content 1] vec)
(assoc-in v [0 :content 16 :content 1 :content] ())))
is there a nicer way? , I had to convert all the nested lazy seqs to vec in order to get it work
@vachichng You don't need as->
there -- plain ->
will work (and then omit v
in all those expressions). as->
is intended to be used inside a ->
pipeline rather than at the top-level.
@vachichng did you try to write a recursive function?
Or use use clojure.zip?
Overall tho', it looks like you might be better served by transforming this to some data structure that would be more amenable to processing -- can you explain in more detail what end result you're trying to achieve?
@seancorfield yeah, I'm consuming a webservice from prestashop, which serves xml data , what I'm trying to do , is get a xml from one instance and do a post to another instance ( migrate data ) with some fields changed
Ugh! That's an unpleasant task. In that case, I think the suggestion to use zippers is probably going to be your best approach so you can convert XML to data, modify it with zippers, and convert it back to XML.
hey guys, I didn't understood this error
class clojure.lang.Keyword cannot be cast to class java.lang.String (clojure.lang.Keyword is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')
I made this error using environ
(->> environ/env (reduce-kv (fn [m k v] (assoc m (keyword "env" k) v)) {}))
and doing this
(keyword "bob" :bob)
-> ClassCastException clojure.lang.Keyword cannot be cast to java.lang.String clojure.core/keyword (core.clj:595)
. You need to use strings not kewords. But what are you putting these with an env namespace?
thanks!
I used here
I'm mixing another file into environ and calling this with another function that I use (let [:env/keys [...] ... )
thanks ๐
https://www.ibm.com/developerworks/library/j-treevisit/j-treevisit-pdf.pdf @alexmiller is everywhere , lol
do I need to run spec/explain on this to find out what the problem is?
(ns hello-cdk.hello-stack
(:gen-class
:extends software.amazon.awscdk.core.Stack
:constructors {[software.amazon.awscdk.core.Construct String] [software.amazon.awscdk.core.Construct String nil]
[software.amazon.awscdk.core.Construct String software.amazon.awscdk.core.StackProps] [software.amazon.awscdk.core.Construct String software.amazon.awscdk.core.StackProps]}
:init "init")
(:import (software.amazon.awscdk.core Construct Stack StackProps)
(software.amazon.awscdk.services.s3 Bucket BucketProps)))
Syntax error macroexpanding clojure.core/ns at (REPL:1:1).
((:gen-class :extends software.amazon.awscdk.core.Stack :constructors {[software.amazon.awscdk.core.Construct String] [software.amazon.awscdk.core.Construct String nil], [software.amazon.awscdk.core.Construct String software.amazon.awscdk.core.StackProps] [software.amazon.awscdk.core.Construct String software.amazon.awscdk.core.StackProps]} :init "init") (:import (software.amazon.awscdk.core Construct Stack StackProps) (software.amazon.awscdk.services.s3 Bucket BucketProps))) - failed: Extra input spec: :clojure.core.specs.alpha/ns-form
class clojure.lang.Compiler$CompilerException
*e
will print the entire exception and stack trace but I don't think it will help you...
I think the problem is that you omitted the :name
option -- which is the only mandatory option for :gen-class
if I'm reading the docs correctly...
Hmm, well, that's not the only problem... let me read the docs more closely...
added :name
:
Syntax error macroexpanding clojure.core/ns at (src/hello_cdk//Users/johnjelinek/Documents/code/hello-cdk/src/hello_cdk/hello_stack.clj:1:1).
((:gen-class :name hello-cdk.hello-stack :extends software.amazon.awscdk.core.Stack :constructors {[software.amazon.awscdk.core.Construct String] [software.amazon.awscdk.core.Construct String nil], [software.amazon.awscdk.core.Construct String software.amazon.awscdk.core.StackProps] [software.amazon.awscdk.core.Construct String software.amazon.awscdk.core.StackProps]} :init init) (:import (software.amazon.awscdk.core Construct Stack StackProps) (software.amazon.awscdk.services.s3 Bucket BucketProps))) - failed: Extra input spec: :clojure.core.specs.alpha/ns-form
I tried wrapping this in an (s/explain
.. didn't add any extra info
Like I said "not the only problem" ๐
I almost never use :gen-class
and I wrestle with it every time I do... ๐
*e
#error {
:cause "Call to clojure.core/ns did not conform to spec."
Yeah, exactly. I said it wouldn't help ๐
:data #:clojure.spec.alpha{:problems [{:path [], :reason "Extra input", :pred (clojure.spec.alpha/cat :docstring (clojure.spec.alpha/? clojure.core/string?) :attr-map (clojure.spec.alpha/? clojure.core/map?)
wat ... it got fixed when I remove nil
:init init
not :init "init"
as well I think?
this evaluates:
(ns hello-cdk.hello-stack
(:gen-class
:name hello-cdk.hello-stack
:extends software.amazon.awscdk.core.Stack
:constructors {[software.amazon.awscdk.core.Construct String] [software.amazon.awscdk.core.Construct String]
[software.amazon.awscdk.core.Construct String software.amazon.awscdk.core.StackProps] [software.amazon.awscdk.core.Construct String software.amazon.awscdk.core.StackProps]}
:init init)
(:import (software.amazon.awscdk.core Construct Stack StackProps)
(software.amazon.awscdk.services.s3 Bucket BucketProps)))
And, yeah, nil
is not a class name. I was just about to point that out.
the thing is .... for the first constructor, it needs to pass nil
to the third param of the superclass
You need the type there.
(java null)
software.amazon.awscdk.core.StackProps
ya, that evals ... but, how do I tell it to pass null to that? does it happen automatically since there's only 2 params passed to the constructor?
You write the constructor.
(defn -init
([parent id] ,,,)
([parent id props] ,,,))
replacing the body with what to tell it to pass to super? I thought gen-class handled that partThe :constructors
part tells Clojure what signatures to use for the generated Java code -- but you still write the code.
At least, that's my understanding... but, like I say, I don't use :gen-class
very often.
The init function is weird. It returns a pair of values -- the first value is the list of values to pass to the super constructor, the second value is any state your class needs.
So based on your example, I'd expect something like:
(defn -init
([parent id] [[parent id nil] ,,,])
([parent id props] [[parent id props] ,,,]))
So in the two-arg case, you return a triple that is used to call the super constructor, passing nil
as the third arg.