This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-05-03
Channels
- # boot-dev (1)
- # cider (27)
- # cljsjs (6)
- # cljsrn (11)
- # clojure (249)
- # clojure-dusseldorf (1)
- # clojure-finland (1)
- # clojure-greece (1)
- # clojure-italy (28)
- # clojure-nl (12)
- # clojure-russia (2)
- # clojure-spec (5)
- # clojure-uk (27)
- # clojurescript (24)
- # clojutre (2)
- # component (8)
- # cryogen (1)
- # cursive (7)
- # datomic (61)
- # editors (18)
- # emacs (1)
- # events (1)
- # figwheel (4)
- # fulcro (35)
- # graphql (4)
- # jobs (3)
- # jobs-rus (1)
- # keechma (1)
- # leiningen (1)
- # london-clojurians (1)
- # luminus (62)
- # off-topic (154)
- # onyx (23)
- # pedestal (43)
- # portkey (66)
- # re-frame (49)
- # reagent (23)
- # shadow-cljs (92)
- # tools-deps (113)
- # uncomplicate (2)
how would you translate this Java into Clojure?
BigQueryOptions.builder().projectId(projectId);
context:
import com.google.cloud.bigquery.BigQueryOptions;
public class BigQueryOptionsFactory {
public static BigQueryOptions create(String projectId) {
BigQueryOptions.Builder builder = BigQueryOptions.builder().projectId(projectId);
return builder.build();
}
}
I'm getting this exception on my attempt:
=> (.. (BigQueryOptions/builder) (projectId "x"))
IllegalArgumentException Can't call public method of non-public class: public com.google.cloud.ServiceOptions$Builder com.google.cloud.ServiceOptions$Builder.projectId(java.lang.String) clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)
maybe try type hinting
(.projectId ^BigQueryOptions$Builder (BigQueryOptions/builder) "x")
Wouldn't that be BigQueryOptions$Builder
?
@wei What's the dependency so we can try this out in a REPL?
it sounds like java reflection isn't entirely doing the right thing there, and in clojure if you don't specify enough information for the compiler to exactly invoke a method you generally are invoking it via reflection
it may or may not work if you avoid invoking it via reflection, because compiling it without reflection involves some reflection at compile time to find the right method
Hmm, I tried (BigQueryOptions/builder)
and got NoSuchFieldException...
OK, so builder
was deprecated, then removed(?), and there's a newBuilder
method instead?
I got this to work @wei but I think it depends on your versions:
(.setProjectId (BigQueryOptions/newBuilder) "x")
if you come to the conclusion that you really need the speed and sharing code with the frontend
Question — say I want to do something like this:
(s/explain
(s/and map? (let [spec (s/and odd? #(> % 1))]
(fn destructuring-spec [{:keys [x]}] (s/valid? spec x))))
{:x 1})
How do I compose these specs such that #(> % 1)
is shown as the failing spec instead of all of destructuring-spec
shown as the failing spec? (Short of throwing an exception within destructuring-spec
via e.g. s/assert
which still doesn't compose)you can’t unless you actually implement your own custom spec impl (which I would not recommend)
Heh I was already doing just that, actually, and about to test it out...
Why would you not recommend it @alexmiller?
I think actually one thing I was looking for was:
(let [spec (s/and odd? #(> % 1))]
(s/explain
(s/and map? (s/nonconforming (s/and (s/conformer (fn [{:keys [x]}] x)) spec)))
{:x 1}))
spec is alpha because the internal spec protocol is likely to change, and then your code will break :)
generally I feel when I’m fighting an api that hard, it’s a good sign I’m going in the wrong direction
Ah okay, makes sense 🙂 Yes, I generally agree
But sometimes it's just a matter of the API not offering the desired feature (don't get me wrong — clojure.spec offers an amazing amount for which I'm incredibly grateful!)
how do people feel about changing clojure’s assert to throw an IllegalArgumentException? We don’t want to catch all Errors in our code and removing asserts in production also doesn’t seem helpful as problems will only be caught later, making it harder to debug…
That's incredibly vague
@schmee yeah, I meant changing it in our code of course, not changing Clojure which won’t and shouldn’t happen.
Not sure of the appropriate channel for it, but - I created a channel for seeking help on side projects (non-paid) or offering help. Help your fellow person! #side-project-help
@whoneedszzz sounds like an announcement to me, maybe drop it in #announcements
I was curious about that, but the channel purpose didn't include that kind of thing
And half of the number of people in here
hi, whats the best way to check if any element in the list [1 2 nil 3] is nil and return nil?
so i tried this
(some nil? [1 2 3 nil])
which returns true if any element in the list is nil, i want a list when none of them are nil but a nil when its a nil. The problem is that i was looking for a one-liner to do it, i can easily do it as a separate function with when/if logic.
user=> (and (not (seq (filter nil? x))) x)
false
user=> (and (not (seq (filter nil? y))) y)
[1 2 3 4]
The best I came up with without much thoughtuser=> (when (every? some? x) x)
nil
user=> (when (every? some? y) y)
[1 2 3 4]
A little betterthanks @U09LZR36F
You can thumb down my response all you want, but we have channels for a reason
Purpose of #beginners - "Getting started with Clojure/ClojureScript? This is the place to ask questions!"
I'm sorry, but how is simply referencing a channel that has the explicit purpose of the person's question rude?
I would say asking a beginner question when there is an explicit channel that everyone is added to when they join is rude
i think there was an assumption made about the question, i am not asking just how to do it, i was trying to figure out if there are any best practices to achieve it
Which is a beginner question
it doesn't say anywhere that questions need to be "pro" enough to be allowed in this channel. You also could have said "hi <person>, you might have better luck asking this in #beginners". The way you put this here seems to imply you're only welcome here from a certain level up, which is elitist and exclusionary.
@U07FP7QJ0 @U0509NKGK While I agree with you that more politeness cannot hurt, I think that being too eager to point every slight transgression does not help much.
That implication is nowhere in what I said
@whoneedszzz compare ‘great question, by the way, we have a #beginners channel too’ vs ‘#beginners’.
I'm sorry I didn't sugar coat it for you guys
apology accepted 🙂
Don't mind the 12k+ people in here that are now subjected to this discussion that would have been avoided if the person used the channels as they were intended. Not like we don't have a channel for just about everything here.
it’s done. let’s carry on with our day, shall we? 🙂
i forgive you @whoneedszzz, you have a good heart and your i value your intention 🙂
We have channels so topics don't get washed out as easily given the amount of people and discussions had on a daily basis. Just looking out for the people that only are in certain channels for specific reasons.
Opened the #graalvm channel, seems an exciting enabling tech for clojure applications
is it possible to have 2 projects in a single repository? what I mean by that is - my website has an admin panel and a normal, public view. I’d like to have a single repo - my-website
for example and within my src
folder, I’d like to have 2 folders - i.e. admin
and public
. And whenever I start my figwheel, I want to be able to switch between the source paths within figwheel repl. so something like (load-project "admin")
or something along those lines.
I’ve seen a repo that had this, somewhere. But I can’t find it anymore and perhaps someone here knows how do achieve this?
I found this exciting, from the interview of Rich by Joy Clark posted today in #announcements: > Rich Hickey: I think that the problem of dependencies is a big problem; it's one of the unsolved problems right now for programming, and I've been thinking a lot about it. This [new dependency stuff in 1.9] is on the path towards that. http://www.case-podcast.org/20-problem-solving-and-clojure-19-with-rich-hickey/transcript
That's not exactly a new idea, that RIch would be working on dependencies, but it sunk in to me that we might see more innovation from him in the dependency space.
We started working on this stuff 2 yrs ago
clj is the first step but lots of things to do there
is it possible to share what else you plan to do?
I don’t have anything concrete to share - we’ve talked broadly about a lot of different stuff
I like the idea of never ever introducing a breaking change. The question is about the range where that can be applied. A public interface? Or also to "internal" code? Or in the context of java classes even in private methods?
never introducing a breaking change sounds like an impossible feat for public interfaces. Obviously you can mark things deprecated for years (like Boost does) but eventually you need to prune those.
but is it reasonable to cater to people who ignore a deprecation for the better part of a decade?
How about the fact you can still compile java code from the 90ies. Isnt that some kind of a "never-introduce-a-breaking-change"?
Even then, there is code from the 90s that will no longer compile, but those corner cases are extremely rare.
Yet Java 9 broke a lot of code
For the better to be fair, but still
@sveri I think one case I vaguely recall had something to do with an anonymous class where something new was added or rearranged (i think involving Runnable
). Otherwise the code wasn't using private API. It was a very subtle corner case, and fairly easy to fix. But it served to me as a counter-example that Sun couldn't completely avoid breaking code.
Having said that, Java is an excellent example of the concept of going out of your way to avoid breaking code.
How so?
One example is that Java will deprecate things in the JDK, but never actually remove them.
+1000 to that. Most of my day is spent working in the ES dsl, it's a nightmare of a language 😄
You should be able to achieve the same functionality using the new & preferred method. I see no reason that doing a conversion internally (e.g. rewrite java.util.Date in terms of java.time) wouldn't allow you to do both quite easily. Changes probably aren't worthwhile if it's not worth having both the old & new interface.
The iOS story is a little odd too. Thing will generally still work at runtime because Apple conditionally honors what your code was built with. But, recompile, and wow, be ready to re-test.
if you remove things you can create a new version of the namespace and leave the old one
voila, no breaking change
consumers switch to the new ns when they want to
I think there's some aesthetic displeasure with bidi.bidi2 vs bidi.bidi. It's also easy to forget which is the "correct" one.
I think Rich would ask you to weigh those aesthetics with upgrading and having your code break
I personally agree with him, I get this sensation of pressure from the larger software community though. I think Rich sees a lot of issues in the software community though.
this is definitely a change from “business as usual” but Rich never has much use for that :)
@alexmiller That's the easy part. But what about later when you refactor stuff or fix a bug. You still carry around all your code you dont use anymore and have to make sure it still works. At least from a business point of view it does have a cost factor. As a user its desirable.
I don’t think I agree with everything you just said
I recall initially not liking the version suffixes on COM interfaces, like IHTMLDocument2
, but in the end, you can’t argue against the nice result.
Windows as well, CreateWindowEx vs CreateWindow
There is a cost factor for sure
Eclipse did this too iirc
one could argue simple ways to "copy"/extend ns'es for new versions could be a solution
I don’t think you need that. If you add something, add it. If you update an interface, do so as foo2 (then it’s just add again). Deleting something requires a new namespace (so you can’t copy/extend)
By the time you get to IHTMLDocument7
you just accept it as “the way things are done” in the name of not breaking anything.
COM also has this interesting pattern where the symbols you see aren’t actually the interface (the real interface “name” is a UUID)
i think there's interface generation and it uses the uuid to find the implementation
Anyway, COM, to me is a great example of this pattern where interfaces are so immutable, they are simply named by UUIDs.
What's interesting to me with the "always add, never change" public interfaces is the problem of internal state in a library. Take spec, for example. Specs are stored in a global atom. would .spec2 include a new atom? What happens if you try to combine specs from .spec1 and .spec2? If libraries would only ever use one or the other, how do I compose a library that uses .spec1 with one that uses .spec2.
Purely functional languages seem a lot easier to keep backwards-compatible. In-memory state may be a bit harder.
no, it's not public, but if what that atom contains needs to change in format, there'd need to be a translation layer into and out of that atom.
Data translation layers are often lossy, unless there's a lot of work put into it
The longer I think about it, the more I come to the conclusion that backwards compatibility is easy to achieve if you add stuff that does not require changes in your datamodell. Like in the product our team has been working on we hardly ever changed existing interfaces, but just added new ones. And for our data model we have migration steps. So you can still use the projects our customer created 7 years ago. I just realized that business rules is backwards compatible down to its first version. Ok, I agree, the cost must not be that high and disagree with what I said first.
@tbaldridge what kind of changes are in spec2 I guess is the question.
Yeah, additive changes are fairly easy in my experience. But it does lock you into a model where you can't truely change the old API.
A nasty side-effect of perfect backwards compatibility like the JVM has is that you can't add in truely new features (value types, generics, etc.) without breaking the old code.
The opposite is Python that namespaced the old and new API into Python3 and Python2. And therefore forked the community
We tended to release a new version, deprecate the old one, then remove it after enough users had migrated across
"People can still use Python2, we didn't remove anything" and 10 years later they still use Python2
I’m not suggesting that all problems are easy to solve, but many (most?) breaking api changes don’t need to be
in the greater software world
there are plenty of gray areas
Issue there is your codebase gets crufted up with all the stuff for the old API calls that you'd actually love to get rid of
related to this discussion is Hyrum's law, which I belatedly learned of just today (though I had similar thoughts) http://www.hyrumslaw.com/
Hah. This is exactly what we're suffering from. We're dividing our API up to have far more specialised systems with a much smaller number of clients instead
I was very heavily into Python at the time of the 2-3 migration. It involved some very hard choices from the maintainers that in the end didn’t play out as I would hope.
It’s not that they changed the language — they created a new interpreter, that involved changing systems and that is hard for a language that is at the core of many popular OSes.
In Rich's talk about this he said sometimes, if you really have to break the API, leave the old thing and make a new thing with a new name. I think that probably should have happened with Python2 and 3
Most criticisms against the C++ language strongly wish that it was comfortable with breaking backwards compatibility, so very old idioms could be completely removed to make the language better
I think most people are comfortable with the usual Deprecate — Hide under a flag — Remove cycle, as long as you give them enough time and control.
alternatively, you end up with eg. rust?
@U7PBP4UVA the problem I often see come from this eventually, on the JVM (but other things too), is the whole “jar hell” thing
Eventually you wind up with some library that transitively uses the older version of a lib that had deprecated stuff that was now removed
and you, or other libs you rely on, want to use that newer version where it is now removed. So now you have a clash
and you can’t pick either version, because both will break the other part of your dep tree
I think the way dependencies all being merged together on a big single path just sucks really 😛
@U051SS2EU I assume you are not a fan of rust?
oh I like rust just fine - I'm just saying that what rust does is take the parts somebody liked of c++, throw away the rest, and give it a new name
But rust brings a lot of new stuff to the table that never existed in c++, its more than just throwing out old stuff. It's quite a different paradigm altogether with more in common with Haskell
it started as mozilla's best practices for c++ code, and the history I read described the haskell features as official enforcing on a language level of behaviors that were previously coding requirements
Another substantive problem with strong backwards compatibility is that it’s not always clear what to do about legitimate bugs in your api.
Python had a fantastic from __future__ import blah
idea that allowed you to switch on interpreter features file-by-file. If they kept doing this there wouldn’t be a fork — but I’m sure they had their reasons.
Most criticisms against the C++ language strongly wish that it was comfortable with breaking backwards compatibility, so very old idioms could be completely removed to make the language better
(I think the main reason is that they wanted to simplify the internals, and doing this would instead add complexity)
Clojure has the benefit of being a far simpler language than most
so it’s very rarely necessary to make a breaking change at the language level
most of Clojure is api
Hah. This is exactly what we're suffering from. We're dividing our API up to have far more specialised systems with a much smaller number of clients instead
i have plenty of times used clojure+stringtemplate to have a clojure program generate a non-clojure program (e.g. golang)
before I give up and just resort to what I know (clojure+stringtemplate), I figured id ask the experts
in a nutshell, i have an AST that I need to use as the basis to emit a .clj file that will be compiled by someone elses program
so, my tool needs to take that AST and emit a fully fledged .clj, decorated with
(ns) (defn) (defn) .. (defn)
the problem I am finding is when I take the obvious (to me) solutions, I end up some seemingly ugly places
for instance, I can do something like
(let [ns (symbol "foo.bar.baz")] `[(ns ~ns) (defn ...) (defn ...)])
and run that through pprint…the (ns form looks good, but all my other symbols are namespace qualified
alternatively, I can just quote the vector, and all my symbols remain undecorated (as I want) but I cant easily parameterize the fields, like (ns)
@ghaskins ` always namespaces things, you can use a hack to prevent it though
~'foo produces foo, without namespace qualification
if you need to create a non-qualified symbol, use ~'foo
that's cleaner than using a string template to generate code - just be aware of why the namespacing behaviour is a default and the kind of error it prevents
in this case, the code is being emitted as EDN and compiled elsewhere, so its the wrong thing in this application
https://gist.github.com/nimaai/2f98cc421c9a51930e16#variable-capture - reasonable common lisp explanation of the problem that transfers
in-ns doesn't prevent the namespacing behavior of `
you can gensym via foo#
two references to foo#
in the same quasiquote refer to the same gensym
no, foo# fixes it
so if it's a local generated inside the block, use foo#, if it's inside the same ns, in-ns fixes it
so, this is what I have rightn now
(defn- pprint-code [code]
(let [buf (with-out-str
(write code :dispatch code-dispatch))]
(subs buf 1 (- (.length buf) 1))))
;; Generate the file contents for the input .proto file
(defn- generate-file-content [protos file]
(let [ns (-> (generate-ns protos file)
symbol)]
(pprint-code
`[(ns ~ns
(:import (com.google.protobuf
CodedInputStream
WireFormat
UnknownFieldSet
ExtensionRegistry)))
(defn- ~'tag-map [f is]
(loop [acc {} tag (.readTag is)]
(if (pos? tag)
(let [[k v] (f tag)]
(recur (if (fn? v)
(update acc k v)
(assoc acc k v))
(.readTag is)))
acc)))
(defn- ~'parse-undefined [tag is]
(let [num (WireFormat/getTagFieldNumber tag)
type (WireFormat/getTagWireType tag)]
(case type
0 (.readInt64 is)
1 (.readFixed64 is)
2 (.readBytes is)
3 (.readGroup is num (UnknownFieldSet/newBuilder) (ExtensionRegistry/getEmptyRegistry))
4 nil
5 (.readFixed32 is))))
(defn- ~'parse-embedded [f is]
(let [len (.readRawVarint32 is)
lim (.pushLimit is len)]
(let [result (f is)]
(.popLimit is lim)
result)))
(defn- ~'parse [f input]
(-> input CodedInputStream/newInstance f))])))
so the two together combine to do the right thing
so change let bindings and arg vectors to use foo#
and def references can stay
no, don't mix those for the same symbol
(in-ns 'target.ns)
`(defn ~'foo [arg#] (map inc arg#))
`(defn ~'bar [] (let [baz# (foo ...)] (concat baz# baz#))
you only need ~' for arbitrary non-namespaced symbols used quoted outside binding blocks, and you can use sym# for everything created by binding blocks, and if you are in the right ns you don't need to quote usage of defs
right, you only need ~' for def or defn first arg, never inside the body
why does clojure.core use assert-args
instead of :pre
?
preconditions can be turned off (they’re backed via assert). assert-args is always checked.
I really don't like the way how: 1. defprotocol Foo 2. ... (reify Foo .... ) 3. reload namespace with 'defprotocol Foo' 4. not finding old impl due to new protocol works
I’ve mused for a while about whether you could hash the class bytes and detect whether the protocol actually changed and avoid reloading it
@qqq Yeah... best mitigation I've found is to declare the protocol in its own file, which won't get reloaded as long as it doesn't change
This might just be bad habit on my part. I often like to mix eval-last-sexp and eval-buffer (to make sure everything still works). IWhen I tried the one file protocol approach, when I adda new protocok, I would accidenlty eval-whole-buffer by accident, and now ALL objects are out of sync
Emacs needs some file tracking or something. I used to have this issue, but with cursive you can turn on "Load out-of-date file dependenceis transitively". Then you simply re-load a child buffer, and anything that's changed in that file's dep tree gets reloaded as well.
Fixed the majority of issues I've had with this.
if you have some object you are holding on to in the repl, just reloading everything in the correct order isn't enough to get you a new instance of that object with all the correct stuff done
right, so not holding onto the stuff helps as well
And to that end it's often better to construct objects through a namespace that gets reloaded with the protocol
which is why you see the combination of tools.namespace's reloading stuff and component
how does smalltalk handle this? in smalltalk, can you create an instance of a class, add a member function to the class, and have the PRE EXISTING object run the new code ?
heh, message passing
same way Erlang does it, same way Python does it, etc.
I kind of like this:
(defn meta-obj [kw]
({:foo (fn [this & args]
(println "foo 2"))}
kw))
(defn make-obj []
(let [st (atom nil)]
(fn [kw & args]
(apply (meta-obj kw) st args))))
(def x (make-obj))
(x :foo)
;; modify function above
(x :foo)
This is a bit slower due to extra layer of indirection -- but I can add new member functions to the meta-obj, and all existing objs will be able to use he new methods. (And if they are incompatible, they just throw exception and die.)"a bit slower" is probably an understatement. If you do that, just use multi-methods and hashmaps
the dispatch method is defonce, or you can do (def multimethod-name nil)
and that resets it
but its pretty rare that you need to change the dispatch mechanics of a multimethod
alternatively you can make the multimethod call through a var:
(defn decide [x]
(:type x))
(defmulti do-stuff #'decide)