This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-05-25
Channels
- # aws (10)
- # babashka (4)
- # beginners (103)
- # calva (19)
- # chlorine-clover (2)
- # cider (10)
- # cljs-dev (23)
- # cljsrn (6)
- # clojure (145)
- # clojure-europe (17)
- # clojure-nl (1)
- # clojure-spec (11)
- # clojure-uk (4)
- # clojurescript (64)
- # conjure (11)
- # core-async (19)
- # cursive (38)
- # datomic (4)
- # duct (2)
- # fulcro (51)
- # helix (11)
- # joker (1)
- # kaocha (7)
- # leiningen (3)
- # malli (5)
- # meander (3)
- # off-topic (12)
- # pathom (17)
- # pedestal (2)
- # re-frame (27)
- # rum (11)
- # shadow-cljs (77)
- # xtdb (9)
- # yada (1)
is there any way to name protocol functions? app.core$eval831$fn__832$G__822__841
is not very helpful
Are you sure that's a protocol function @vale?
That looks more like something from an anonymous function...
Oh wow, yeah, just repro'd.
Hah, I've never tried to just look at a bare protocol function type before...
i've been staring at flame charts a lot recently and this can make pinpointing problems really hard
luckily i only have a few protocols only called from 1-2 points so i can do trial and error
it'd be great if i could name them somehow even if it had to be some manual meta map or something
What version of Clojure are you using? I get a much more intelligible error for an arity error in the call
I get something like No single method: bar of interface: user.Foo found for function: bar of protocol: Foo
(for a call of (bar obj 1 2 3)
when it should just be (bar obj)
user=> (defprotocol Foo (bar [this]))
Foo
user=> (defrecord Fubar [] Foo (bar [this] (println "fubar!")))
user.Fubar
user=> (bar (->Fubar))
fubar!
nil
user=> (bar (->Fubar) 1 2 3)
Syntax error (IllegalArgumentException) compiling bar at (REPL:1:1).
No single method: bar of interface: user.Foo found for function: bar of protocol: Foo
user=>
That's on 1.10.1user=> (extend-protocol Foo String (bar [this] (println "string!")))
nil
user=> (bar "hi!")
string!
nil
user=> (bar "hi!" 1 2 3)
Syntax error (IllegalArgumentException) compiling bar at (REPL:1:1).
No single method: bar of interface: user.Foo found for function: bar of protocol: Foo
user=>
it shows up in error messages only if the implementation is wrong (for example arity missing)
This was kind'a surprising to me:
user> (def bla (filter #(throw (Exception. %)) ["lol" "foo"]))
;; => #'user/bla
user> (prn bla)
Execution error at sun.reflect.NativeConstructorAccessorImpl/newInstance0 (NativeConstructorAccessorImpl.java:-2).
lol
(
user> (prn bla)
()
;; => nil
user>
I think I would have expected the second prn
to print nil
(or something else than an empty list (which in someways is nil
, so whatever) 🙃Laziness. Since the first element wasn't realized yet, the exception wasn't thrown.
The problem isn't that (def bla ...)
doesn't throw, the problem is that the second prn
doesn't do what I expected it to do.
check the type of blah. I think it's a LazySeq with 0 elements, which checks out, since filter returns a LazySeq and when you tried to materialize the first element (via prn) it threw, thus remaining with 0 elements
Yeah, same. Just by looking at the LazySeq
code I don't see why it would not do that.
http://clj-me.cgrand.net/2013/09/11/macros-closures-and-unexpected-object-retention/ on the :once
thing
(defn filter
"Returns a lazy sequence of the items in coll for which
(pred item) returns logical true. pred must be free of side-effects.
Returns a transducer when no collection is provided."
{:added "1.0"
:static true}
([pred coll]
(lazy-seq
(when-let [s (seq coll)]
(let [f (first s) r (rest s)]
(if (pred f)
(cons f (filter pred r))
(filter pred r)))))))
Interesting - my filter
is quite a bit more complex. But works the same way.
> The answer isn't in LazySeq's code, but in filter's
I disagree. If it was that simple, you'd see the exception being thrown each time you call bla
.
blah isn't a function, it's a lazy sequence, once it's realized it isn't realized again (in that sense LazySeq's implementation does matter)
final synchronized Object sval(){
if(fn != null)
{
sv = fn.invoke();
fn = null;
}
if(sv != null)
return sv;
return s;
}
final synchronized public ISeq seq(){
sval();
if(sv != null)
{
Object ls = sv;
sv = null;
while(ls instanceof LazySeq)
{
ls = ((LazySeq)ls).sval();
}
s = RT.seq(ls);
}
return s;
}
"Realized" sounds like it has a state. "sval throws" doesn't sound like a state. I'm trying to play with it with a debugger.
So something strange is happening. The first call to fn.invoke()
when you eval bla
the first time throws, just as expected. But the second call just... returns nil
. No idea why.
I think I see your point, you're saying that because it throws it doesn't get to the line where fn is set to null?
Ah, got it. Because of :once
, the vector gets garbage collected, so the body of (when-let [s (seq coll)] ...)
is not executed.
Oh maybe I was half right, given that "closed-over references should be cleared" (from the link above). this.coll
of the fn
definitely becomes null
, so maybe that's that. But I don't know how references really work in Clojure, so that may be something else entirely.
One could of course argue that throwing an exception makes the anon-fn impure, which would make the observed original behaviour ok.
https://ask.clojure.org/index.php/9326/on-the-realization-of-lazy-seqs-which-throw-exceptions
Just a general question, I am kinda new (I did clojure for fun, now I'm dealing with real codebase). How prevalent is the use of atoms in clojure, my codebase has so many atoms which are used more or less like global vars and the whole thing gives my headache so I think this is not the way to roll(sadly its over 50k loc, so I have to find a strategy to be effective with changes/refactors)? A problem I run into now there is this conf namespace that holds all the configuration in atoms, then later every function uses this atoms in its inner logic, so if I want to say push an item to queue I can define the item, but cannot reliably deliver different queue details, since the queue-push takes only item details and queue details are inferred internally in function from atom, if the client is not started it gets started etc. So returning to original is abuse of atoms as global-vars-look-alike common/desired?
Atoms should not be used like that. I've not heard of that in clojure. I see maybe one or two used in small areas of a codebase.
I would start adding an arity to functions which take an explicit value instead of the atom value. Then I'd work on slowly bubbling the conf namespace use as far outwards as possible. Once there you have minimized use of it, and you can more easily get rid of it. You have a long journey ahead.
the arity solution is something I was thinking, however I see how it helped them with development, since you do not have to pass around a lot of stuff and do bookkeeping, like with this queue example, you just simply have some config for the queue and the client will be created for you from config atom, then stored in client atom, and you just have to run one function in every other function to simply get it, instead of passing it around to every function
still far better to have a single atom and update parts of it using functions than have multiple atoms
Also, consider component. I discovered it a few days ago, and it certainly makes it easier to deal with stateful bits
I would argue having those explicit parameters is showing you all the systems a particular function is touching with its change, and is a case of positive pain; like the pain that protects you from keeping your hand on the hot stove, once your function starts requiring lots of parameters, it gets bulky and painful to use, but that may be a sign something should be changed. Imperative code doesn't tend to be without the injury inflicted, its just numb to it. Building up a program that way is like living a life on painkillers -- definitely a bit smoother at times, but unfortunately with the darker side of its tradeoffs as well.
Well this is beyond imperative/declarative/functional, its more about hiding certain important parts of the system. Cause next thing is that testing is suffering, because the client is implicitly created, rather than passed around, which makes it hard to mock the stateful parts. And you can do it in almost any language.
I strongly disagree - declaritive
literally means that there's nothing changing or changed by a form other than what you see in the text of the form and what it calls.
if you are using atoms that aren't passed in as arguments and then returned to the caller on completion, it's not declarative
I can see why you would do it, I think they considered it less bloatey, to keep the client instance in the atom and refer to it when needed from within a function, as oposed to incrementing arity by the client and doing passing around. It's sort of like a Singleton I guess, at least pattern looks similar. Again the problem remains that you have 7 functions and just can toss data into them. So in the end, "interfaces" are cleaner, because there is only one way to instantiate client and only one client should be available. So I am still torn if that is okish code...
injection > single atom > multiple atoms
is that a fair hierarchy?
injection like component
explicitly, each dependency to each function
Well, I'm referring specifically to the idea of using atoms over parameters, nothing more. That particular aspect is trading a pure function for an imperative effect
as for the Don't program imperatively kids
, that was me trying / failing to make a drugs joke, since I compared it to basically doing heroin / painkillers and realized immediately the comparison was not perfect ahahaha
Dont worry I get it. Not recommending all drugs is an obvious joke 🙂 I was just claiming that it doesn't matter what "paradigm" you can do this type of stuff anywhere.
Ok good ahahah You can program imperatively in any language, that does not make it not imperative. Using atoms this way is expressing change imperatively, using side effects / statements instead of expressions, and this is the drawback of doing so in all languages -- your function draws in inputs implicitly to compute its change, through a side effect (reading from an atom in this case), and expresses that change by sending it out to another atom (or returning it -- imperative functions are seldom all imperative). And as I argued, writing it declaratively with a pure function makes all this explicit, and the pain it adds is positive
thanks for help, this were my intuitions as well, especially that I see a lot of stuff that is not stateless like s3 client, and they still shove it into atom... it just makes all the debugging very mystic... I have to track every atom, where it was set, how was it set etc. forget about repl, since the whole bootstraping is so complicated that you just cannot hookin and investigate values
reagent atom cursors could be interesting for managing atoms in applications
Ok, since today's morning advices were very helpful, I riddle you that(perhaps #beginners is better place? I dont know so shout if so): I think this is a common pattern for application that there is some configuration file and it gets loaded and some values get set. In the codebase I deal with people are kinda doing java abstractions(using packages/ns as quasi objects). They load config data into an atom (I guess that use case is legit?) then there is a bunch of packages that are built implicitly on this config atom. So there is package/ns that handles s3 and every function starts with a dance of creating another atom with s3 client(kind like a singleton pattern), that part seems crooked since s3 client is stateless so you can recreate him ad-hoc on every invokation and dont have to persist reference. But then I guess I have to append extra param to every function and so every package using/importing that s3 package would have to deal with passing the config and extracting (required part of) the config?
I think the key is that you can talk about S3 operations without any configuration (ie, separate what from how within your codebase). the only part of the system that needs the configuration is the part that performs the operations. So you can talk about gets, puts, deletes, etc and even compose them without requiring the caller to specify any configuration. you can then pass these sets of operations to a function that knows how to execute them. You can even have multiple implementations (eg. mocking) for dev/prod/test. example pseudo code:
(def operations (comp (s3/get "foo")
(s3/put "bar"))
(def test-result (mock-s3/execute! operations))
(def result (s3/execute! my-s3-config operations))
if you’re passing around config everywhere and each part of your program requires some sort of context, then you’re probably unnecessarily combining what and how
I see, looks legit, the problem is s3, is used sporadically "here and there". So its difficult since side effects are scattered, I agree that it would be best to build series of operations and just run them.
in the short term, if you’re truly only using one configuration within your application, then it’s okay, but not ideal, to have global state
for aws specifically, the java libraries (and some of the clojure libraries built on top of them) have decent solutions for this problem like using profiles. I’ve also used instance based authentication in the past with good results
thanks, no worries i roughly know how iam and sts operates - I was more suspicious of code patterns that I looked at
btw interestingly enough what @U7RJTCH6J wrote (at least at first, I've only read the first two posts so far) is a lot of what I was coming back to write anyways
because, going back to my imperative ramble, the difference in using imperative programming as your entire paradigm, and functional programming, is not the existence of imperative effects. All programs need to eventually cause an effect, they have to cease being a description, a seed, and begin living. The real difference is functional programming tends to put off committing to the how until its absolutely time to do so, at the edges of a program
The use of atoms on its own could be considered an anti-pattern, especially for a backend service
Kind of feel like too many for me. But like, nothing is black and white. So each use needs to be considered and might have good reasons.
But in theory, an atom only stores values that changes over time. And 99% of the time they are used its to model that.
Now the thing is, most modern backend does not maintain domain state in memory. It uses one or more database. Which is why you'll find plenty of code bases with zero atoms in them.
And even if you were to keep domain state in memory. A common pattern in Clojure is to use an in-memory database. So you'd model your app the same as if it was using a real DB. Except the DB might just be a big map inside a single atom. And you'd query/write/uodate that single atom.
And definitely functions should be get as pure as possible, the bare minimum means they shouldn't be making us of global variables. If they need some resource it should be injected into them as an input.
Is it possible to use with-redefs
with Java? For example:
(with-redefs [LocalDate/now #(LocalDate/of 2020 05 25)]
something like that
> Unable to resolve var: LocalDate/now in this context
I'd say that's the best is to wrap the interop in Clojure fns and vars and then you'd mock the wrapper instead.
Otherwise, it is possible for you to use the same mocking libs as Java does, like Mockito
That said, LocalDate/Now is a static, and statics are hard to mock in Java. So I really think you're better of wrapping it. But one could use PowerMockito, but it does all kind of bytecode wrangling I believe not sure it's REPL friendly
@kwladyka in that particular case, I prefer to use (LocalDate/now clock)
signature and pass the clock as argument, then you can write a MockClock for your tests (Clock is a abstract class, so you will use proxy
for that)
@kwladyka Which time libraries do you think are not mature?
tick has not updated documentation and in the newest versions things not work
clj-time is deprecated, but mature for sure
clojure.java-time is quite ok, but because of the not common way how it is coded editor can’t help me figure out which fn I can use when I am typing java-time/
and not common ns java-time
luck of some fn which I need
just I didn’t find this libs make my life easier. It is simpler for me to use Java interop, than this libraries
Using Java interop is just fine -- Clojure is designed as a hosted language for good reasons.
We use clojure.java-time
heavily in production at work so it's definitely mature/solid, but it's a thin wrapper so it doesn't improve over bare interop except in two cases: 1) it tries to auto-convert between date/time types as needed which can help you avoid quite a bit of boilerplate when you need to go back and forth between different types and 2) complex date/time "arithmetic" looks much nicer that bare interop.
Which editor are you using that won't give you code assist/completion with clojure.java-time
? I get full auto-complete and inline docs for it in Atom/Chlorine (without even using Compliment)
If its cursive: I have seen this, I think clojure.java-time
uses potemkin’s import-vars, which cursives static analysis doesn’t understand.
Ah, a time when dynamic code assist via a connected REPL does a better job than static analysis 🙂
I use Atom with Chlorine and Cognitect's REBL. I've done some YouTube videos of how I work with that -- and I have a atom-chlorine-setup GitHub repo if you're interested
I used Emacs for years before I switched to Atom 🙂
I'm on my phone but I think this link should work https://youtube.com/watch?v=ZhzMoEz4j1k
@kwladyka They're fairly similar in many ways but VS Code is much harder to customize at startup because it lacks an "init" file that can contain code. Atom lets you write an init.coffee
file (containing CoffeeScript) so you can extend the editor in interesting ways.
Chlorine now has a config file, written in ClojureScript, that is processed via sci
(Small Clojure Interpreter) so you can do a number of extensions to Chlorine by writing that chlorine-config.cljs
file. There's a variant of Chlorine for VS Code, but I don't know if it has the cljs extension stuff added yet.
I was trying VSC last year, but Cursive gives me much better experience. Why did you decide to use atom instead of VSC?
I am using VSC as a second editor to Intellij which is lighter to other things, than Clojure
Just thinking if make sense to try atom instead of VSC? Can it make any benefits for me?
As I said above, Atom lets you write additional functionality in CoffeeScript that is loaded at startup -- adding new commands on the fly and extending the editor. You can't do that with VS Code. All of the REBL integration I use on a day-to-day basis was originally added that way.
Recently, I moved all of my REBL integration into Chlorine's cljs config system -- which is still something you can't do with VS Code.
If you've watched my videos, you'll see how much I use REBL. If Clover (the VS Code version of Chlorine) is ever able to add commands at startup with keys bound to those additional commands, I might switch to VS Code. But the author (of both Clover and Chlorine) says that it doesn't look possible with VS Code, only with Atom.
Consider sending the value for now as a parameter. That way your function becomes purer and needs less mocking.
yes, I will split the code. Just I don’t like when I have to make code more complex, because of tests but sometimes I need to 🙂