Fork me on GitHub
#clojure
<
2018-05-03
>
wei00:05:46

how would you translate this Java into Clojure?

BigQueryOptions.builder().projectId(projectId);

wei00:05:59

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();
  }
}

wei00:05:28

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)

wei00:05:57

the Java code works but my Clojure version doesn't

hiredman00:05:59

maybe try type hinting

(.projectId ^BigQueryOptions$Builder (BigQueryOptions/builder) "x")

seancorfield00:05:21

Wouldn't that be BigQueryOptions$Builder?

seancorfield00:05:04

@wei What's the dependency so we can try this out in a REPL?

hiredman01:05:34

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

hiredman01:05:11

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

seancorfield01:05:14

Hmm, I tried (BigQueryOptions/builder) and got NoSuchFieldException...

seancorfield01:05:56

OK, so builder was deprecated, then removed(?), and there's a newBuilder method instead?

seancorfield01:05:09

I got this to work @wei but I think it depends on your versions:

(.setProjectId (BigQueryOptions/newBuilder) "x")

emccue01:05:36

@qqq Let me meme your problem and say "why not rust?"

emccue01:05:55

if you come to the conclusion that you really need the speed and sharing code with the frontend

emccue01:05:07

I know far less than I really should about VBOs and Open/Web GL in gneneral

emccue01:05:35

can you explain more about the bottleneck?

emccue01:05:34

Both for my education and for some second reason that makes it worth it for you

emccue02:05:03

(though yeah, I kinda wonder why this is such a bottleneck

alexandergunnarson02:05:07

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)

Alex Miller (Clojure team)02:05:00

you can’t unless you actually implement your own custom spec impl (which I would not recommend)

alexandergunnarson02:05:32

Heh I was already doing just that, actually, and about to test it out...

alexandergunnarson02:05:37

Why would you not recommend it @alexmiller?

alexandergunnarson03:05:36

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}))

Alex Miller (Clojure team)04:05:41

spec is alpha because the internal spec protocol is likely to change, and then your code will break :)

Alex Miller (Clojure team)04:05:38

generally I feel when I’m fighting an api that hard, it’s a good sign I’m going in the wrong direction

alexandergunnarson04:05:11

Ah okay, makes sense 🙂 Yes, I generally agree

alexandergunnarson04:05:57

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!)

mkvlr08:05:02

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…

schmee08:05:53

if you need that you can make your own version of assert

schmee08:05:52

the likelihood that the type thrown by assert will change is slim to none

Bravi09:05:52

is there a list of all protocols somewhere?

WhoNeedszZz09:05:09

That's incredibly vague

mkvlr10:05:51

@schmee yeah, I meant changing it in our code of course, not changing Clojure which won’t and shouldn’t happen.

WhoNeedszZz10:05:11

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

plexus10:05:03

@whoneedszzz sounds like an announcement to me, maybe drop it in #announcements

WhoNeedszZz10:05:33

I was curious about that, but the channel purpose didn't include that kind of thing

WhoNeedszZz10:05:12

And half of the number of people in here

Qambar10:05:40

hi, whats the best way to check if any element in the list [1 2 nil 3] is nil and return nil?

dominicm10:05:34

What do you want to do if none of them are nil?

Qambar10:05:13

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.

dominicm10:05:32

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 thought

👍 4
dominicm10:05:00

doing s/and/when makes it do what you want

dominicm10:05:07

user=> (when (every? some? x) x)
nil
user=> (when (every? some? y) y)
[1 2 3 4]
A little better

dominicm10:05:16

I suppose not-any? would be quicker for longer sequences

WhoNeedszZz10:05:15

#beginners

👎 8
✔️ 4
WhoNeedszZz10:05:15

You can thumb down my response all you want, but we have channels for a reason

WhoNeedszZz10:05:56

Purpose of #beginners - "Getting started with Clojure/ClojureScript? This is the place to ask questions!"

plexus10:05:06

sorry @whoneedszzz but you're being rude

👍 16
WhoNeedszZz10:05:40

I'm sorry, but how is simply referencing a channel that has the explicit purpose of the person's question rude?

WhoNeedszZz10:05:06

I would say asking a beginner question when there is an explicit channel that everyone is added to when they join is rude

Qambar10:05:21

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

WhoNeedszZz10:05:57

Which is a beginner question

plexus10:05:35

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.

blueberry16:05:22

@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.

👍 4
WhoNeedszZz10:05:59

That implication is nowhere in what I said

robert-stuttaford10:05:02

@whoneedszzz compare ‘great question, by the way, we have a #beginners channel too’ vs ‘#beginners’.

WhoNeedszZz10:05:24

I'm sorry I didn't sugar coat it for you guys

robert-stuttaford10:05:35

apology accepted 🙂

WhoNeedszZz10:05:12

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.

robert-stuttaford10:05:40

it’s done. let’s carry on with our day, shall we? 🙂

Qambar10:05:51

i forgive you @whoneedszzz, you have a good heart and your i value your intention 🙂

WhoNeedszZz10:05:31

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.

👍 8
borkdude11:05:03

Opened the #graalvm channel, seems an exciting enabling tech for clojure applications

Bravi11:05:36

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?

jeff.terrell12:05:34

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

jeff.terrell12:05:39

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.

Alex Miller (Clojure team)12:05:00

We started working on this stuff 2 yrs ago

Alex Miller (Clojure team)12:05:01

clj is the first step but lots of things to do there

kirill.salykin12:05:38

is it possible to share what else you plan to do?

Alex Miller (Clojure team)12:05:08

I don’t have anything concrete to share - we’ve talked broadly about a lot of different stuff

sveri12:05:08

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?

jgh12:05:51

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.

jgh12:05:33

and inevitably there will be people for whom that is a breaking change

jgh12:05:13

but is it reasonable to cater to people who ignore a deprecation for the better part of a decade?

sveri12:05:12

How about the fact you can still compile java code from the 90ies. Isnt that some kind of a "never-introduce-a-breaking-change"?

mfikes12:05:13

Even then, there is code from the 90s that will no longer compile, but those corner cases are extremely rare.

jgh12:05:52

im not that familiar with java tbh

sveri12:05:38

@mfikes I suspect that code uses part of the unoffical API like com.sun... stuff?

sveri12:05:01

@jgh You can do that, it's an explicit goal of the JDK and still is.

WhoNeedszZz12:05:27

Yet Java 9 broke a lot of code

WhoNeedszZz12:05:40

For the better to be fair, but still

mfikes12:05:06

@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.

mfikes12:05:54

Having said that, Java is an excellent example of the concept of going out of your way to avoid breaking code.

☝️ 4
mfikes13:05:17

One example is that Java will deprecate things in the JDK, but never actually remove them.

mfikes13:05:03

At the other end of the spectrum seems to be React, IMHO.

mpenet13:05:16

or elasticsearch dsl

tbaldridge15:05:01

+1000 to that. Most of my day is spent working in the ES dsl, it's a nightmare of a language 😄

dominicm13:05:33

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.

mpenet13:05:50

they move fast, they break things and leave skid marks everywhere

😂 4
mfikes13:05:26

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.

dominicm13:05:41

That's interesting.

Alex Miller (Clojure team)13:05:03

if you remove things you can create a new version of the namespace and leave the old one

Alex Miller (Clojure team)13:05:13

voila, no breaking change

Alex Miller (Clojure team)13:05:28

consumers switch to the new ns when they want to

dominicm13:05:15

I think there's some aesthetic displeasure with bidi.bidi2 vs bidi.bidi. It's also easy to forget which is the "correct" one.

Alex Miller (Clojure team)13:05:28

I think Rich would ask you to weigh those aesthetics with upgrading and having your code break

dominicm13:05:46

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.

Alex Miller (Clojure team)13:05:36

this is definitely a change from “business as usual” but Rich never has much use for that :)

sveri13:05:23

@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.

Alex Miller (Clojure team)13:05:22

I don’t think I agree with everything you just said

mpenet13:05:35

require :as makes all this moot no?

mfikes13:05:41

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.

tbaldridge15:05:05

Windows as well, CreateWindowEx vs CreateWindow

Alex Miller (Clojure team)13:05:45

There is a cost factor for sure

Alex Miller (Clojure team)13:05:02

Eclipse did this too iirc

mpenet13:05:32

one could argue simple ways to "copy"/extend ns'es for new versions could be a solution

Alex Miller (Clojure team)13:05:58

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)

mpenet13:05:47

I guess immutable namespaces/modules would make sense in that context

mfikes13:05:24

By the time you get to IHTMLDocument7 you just accept it as “the way things are done” in the name of not breaking anything.

mfikes13:05:17

COM also has this interesting pattern where the symbols you see aren’t actually the interface (the real interface “name” is a UUID)

dpsutton13:05:46

that's a pain getting a C# project to reference a COM object

dpsutton13:05:01

i think there's interface generation and it uses the uuid to find the implementation

dpsutton13:05:17

(if we're talking about the same thing)

mfikes13:05:21

Right, and in languages like that you end up using IDispatch right?

dpsutton13:05:46

i don't remember using that interface but that doesn't mean it wasn't there 🙂

mfikes13:05:26

Anyway, COM, to me is a great example of this pattern where interfaces are so immutable, they are simply named by UUIDs.

tbaldridge15:05:36

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.

tbaldridge15:05:11

Purely functional languages seem a lot easier to keep backwards-compatible. In-memory state may be a bit harder.

dominicm15:05:08

Does spec make that atom accessible? I thought there was a "getter" for it.

tbaldridge15:05:23

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.

tbaldridge15:05:37

Data translation layers are often lossy, unless there's a lot of work put into it

sveri15:05:33

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.

dominicm15:05:24

@tbaldridge what kind of changes are in spec2 I guess is the question.

tbaldridge15:05:45

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.

tbaldridge15:05:04

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.

danm15:05:24

Yeah, we had that problem

tbaldridge15:05:28

The opposite is Python that namespaced the old and new API into Python3 and Python2. And therefore forked the community

danm15:05:47

We tended to release a new version, deprecate the old one, then remove it after enough users had migrated across

tbaldridge15:05:06

"People can still use Python2, we didn't remove anything" and 10 years later they still use Python2

Alex Miller (Clojure team)15:05:16

I’m not suggesting that all problems are easy to solve, but many (most?) breaking api changes don’t need to be

Alex Miller (Clojure team)15:05:21

in the greater software world

Alex Miller (Clojure team)15:05:31

there are plenty of gray areas

danm15:05:56

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

noisesmith15:05:40

related to this discussion is Hyrum's law, which I belatedly learned of just today (though I had similar thoughts) http://www.hyrumslaw.com/

danm15:05:08

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

4
orestis15:05:01

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.

orestis15:05:16

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.

orestis15:05:57

(This probably belongs to #off-topic)

captainlexington15:05:31

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

ajs15:05:00

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

orestis15:05:32

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.

noisesmith15:05:28

alternatively, you end up with eg. rust?

mikerod16:05:44

@U7PBP4UVA the problem I often see come from this eventually, on the JVM (but other things too), is the whole “jar hell” thing

mikerod16:05:06

Eventually you wind up with some library that transitively uses the older version of a lib that had deprecated stuff that was now removed

mikerod16:05:31

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

mikerod16:05:46

and you can’t pick either version, because both will break the other part of your dep tree

mikerod16:05:04

I think the way dependencies all being merged together on a big single path just sucks really 😛

ajs17:05:34

@U051SS2EU I assume you are not a fan of rust?

noisesmith17:05:09

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

ajs17:05:47

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

noisesmith17:05:37

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

donaldball15:05:43

Another substantive problem with strong backwards compatibility is that it’s not always clear what to do about legitimate bugs in your api.

orestis15:05:18

Deprecate the old API, introduce the new one?

orestis15:05:11

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.

ajs15:05:00

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

orestis15:05:02

(I think the main reason is that they wanted to simplify the internals, and doing this would instead add complexity)

Alex Miller (Clojure team)15:05:14

Clojure has the benefit of being a far simpler language than most

Alex Miller (Clojure team)15:05:36

so it’s very rarely necessary to make a breaking change at the language level

danm15:05:08

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

4
ghaskins18:05:22

hi all, im struggling with something that is likely very basic

ghaskins18:05:02

i have plenty of times used clojure+stringtemplate to have a clojure program generate a non-clojure program (e.g. golang)

ghaskins18:05:10

but now I am needing to generate a clojure program

ghaskins18:05:34

so my instinct was to use clojure directly and then pprint+code-dispatch

ghaskins18:05:39

but its not going great….

ghaskins18:05:08

before I give up and just resort to what I know (clojure+stringtemplate), I figured id ask the experts

ghaskins18:05:59

I know just enough macro/quote/unquote stuff to be dangerous, heh

ghaskins18:05:18

but it seems like this should be something very easy to do, but im struggling

ghaskins18:05:20

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

ghaskins18:05:13

so, my tool needs to take that AST and emit a fully fledged .clj, decorated with

(ns) (defn) (defn) .. (defn)

ghaskins18:05:12

the problem I am finding is when I take the obvious (to me) solutions, I end up some seemingly ugly places

ghaskins18:05:22

for instance, I can do something like

(let [ns (symbol "foo.bar.baz")] `[(ns ~ns) (defn ...) (defn ...)])

ghaskins18:05:02

and run that through pprint…the (ns form looks good, but all my other symbols are namespace qualified

ghaskins18:05:39

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)

noisesmith18:05:51

@ghaskins ` always namespaces things, you can use a hack to prevent it though

ghaskins18:05:02

im not quite sure what to do, other than punt and go back to stringtemplate

noisesmith18:05:19

~'foo produces foo, without namespace qualification

ghaskins18:05:32

right, understood..i know its supposed to

ghaskins18:05:40

just not sure of “best practices” here

ghaskins18:05:53

id be curious to hear about the hack you mentioned

noisesmith18:05:04

if you need to create a non-qualified symbol, use ~'foo

noisesmith18:05:43

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

ghaskins18:05:27

right, i think I get why it does what it does in a typical macro scenario

ghaskins18:05:47

in this case, the code is being emitted as EDN and compiled elsewhere, so its the wrong thing in this application

noisesmith18:05:53

https://gist.github.com/nimaai/2f98cc421c9a51930e16#variable-capture - reasonable common lisp explanation of the problem that transfers

ghaskins18:05:18

it sounds like the only option is to explicitly undecorate each symbol?

ghaskins18:05:32

or are you saying the unquote-quote hack can be applied globally?

ghaskins18:05:20

perhaps I can use in-ns

noisesmith18:05:43

in-ns doesn't prevent the namespacing behavior of `

ghaskins18:05:50

yeah, i see that now

noisesmith18:05:16

you can gensym via foo#

noisesmith18:05:34

two references to foo# in the same quasiquote refer to the same gensym

ghaskins18:05:37

yeah, gensym isnt my problem though, its purely a ns issue

noisesmith18:05:45

no, foo# fixes it

ghaskins18:05:52

unless I am misunderstanding what you mean

ghaskins18:05:54

which is likely, heh

noisesmith18:05:19

so if it's a local generated inside the block, use foo#, if it's inside the same ns, in-ns fixes it

ghaskins18:05:23

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))])))

noisesmith18:05:27

so the two together combine to do the right thing

ghaskins18:05:48

so I hand-decorated each symbol with the unquote-quote hack

noisesmith18:05:51

so change let bindings and arg vectors to use foo#

ghaskins18:05:56

which does seem to work, but its kinda awkward

noisesmith18:05:01

and def references can stay

ghaskins18:05:54

the gensym doesnt work if I use plain quoting though

ghaskins18:05:02

unless I flubbed that experiment

noisesmith18:05:10

no, don't mix those for the same symbol

ghaskins18:05:18

not following, sorry

ghaskins18:05:22

you mean using ‘ns#’ gensym?

ghaskins18:05:24

or something else?

noisesmith18:05:01

(in-ns 'target.ns)

`(defn ~'foo [arg#] (map inc arg#))
`(defn ~'bar [] (let [baz# (foo ...)] (concat baz# baz#))

noisesmith18:05:42

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

ghaskins18:05:06

right, ok…i think I understand all that

ghaskins18:05:23

i was just trying to avoid having to hand-tune each symbol with the ~'

ghaskins18:05:42

but it sounds like, I either chose to do that, or I dont use EDN

noisesmith18:05:42

right, you only need ~' for def or defn first arg, never inside the body

ghaskins18:05:55

right, ok, thats good to know its scope limited

ghaskins18:05:06

now I understand what you meant by def/defn comment

ghaskins18:05:28

ok, i appreciate your time

micahasmith21:05:34

why does clojure.core use assert-args instead of :pre?

Alex Miller (Clojure team)22:05:44

preconditions can be turned off (they’re backed via assert). assert-args is always checked.

qqq21:05:53

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

Alex Miller (Clojure team)22:05:29

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

val_waeselynck21:05:46

@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

qqq21:05:08

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

tbaldridge21:05:51

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.

tbaldridge21:05:00

Fixed the majority of issues I've had with this.

hiredman21:05:34

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

tbaldridge21:05:58

right, so not holding onto the stuff helps as well

hiredman21:05:28

or having a systematic way to tear down and reconstruct stuff

tbaldridge21:05:41

And to that end it's often better to construct objects through a namespace that gets reloaded with the protocol

hiredman21:05:57

which is why you see the combination of tools.namespace's reloading stuff and component

qqq21:05:53

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 ?

tbaldridge21:05:39

heh, message passing

tbaldridge21:05:48

same way Erlang does it, same way Python does it, etc.

hiredman21:05:52

smalltalk has a hierarchy

hiredman21:05:21

so messages effectively traverse up the hierarchy until something can handle it

qqq21:05:43

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.)

tbaldridge21:05:22

"a bit slower" is probably an understatement. If you do that, just use multi-methods and hashmaps

qqq21:05:13

aren't there parts of defmethod/defmulti that can not be reloaded without repl restart ?

tbaldridge21:05:39

the dispatch method is defonce, or you can do (def multimethod-name nil) and that resets it

tbaldridge21:05:52

but its pretty rare that you need to change the dispatch mechanics of a multimethod

tbaldridge21:05:29

alternatively you can make the multimethod call through a var:

(defn decide [x]
  (:type x))

(defmulti do-stuff #'decide)

hiredman21:05:02

I guess the biggest issue with mutating protocols in place is protocols rely on java interfaces for a fast path, and those are not mutable