This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-12-16
Channels
- # adventofcode (76)
- # aleph (1)
- # aws (2)
- # beginners (101)
- # boot (1)
- # boot-dev (1)
- # calva (25)
- # cider (12)
- # cljs-dev (29)
- # clojure (214)
- # clojure-europe (1)
- # clojure-nl (5)
- # clojure-spec (1)
- # clojure-uk (6)
- # clojurescript (155)
- # data-science (1)
- # datomic (48)
- # figwheel-main (5)
- # immutant (1)
- # leiningen (11)
- # nrepl (2)
- # off-topic (24)
- # pathom (2)
- # pedestal (2)
- # portkey (8)
- # protorepl (5)
- # re-frame (2)
- # rum (4)
- # shadow-cljs (21)
I am not aware of anything in the Clojure/Java compiler uses metadata on the function object for, but neither do I have exhaustive knowledge there. Yes, when you use defn to define a function and put metadata after 'defn' and before the symbol naming the function, the metadata goes on the Var created, not on the function object created.
like you said, I wonder if further attention will be given to these corner cases now. I’ve seen Rich talking quite a bit in #rebl about how to handle serializing functions to go between REBL and e.g. a CLJS or remote CLJ app
People like Rich, David Nolen and Mike Fikes know way more about the detailed differences between Clojure/Java and ClojureScript than I do, and the reasons for them, but my limited understanding is that some of them go pretty deep into the nature of the ClojureScript compiler and the target.
The #cljs-dev and #rebl channels are probably more target-rich environments for answering such questions.
just wanted to gather info on how metadata was handled in general before I started making noises about how it could behave in CLJS
I would be surprised if there are any significant differences in the handling of metadata on immutable Clojure collections between CLJ and CLJS -- the answers are probably pretty clear there. On vars and function objects, I dunno so much there.
I guess its not obvious to me why AFunction even implements IObj. But, since it also implements Comparator and Serializable, my guess is that it has to be related with the idea that functions can be serialized and sent over a wire, and thus compared for equality and thus also have meta.
I think in CLJS, Vars only exists at compile time, they are full on Clojure Vars, but get compiled into JS variables. Which gets rid of the metadata, since JS vars don't have meta.
serializing two functions with identical textual definitions over a wire, and expecting them to be = on the other end after being created independently in memory on the other side, either requires remembering/caching the source text and comparing those to each other, or solving the halting problem. The first is way more likely to happen in practice, but I don't know what plans people might have in mind there for REBL.
Even comparing the textual definitions of two functions and seeing they are the same doesn't really mean they are necessarily equal, if one has direct linking call to a second function that was redefined between the deserializing of the two identical source texts. i.e. (defn foo [x] (my-fn x)) deserialized at two different times, with a redefinition of my-fn in between them, should those be = or no?
(I am thinking in terms of a Clojure/Java interactive session here -- I don't know whether any of that applies in the CLJS world, since I haven't used it enough to be familiar with the operational environments possible)
Well, there is a field called methodImplCache lol, now I'm just speculating, but whats that for?
From a quick scan of the relevant Clojure/Java source mentioning it, it looks related to the implementation of protocols
i.e. speeding up protocol calls in the common case.
I have to admit, I wish Rich Hickey had a little bit more comments in his code 😋 Hard to figure out the intent of RestFn, AFunction, etc.
I guess RestFn is just the concrete implementation for all nornal Fns, why is it called Rest, I can't find another Fn concrete class, kinda confusing
That seems unlikely to happen, unless someone besides Rich Hickey writes such comments and publishes them separately. Someone years back did start work on a 'literate code' document for Clojure source code -- I don't know how far they got, or how accurate their comments might have been. Given current state of affairs, folks who have dug into implementation like Alex Miller, Nicola Mometto (bronsa here), and several others are the best options for learning such things.
Haha ya, I'm still hoping someone will drop a book eventually specifically about the Clojure/ClojureScript implementation.
Other possible sources of info, although perhaps more time consuming, would be some of the Clojure/conj talks over the years that focus in internal implementation details (check ClojureTV YouTube channel), and doing searches on such names in the Google Clojure group message archive.
The only thing that I see adding the passed in meta is a call to AFunction withMeta, whoch returns a new RestFn with the meta attached. But I think only PersistentList makes use of this.
But, I'm clearly wrong, since anonymous fn definitly support adding meta and return it
The interesting bit is that with-meta returns an AFunction, whereas an fn call returns the type of the fn class
You should not consider = to be a useful operation on functions
@alexmiller Any reason why functions implement IObj? Is there any particular use of meta on functions?
I believe it is sometimes used in the compiler to hold type tags
Right, so very limiter use for the compiler itself. I think its fair to say as a user, you should threat fns like they don't support meta.
Generally you should put them on vars or symbols
Or collections
hey folks, can i consider this (doall (map @profiles connections-ids))
under side effect of @profiles
?
I am not sure I understand your question. @profiles is the same as (deref profiles), and I am guessing from the context that profiles is an atom that you use to point to a function you want to call via map?
The expression @profiles does not cause any side effects by itself, only dereferences the atom profiles
(or whatever stateful thing profiles happens to be, if not an atom)
That deref operation will happen only 1 time while executing that expression, not once for each element of the sequence connection-ids
@andy.fingerhut profile
is an atom
Does profiles deref to a function, {map}, :keyword, or #{set}?
So with the comments I gave above, do you still have a question about 'side effects'? I don't see any side effects possible if profiles is an atom that points at a hash map.
@andy.fingerhut no, you've been very clear
sure, no problem.
you right @jayzawrotny, i just noticed that with your observation (= (hash-map) {})
(every? #(apply = %) [[(hash-map :a 1) {:a 1}]
[(hash-set :a :b) #{:a :b}]
[(list :a :b) '(:a :b)]
[(vector :a :b) [:a :b]]])
thank you @jayzawrotny 🙂
Of course, wanted to reinforce the relationship between constructor functions and literal syntax.
@jayzawrotny that was nice from you, every piece of information is important for me right now

(defn get-profile-connection-suggestion!
"Take an id and return a list of the connections suggestion"
[id]
(info "Calling get-profile-connection-suggestion! Suggestion list generation for ID: ", id)
(let [profile (get-profile! id)
connections-ids (get-profile-connections-ids profile)
ids-connection-of-connection (mapcat get-profile-connections-ids (map @profiles connections-ids))
all-possible-suggestions (map @profiles ids-connection-of-connection)
public-possible-suggestions (map :id (filter :visibility all-possible-suggestions))
final-suggestions-list (frequencies (remove (set (conj connections-ids id)) public-possible-suggestions))]
(doall (map (fn [[id count]]
(get-profile! id))
(sort-by val > final-suggestions-list)))))
(->> prof
:connections
(map @toy-data)
(mapcat get-profile-connections-ids)
(map @toy-data)
(filter :visibility)
(map :id)
(remove (set (conj (:connections prof) 2)))
(frequencies)
(sort-by val >)
(map (fn [[id count]]
(get @toy-data id))))
that helped @U0Q8JHZAM, thanks 🙂
> Programs are easier to understand/test when functional value calculation is independent of identity/value association.
(here, I never need to think about what happens if toy-data atom is swapped while your code is running, if you only deref once)
Generally nothing sticks out to me, I do think some of those can be grouped together into more specific functions though. As a practice, that can make the intention of the code more clear and help focus on the domain instead of the implementation.
i got it @jayzawrotny, i am always trying to do that
(defn get-profiles!
"Return all profiles"
[]
(info "Calling get-profiles! to get all profiles")
(-> profiles deref vals))
I believe no. I am having difficulty thinking of any use of deref that would cause a change in state of the system. I am guessing someone more awake than I am could come up with an example of that, but not if profiles is an atom pointing at an immutable value.
So does (rand)
, I think the ! is more often to denote writes
Like reset!, swap!, set!, and compare-and-set!
(rand) will return a new value each call
Same with (time)
All good, it was a pattern I didn’t understand until recently myself.
Another note is that something like swap! returns the value it was passed in, the ! helps show that it does more than what we get from the return value.
I have been fighting with a macro for the last hour, I am trying to generate a interface with the help of gen-interface
for a list of domain objects. Because I have some constraints I have to have a class file so that some bytecode scanner can inspect the generated interfaces and can find any annotations on them. But I cannot get for the life of me annotations to work within a macro which generates a list of interfaces:
(defmacro generate-interfaces []
(let [db-types (get-types)]
(into (for [{:type/keys [PascalCaseName] :as db-type} db-types]
(let [iname (with-meta (symbol (str interface-ns (name PascalCaseName)))
{Deprecated true})
imethods (mapv (fn [{:field/keys [PascalCaseName type]}]
[(with-meta (symbol (str "get" (name PascalCaseName)))
{Deprecated true
Retention RetentionPolicy/RUNTIME})
[]
(cond
(:type/enum type) (symbol "String")
(= (:type/nature type) :primitive) (symbol (str
"java.lang."
(name (:type/PascalCaseName type))))
:default (symbol (str
interface-ns
(name (:type/PascalCaseName type)))))])
(:field/_parent db-type))]
(println iname)
(println (meta iname))
`(gen-interface
:name ~iname
:methods ~imethods)
))
`(do))))
Note I am just playing around with the Deprecated macro for easy testing, anyone have any hints? And I have it working without my own macro btw like this:
(gen-interface
:name
"nl.avisi.atlascrm.domain.Foo"
:methods
[[^{java.lang.Deprecated true}
getId
[]
java.lang.Id]
[^{java.lang.Deprecated true}
getLabel
[]
java.lang.String]
[^{java.lang.Deprecated true}
getOrder
[]
java.lang.Float]])
This is one the generated class files:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package nl.avisi.atlascrm.domain;
public interface Foo {
String getType();
Section getSections();
}
No annotations 😞
And the little test example without my macro around it generates:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package nl.avisi.atlascrm.domain;
public interface Foo {
@Deprecated(true)
Id getId();
@Deprecated(true)
String getLabel();
@Deprecated(true)
Float getOrder();
}
@U060GQK8U perhaps have the (with-meta ..)
call in the output code of the macro instead of already on the symbol in the output of the macro:
(defmacro generate-interfaces []
(let [db-types ["Foo" "Bar"]
interface-ns "ns-prefix"]
(into []
(for [type-name db-types]
(let [iname# `(with-meta (symbol ~(str interface-ns "/" type-name))
{Deprecated true})]
(println iname#)
(println (meta iname#))
`(assoc {}
:meta (meta ~iname#)
:name ~iname#)
)))))
;; (let [res (generate-interfaces)]
;; (mapv (comp meta :name) res)) => [{java.lang.Deprecated true} {java.lang.Deprecated true}]
@U06D9RGQM That is good idea let me try that
Doesn't seem to work, I now even tried a simpler version:
(defmacro very-simple-test []
`(definterface
~(with-meta (symbol "Foo") {Retention RetentionPolicy/RUNTIME})))
(definterface ^{Retention RetentionPolicy/RUNTIME} Foo2)
(very-simple-test)
When I compile this Foo2
has the annotation and Foo
doesn'tOff the top of my head, no idea
File a jira if you don’t figure it out
I will thank you
I think the problem is symbols from macros lose their metadata, the definterface is downstream from that
Why does (seq nil) return nil but (str nil) return ""
Probably to prevent NPEs in platform code?
not sure if this is a reason, but string has variable arity, so what should (str "a" "b" nil "c")
return
nil pun it to ""
(assoc nil :a 1) works fine
There’s no correlation or relationship between seq and str
Seq returns either a seq view with at least one element or nil
yes i know but design wise i thought it is interesting thought that str does not nil pun
str is string concatenation and always returns a string
oh i see
concat
interesting
Nil punning is a collection thing. Strings are values, not collections.
In this case, the doc string for str
explicitly says what it does for nil, and that it concatenates.
They can produce a sequence view, but they’re not collections
We write this a lot (if (blank->nil label)
(a LOT)
Some apps may want to treat blank strings as nil, but that’s an app thing, not a Clojure thing
clojure.string/blank? exists to blur that line too
I see, so there isn't a touch point to do the nil punning, like there is for collections (seq)
By 'touch point' do you mean 'a function that can be overridden, or a configuration knob that can be changed, to modify the language's behavior'? Sorry if that phrase has a standard meaning I'm not aware of.
I don’t think they have any relation
Nil pinning is primarily about seqs, it’s not a general thing
No, i mean that when you work with collections, there is a thing you always want to do which is iterate them, and seq
kind of means "turn this thing into an interator"
It is true that many many Clojure functions call seq
, which does give them a common behavior in this regard.
When working with strings, the string just is a simple value and its done, there is no further common operation where clojure.core could do the nil pun
seq is very much by design, clojure.core data operations fit together brilliantly around it
Related is that collections are polymorphic on nil, but I’m not sure lispers would consider that to be the same thing as nil punning
"polymorphic on nil" not sure what you mean
You can invoke collection functions on nil
(assoc nil :a 10)
conj assoc get etc
Right, they lift nil to the empty value of "the thing you want it to be"
I would say all of the collection ops are polymorphic - they have different behavior based on type, and nil is one of those cases
So I don’t see that as special, just another case
Nil punning traditionally I believe refers to treating empty lists and nils the same
static public Associative assoc(Object coll, Object key, Object val){
if(coll == null)
return new PersistentArrayMap(new Object[]{key, val});
return ((Associative) coll).assoc(key, val);
}
Is there a better example than assoc to demonstrate this? Conj i guessAll of this is imo unrelated to strings at all
"It is called punning because you can use it to mean different things in different contexts. In Clojure, nil, as a value, is nearly void of meaning." (pulled from an article)
I don’t agree with a lot of stuff in that article
Ha ok
It seems to me that nil means empty in many cases, string being the exception, but i have not seen this idea from alex or rich
To be fair I didn't read all of the way down
Yeah, wouldn’t agree with that
nil is a case that is given utility in different polymorphic operations
Well we have Alex here, so what is the canonical meaning of nil?
What do you think about this idea of nil as monoid-zero https://www.reddit.com/r/Clojure/comments/8kg8pl/nil_punning/dz8t2d6/
I view it as I said above
Don’t try to assign it some special global meaning
The approach is entirely pragmatic
So then what the article was saying was in the right direction at least - that it depends on the context
"The places where Clojure does not do this [nil as mzero] seem to be the places where Clojure inherits the platform behavior for pragmatism and speed" is this accurate ?
I think it’s missing the point
Which is that by assigning a useful meaning to nil in polymorphic ops, we can write simpler more obvious code with fewer conditionals
I can examine a piece of code, analyze where nils may occur, and make my code do what I want, usually without any nil checks
Doesn't seem to work, I now even tried a simpler version:
(defmacro very-simple-test []
`(definterface
~(with-meta (symbol "Foo") {Retention RetentionPolicy/RUNTIME})))
(definterface ^{Retention RetentionPolicy/RUNTIME} Foo2)
(very-simple-test)
When I compile this Foo2
has the annotation and Foo
doesn't(+ 1 (bigint 1)) is polymorphic but (+ 1 nil) is NPE (why not ground nil to zero?)
Platform efficiency is a reason
@alexmiller Can you expand a bit on that last bit?
There are different “domains” and different choices made
same with (if (blank->nil label)); (if "") is true
I’m talking about collections primarily
Math is it’s own domain with its own set of choices
Yeah, I recognize I am trying to unify something that Rich probably specifically chose not to unify
Java interop is another, where npes are most commonly seen
yea java interop is imperative, nothing really to be done to avoide npe in imperative code
have to just be careful
I apply a different level of code reading with interop, and make more use of things like some->
I think the beauty of nil punning is that if you constrain yourself to the little Clojure data processing world of clojure.core on clojure data structures, NPEs simply never happen. When people talk about needing Maybe types on boundaries, they are often talking about interop and not data processing biz logic, a misunderstanding that causes lots of conflict
Yeah. I marvel at being able to write a chunk of code, look at it, and know it’s safe from npes, but also handles all the edge cases
Without special cases
Thanks for digging in with me Alex, I am going to clean this up and post it on r/clojure
I can even give you an example...
The new ex-triage code at https://github.com/clojure/clojure/blob/master/src/clj/clojure/main.clj#L193
Is a gnarly of bit of code handling a wide variety of optional and missing data
And it employs lots of Clojure tricks to make it both safe and concise
Destructuring, cond->, polymorphic nils, etc
What’s the process for creating a clojure lib README? Is there a recommended template somewhere?
I don't think there is a template that they all use. The 'contrib' libraries that are part of http://github.com/clojure/<libname> mostly use a common template that you could look at, but maybe not all of it would be relevant for your case.
What about an example of a .travis.yml that uses clojure.tools.deps and the clj cli?
Sorry, I do not have any examples of that handy.
That’s ok, I couldn’t find any in the wild either. Ended up adding a project.clj using the standard lein test route.
if you're using Aleph in production, we'd like to hear about it: https://github.com/ztellman/aleph/issues/450
hi, anyone else having problems with latest compojure-api lein template?
says no :main namespace specified when i do lein new compojure-api
I’m writing a deftype
which implements an interface which contains overloaded methods. I have to type hint the params and return type of those methods, and I don’t know how to hint the method return to be a primitive int.
I’m getting: IllegalArgumentException: Mismatched return type: calcWidthInPixels, expected: int, had: java.lang.Object
The interface methods are default int calcWidthInPixels(@NotNull Inlay inlay) {...}
and default int calcWidthInPixels(@NotNull Editor editor) {...}
.
Currently I’m trying:
(deftype InlineRenderer [text]
EditorCustomElementRenderer
(calcWidthInPixels ^"I" [this ^Inlay inlay]
...))
I also don't end up having to do this much, but if I recall the last time I did, I think I type hinted the symbol of the method name, not the argument vector
i fixed my thing, the docs were wrong
its lein ring server, not lein run
type hinting on the arg vector is kind of the newer (but many releases old way to do it), most places that support type hinting first did it on the name, but one new feature introduced doing it on the vector (I think invokePrim) and people started expecting it to match that, so hinting of the vector has sort of been slowly spiraling outwards from there
Yeah, but I thought the name was more in cases where the hint would end up on the var, i.e. def
-like things. I’d assumed deftype
had always used the arg vector hint, or at least that it would work in recent versions.
Is it doable\reasonable to push to github -> travis ci tests -> publish to clojars?
@jayzawrotny that would involve putting your signing keys within the CI system
Hah yeah that would be a terrible idea.