I’m getting ready to teach the first day of clojure to some students. I’m reviewing my understanding of how the clojure repl works as opposed to the repls in other lisps. Can someone help me understand why evaluating the list (1 (println 2)) fails without printing 2. My guess would have been that all the elements of the top level list would be evaluated BEFORE it discovers that 1 is not a function. So I’d have expected the side effect to occur before the exception.
TL;dr. what does the REPL do when I try to evaluate a list?
BTW, this behavior is not unique to clojure. elisp does basically the same thing.
elisp also discovers that 1 is not a function before evaluating (println 2)
my guess is that it first examines the first argument to decide whether it is a special form, a macro, a keyword, or several other possibilities. and if it is a symbol designating a known variable. and only in some of these cases does it proceed to evaluate the remaining arguments. e.g. we certainly would not want the println to be called in the cases that we have entered a top-level macro call.
However, this does not exactly seem to correlate to the documentation description found here. https://clojure.org/reference/evaluation Which says
If the operator is not a special form or macro, the call is considered a function call. Both the operator and the operands (if any) are evaluated, from left to right. The result of the evaluation of the operator is cast to IFn (the interface representing Clojure functions), and invoke() is called on it, passing the evaluated arguments.
This seems to imply that the arguments are evaluated before the first argument is case to IFn.
The easiest way to inspect things like this is probably with clj-dava-decompiler:
user=> (add-lib 'com.clojure-goes-fast/clj-java-decompiler)
[com.clojure-goes-fast/clj-java-decompiler org.bitbucket.mstrobel/procyon-compilertools org.bitbucket.mstrobel/procyon-core]
user=> (require '[clj-java-decompiler.core :refer [decompile]])
nil
user=> (decompile (fn [] (1 (prn 2))))
// Decompiling class: user$fn_line_1__256
import clojure.lang.*;
public final class user$fn_line_1__256 extends AFunction
{
public static final Object const__0;
public static final Var __prn;
public static final Object const__2;
public static Object invokeStatic() {
return ((IFn)const__0).invoke(__prn.invoke(const__2));
}
@Override
public Object invoke() {
return invokeStatic();
}
static {
const__0 = 1L;
__prn = RT.var("clojure.core", "prn");
const__2 = 2L;
}
}
// Decompiling class: cjd__init
import clojure.lang.*;
public class cjd__init
{
public static void load() {
new user$fn_line_1__256();
}
static {
Compiler.pushNSandLoader(RT.classForName("cjd__init").getClassLoader());
try {
load();
Var.popThreadBindings();
}
finally {
Var.popThreadBindings();
}
}
}
nil
This is the most pertinent line: ((IFn)const__0).invoke(__prn.invoke(const__2));.
Here we can see that in order for 1 (which is assigned to const__0) to be called as a function, it gets cast to IFn - before any of the arguments are evaluated.hmmm. do you think the documentation is misleading in this corner case?
Side question. (maybe best for another thread???). You compiled (fn [] (1 (prn 2))), however I typed (1 (println 2)) at the repl. Does the introduction of (fn …) change what’s happening?
Your guess is wrong. For functions, all arguments are evaluated. But for macros not. So it must first find out if 1 is a function or a macro.
Docs don't seem misleading to me.
@hbrng.computer yes the evidence seems to indicate that you are right. However, the documentation (to my reading) says otherwise. it says that it first checks whether it is a macro or a special form, otherwise it assumes it is a function call (without verifying). and then evaluates the operator and operands, then casts the operator to IFn and attempts to invoke the function with the already-evaluated arguments.
@dave.liepmann i’m interesting in better understanding your point of view.
>
Both the operator and the operands (if any) are evaluated, from left to right.
>
> The result of the evaluation of the operator is cast to IFn
Where's the lie?
The sequence of operations, specifically when casting to IFn happens, is not part of the documented contract.@dave.liepmann so you don’t read the paragraph as a sequence of events in order, but just a randomly ordered list of things that happen? You are right that it does not explicitly say this is the order of events. Actually in English the sentence “the operands are evaluated” is ambiguous. it can mean “something evaluates the operands” or it can mean “the operands are in the state of already having gotten evaluated”. Danger of passive voice.
I see how one could interpret it as ordered, but it's not. It's above my pay grade whether there's good reason to lock down the documented behavior but by default I'm skeptical.
Is there interest in improving the documentation? I think the paragraph could be easily worded better. for example, it could say that “the order of evaluation of invalid forms is unspecified”
this is expecially true given the decompliation shown above. the actual evaluation is left up to the java compiler which might even re-order some things in some situations.
@hbrng.computer That check is done explicitly, not via casting. So it's not relevant. The code compiles correctly. It just cannot run.
> Is there interest in improving the documentation? Yes, suggestions for documentation can be filed at https://github.com/clojure/clojure-site/issues. And all the info on contributions in general, including docs improvements, is here: https://clojure.org/community/contributing.
> the actual evaluation is left up to the java compiler which might even re-order some things in some situations. Such a reordering should not change anything relevant to the thread.
I’ve added an issue on clojure-site: https://github.com/clojure/clojure-site/issues/751 I’ve noted that some people do not disagree with my opinion. I didn’t note that in fact everyone disagrees with my opinion (smirk).
you could also point students to clj-kondo:
$ clj-kondo --lint - <<< '(1 (prn :hello))'
:1:2: error: a number is not a function
linting took 7ms, errors: 1, warnings: 0 Having wrote a Lisp evaluator as part of an assignment in the 80s (adding more special forms than the defaults aka fn and macros), I find this behavior normal. If it's not quoted the list is evaluated as an fn or macro expression and obviously the first arg needs to be evaluated before anything else can occur. You can't eval the args if you don't know which special form you're dealing with from the start...
same behavior in SCI / bb btw:
user=> (1 (prn :hello))
java.lang.ClassCastException: java.lang.Long cannot be cast to clojure.lang.IFn [at <repl>:1:1]> the first arg needs to be evaluated before anything else can occur.
The 1 is evaluated to 1. :)
It's the cast that's the problem. And nothing requires the cast to happen before evaluating the rest of the arguments.
Being evaluated to a number and a list starting with a number is not a valid special form. Reporting this as an error is fine. At that point you still don't know what to do with the remaining arguments so why would you eval them ?
CLJS:
cljs.user=> (1 (prn :hello))
:hello
Execution error (TypeError) at (<cljs repl>:1).
1.call is not a functionWhich to me is inconsistent....
That cloud lead to bad side effects...
... could...
public static Object invokeStatic() {
return ((IFn)const__0).invoke(__prn.invoke(const__2));
}
I guess that could have been written as
var arg = __prn.invoke(const__2);
return ((IFn)const__0).invoke(arg)
Probably just not very well defined what should happen here.
If you care about this specific case you can always use a let yourselfAgree that it should be clarified. I do not see any logic in the current implementation and a potentially harmful loop hole. That's my SCADA background haunting me 😂
@lprefontaine The point is not the "why would you", the point of my message was to refute the "you can't eval". You can in some cases, including this one. It might make little sense (but someone could make it make sense in their specific deranged case since class cast exceptions can be caught and argument evaluations can have side effects), but it's feasible.
@lprefontaine > You can’t eval the args if you don’t know which special form you’re dealing with from the start... Yes, but the documentations says that if it is not a macro and not a special form then it is assumed to be a function call.
Yeah I guess the docs could clear up the order of the cast and evaluation.
It defies logic to allow this to happen. The default to assume it's a function call is ok, what's not is to eval the args before knowing that it's a function and not a programming error.
"clojure is designed for correct programs"
Yeah but this is an area where most don't go deep...
Think about a production issue, you have stack trace reporting that it's not a function but the args where evaluated with some persistent side effects 😵💫 The investigation will take for ever...
@jimka.issy Maybe you shouldn't bother students with these edge cases? lolcry In my 16 years with Clojure this hasn't tripped me up once.
I know that we live in a pragmatic world; however, I think documentation should be correct and not misleading at least when it is easy to be so. Ideally a programming language should be defined by its documentation, not by its specification. yea yea yea, I know that the world is not an ideal place.
oh I agree that the docs should be clarified.
@borkdude, the situation arose when I asked myself: Do I really understand what the REPL does, and I realized. hmmm No I really dont.
congrats in teaching clojure btw. I did it too in 2011-2013
@borkdude agreed, this is not something I should confuse the students with.
@borkdude would you mind commenting on the github issue that the behavior is inconsistent clj vs cljs ?
Never thought that this loop hole existed... taking note, ya never know when a truck will hit you sideways on your green light at a crossroads...😬
I guess it's kind of host specific. since CLJS functions are real JS functions and casting isn't a thing in JS
@lprefontaine > Never thought that this loop hole existed.. If you follow the Common Lisp community these kinds of corner cases discussions are commonplace. CL is defined by a specification and there are a hand full of implementations. Whenever two different implementations give different behavior people discuss what the specification says, and have discussions worthy of theologians about every jot at tittle of the specification.
C++ programmers have cursed the CL community in revenge for having to follow the Greenspun's tenth rule.
I don’t follow C++, do they follow Greenspun’s 10th rule?
My first Lisp was UCI Lisp. In the 80s. The specs were very clear as the implementation.
I heard a funny discussion at Franz (a professional proprietary lisp compiler company). While implementing AllegroGraph, one of their developers commented that even though AllegroGraph is implemented in CL, it is also a victim of this rule.
@jimka.issy I meant that C++ is designed by a committee. I believe the rule predates C++, but I'm pretty sure C++ also follows it by its nature, although any negative epithet in the rule is likely to be magnified by the C++'s complexity.
Back to the documentation excerpt …. I think that paragraph is also missing another very important case. reading that literally does not allow for (:x {:x 100 :y 200}). Or did I miss something?
@lprefontaine how many implementations were/are there of UCI Lisp?
> reading that literally does not allow for (:x {:x 100 :y 200}).
Why not?
it says that if the form is not a special form nor a macrocall, then it is interpreted as a function call where the first element is cast to Ifn.
ahhhh, does :x cast to Ifn as a call to get ?
that would be clever if that’s the case.
Keywords are functions, yes.
Associative collections are also functions (that includes vectors and sets). And symbols. Meaning, a symbol can find itself in a map.
not sure if you also had this in scope in this thread:
user=> ((prn :foo) (prn :bar))
:foo
:bar
Execution error (NullPointerException) at user/eval3 (REPL:1).
Cannot invoke "clojure.lang.IFn.invoke(Object)" because the return value of "clojure.lang.IFn.invoke(Object)" is nullThe N-gon just became an (N+1)-gon!
why is a NullPointerException unexpected? if you try to call nil as a function?
The above happens because you can succesfully cast a nil to any type in JVM
user=> ((do (prn :foo) 1) (prn :bar))
:foo
Execution error (ClassCastException) at user/eval9 (REPL:1).
class java.lang.Long cannot be cast to class clojure.lang.IFn@jimka.issy UCI Lisp was a derivative of Stanford Lisp 1.6 and in turn inspired 4 other implementations under various names. UCI Lisp ran on PDP architectures up to Dec-20s, I recall using it on both Tops-10 (DEC 10] and Tops-20 (DEC 20).
i would have anticipated the code to work like borkdude's suggestion (copied below), but i think it's more a feature/quirk of the jvm than of clojure
var arg = __prn.invoke(const__2);
return ((IFn)const__0).invoke(arg)
i suspect that the way you can nest calls means you'd have to rewrite/change the compiler a bunch to handle this "properly", for a somewhat small consistency fix
The Clojure compiler has full control over this btw
Are you sure this topic matters? Calling a number is illegal. It is not a corner case. Language standards normally don't specify what happens in such situations. In C++ this is called undefined behaviour. It is intentionally undefined.
"does this topic matter?" any topic is worth discussion. if you don't like the topic, you don't have to engage.
Which AI tool do you use together with Clojure? I've tried chatgpt and gemini via their web interfaces to ask for some simple functions working on tablecloth, XMLs, basic data structures but the results were pretty underwhelming.. eg regularly giving me code that includes non-existing functions and stuff like that. Which tool do you use and how?
Maybe check out #ai-assisted-coding if you haven't already
Thanks. For some reason it slack did not show that this channel exists at all.
Slack doesn't join you to all its channels, unlike e.g. Discord. There are many channels here, but you have to search for them via the channel search tool.
I'd recommend using tooling that integrates with your editor (e.g., VS Code and its GitHub Copilot) or a dedicated coding AI tool (e.g., Claude Code). The standalone AI chat apps / web have no context for your codebase/project, and that matters.
I put several LLMs to trial, including local models but didn't get very far in terms of success in day to day use. It's almost a miracle to get error free code generated. You need an MCP to allow your agent to fix it, clojure-mcp. Kind of lame to have to rely on this, considering that Lisp code is a data structure but it's the only way. Of course this MCP is helpful for other things aside deo syntax validation. Maybe there's not enough training data out there. On the other hand since there's no boilerplate code, "typing speed" is not a thing to me. So I tackled more complex thing using AI, I am migrating an old ReactNative mobile app to ClojureDart/Flutter. The original app itself is pretty bad in terms of code reuse and I am in the process of fixing the generated code. Trying to get an LLM to improve the generated code reuse has been a mixed bag. For code review however it proved useful. My conclusion so far is that we need a better trained LLM if we want more than light code assistance. They're better at verbose programming languages like OOP. Next fall, I will put my hands on a Mac Studio on steroids and dive in this deeper using local models.
i was shopping for a thin wrapper around java.net.http.HttpClient and eventually landed on https://github.com/babashka/http-client. seeing https://github.com/babashka/process “upstreamed” and included in Clojure 1.12, i'm thinking: wouldn't it be nice for future Clojure versions (that are based on Java 17+) to have a similar but built-in wrapper like babashka.http-client? 🙂
There's hato which is a thin wrapper around the Java 11+ HTTP client. https://github.com/gnarroway/hato That's what we use at work.
it's built into the jdk since 11 https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html
yes, hato was amongst the options that i considered but development on it seems to have stalled…
A lot of libraries in Clojure are simply "complete" and don't need additional development 🙂
I don't use any of these often enough to know if hato provides enough additional help that including something like it would be worth doing in core (once we move past JDK 8 as a base), but I think that would be good thing to consider
would welcome an Ask Clojure question along those lines
I would also assume that clojure.java.process was independently designed by the core team, rather than being in any way an "upstreaming" of babashka.process...
oh, Opus somehow thought it was modeled after babashka.process…
yes, I would expect a similar re-examination for something like the http client, but who knows
there are similarities to babashka process for sure but it was a clean impl
> Opus somehow thought... "Opus somehow hallucinated..." FTFY 🙂
(don't get me wrong: I use Claude LLMs all the time but...)
Definitely worth a request on https://ask.clojure.org -- once Clojure 1.13 drops JDK 8 support, it would be nice to have an HTTP client in the core library.
oh, i see… good to know. i should have gotten my facts straight before saying that… didn't mean to discredit the core team. anyway, it sounds like there is a possibility for this. i'll go ahead and create a request on Ask Clojure.
> A lot of libraries in Clojure are simply "complete" and don't need additional development 🙂 I wouldn't say Hato is complete though...
I wonder if any HTTP client library could ever be complete? Perhaps "sufficiently complete" (and you can drop down to Java interop as needed) is the best we can hope for with Java API wrappers? 🤔
Well, that reasoning can be applied to absolutely any library. :D In the case of Hato, I myself consider it using cheshire to be a deal breaker when it comes to deciding whether it's complete or not. I'd rather see serialization pluggable than hard-coded.
I remember Alex and I had some back and forths about bb.process when he designed c.j.process, about the option map argument etc.
Cheshire is optional with Hato. We use clojure.data.json -- we don't have Cheshire anywhere in our deps tree at work, as I recall. I worked hard to remove it 🙂
bb.http client is very similar to hato but has some extra things I needed and some things I didn't like it doesn't have :)
Hato has zero dependencies, which is part of why we switched to it (from clj-http).
Well, yeah, but then the value-add of the library drops significantly if you can't use :as. It would be around 20% more lines to just use the JDK stuff directly, last time I checked it in my own code. It's already slim as it is, given how functions like build-http-client take 40 lines but do basically nothing. :)
Maybe I'm just even less of a fan of Java wrappers than you are. :D
Probably. And I'm already not really much of a fan of them 🙂
i think http is super complex compared to the other mentioned examples (which is why there are so many libraries). I think even if you want to just do a light java.net.http wrapper + clojure maps for request/response, that road eventually leads to needing something like an interceptor pattern or accepting some ways http is legitimately used will be less performant or convenient than necessary (not that i wouldn't love to see the core team's take on it)
But Hato has a (mostly) clj-http-compatible API so it was an easy switch, and we use explicit JSON conversion, in and out, via c.d.j so...
Somewhat ironically, we had one old app that had to stay on JDK 8 for years, and so we wrote our own wrapper for Hato that we could swap out for a JDK 8 compatible client with the same calling API, so we're another layer removed from Java interop because of that.
@jjttjj I agree that it's a much harder problem than it looks on the surface...
bb.http-client also has zero deps (based on java.net.http) and everything is pluggable. it does have some good defaults which are overridable. I started from scratch but with two examples in mind. one was schmee's https://github.com/schmee/java-http-clj and hato (and clj-http for how it named stuff), but I wanted it to be minimal unless I needed something. And I didn't want to depend on either project and be at the mercy of "is this project maintained" or wait 2 months for something to be merged
also one strong requirement was that it worked well with graalvm native-image. only schmee's project did so out of the box at the time
The Cognitect http-client is changing to the jdk built-in http-client in the next release, which we are about to ship. And it should be open-source, in that the source should be in the JAR in maven-central shortly.
OK, at long last, here is the request: https://ask.clojure.org/index.php/15129/consider-including-wrapper-around-httpclient-versions-clojure please vote (and participate in the discussion) if you like this proposal, too. 🙂
Thank you! Upvoted.
I enjoyed the library curation discussion on the Dev Call. clojure.land was news to me, but I knew about prior art such as the https://www.juxt.pro/radar/ and the https://www.clojure-toolbox.com/. Should these newer tools be be included https://clojure.org/community/libraries I'll add another data point: I was recently exploring Swift and was pleasantly surprised by their unofficial https://swiftpackageindex.com/groue/GRDB.swift: it gathers the license, versions, dependencies (transitive or not), snippets for package managers, API docs etc. A good source of inspiration IMO, although I haven't used it in anger
I updated Clojure Land to pull in the GitHub topics and use them for search. So, for example, now searching for "encrypt" will surface Tempel
Thank you for the links!
I've added clojure.land to the page. the juxt radar is pretty old
It's not very pretty, but I think https://phronmophobic.github.io/dewey/search.html gives good results when searching for clojure libs. https://cloogle.phronemophobic.com/doc-search.html is useful for finding specific functions (although it's about a year out of date at the moment). (example query, https://cloogle.phronemophobic.com/doc-search.html?q=create+and+return+a+temporary+file).
Seems like some of what you're after is sort of clojars + cljdoc and those two both have search functionality and they cross-link for specific versions?
I guess what's missing there would be some sort of "curation" / "popularity" index?
Also, clojure libraries can be consumed as git deps, so it would be missing all the usable libraries that aren't on clojars.
Yeah, true. At some point, we may get (popular) git deps on cljdoc but that requires quite a bit of work...
A problem with nearly all the "curated" lists is that they are somewhat at the whim of whoever cares enough to submit a PR... and they can easily get outdated when new, more popular libraries appear but don't get added (and, in particular, do not replace the older, less popular library).
You could use the various various sources of activity (eg. zulip, github, clojars, etc) to approximate recent usage.
http://clojure-doc.org used to have a "curated" list of libraries but it got very out of date. In 2023, when I was overhauling that site, one of the big pieces of work in Q2 was going through everything listed on clojure-doc and the toolbox site, and making sure everything that was at least reasonably maintained got added to / updated on the toolbox site, so I could retire the clojure-doc version.
I believe this is ezactly what clojure.land is trying to do with multiple activity metrics
Yeah, it may well be the "best" index we have for now...
Looks like Brett is pretty active about adding project metadata from the weekly Deref! That makes me feel a lot better about it as a sustainable option!
@batoms and I have been talking, and yes, he's been using the Deref as a source.
I like clojure.land and the work he's put into it.
What I had suggested to Brett was the idea of develop out different "signals" to try an help prioritize results. (Eg. GitHub stars, recent commits, frequency of commits, mentions in other projects, closed issues, etc).
A similar idea to what @smith.adriane is suggesting.
One thing I noticed when comparing clojure land with https://phronmophobic.github.io/dewey/search.html is that clojure land is missing the topics from github which seem to be more comprehensive. I think it's also useful to be able to search by repo author.
Yeah, searching by author is nice for sure. Eg. I love @christian767's work, so anything he puts out is immediately interesting to me.
When I migrated stuff from clojure-doc to the toolbox, the first PR I submitted was to get the description of the library surfaced as a tooltip, so I agree with Adrian on adding the "about" from GH if poss.
Re: signals -- sounds good! Stars is a metric that rarely if ever goes down, so something that got a lot of stars back in the day but isn't as popular today (e.g., Om) should be lower down the list. Same with downloads for things that are rarely used on their own but come in transitively as part of something that is wildly popular (Riddley, Cheshire, and Potemkin may be examples?). Finding the right weighted metric is hard of course, and subjective 🙂
maybe it should have reviews like yelp, google maps, amazon, etc 😛
Yeah, I think experience reports (aka "reviews") are another data point that could be considered. What's tough about that is moderation.
If someone posts some kind of flaming takedown full of personal attacks, there has to be a process to address that, for example.
I think there are lots of different angles on a decision, so if we can help surface the data for each of those angles, people can make up their mind.
> Yeah, true. At some point, we may get (popular) git deps on cljdoc but that requires quite a bit of work... I played around a lot of with the popularity index on Clojure Land and to try to get something that felt honest. Ultimately I landed on a heuristic based on stars, downloads per day from clojars and with a staleness decay based on the last release date so that projects that were really popular 10+ years ago like Om don't appear more popular than more recent projects.
One thing I noticed when comparing clojure land with dewey search is that clojure land is missing the topics from github which seem to be more comprehensive. I think it's also useful to be able to search by repo author.In Clojure Land I'm using the project description from the GitHub by default but it also allow it to be over written in the projects.edn file. For the Github topics they just felt too arbitrary and uncurated so I started with the categories from the Clojure Toolbox for the Clojure Land tags and then worked from there. To be honest these days I just have the LLM assign tags based on the project README and rarely try to curate them myself.
Ah, I misunderstood what was meant by "topics" there... so, what exactly is that?
They're the topics you can assign to a Github repo like the "jdbc" topic assigned to the seancorfield/next-jdbc repo
In Clojure Land I call them "tags". In Dewey you can view projects by topic but since they're managed by the repo owner then it can get really messy, e.g. on the first page of the https://phronmophobic.github.io/dewey/topics.html there is 12-factor, 12factor and 12factorapp
Haha... totally forgot I'd added that... and so I had forgotten those "tags" were called topics. Thank you. Yeah, I don't think I've added topics to any of my other repos.
I almost never use dewey's topic page. For my purposes, I'd rather find a library and decide not to use it then not find a relevant library. As an example, I was looking for an encryption library and searched "encrypt". With dewey search, I found tempel as the second result and it doesn't appear as a result on clojure.land, despite tempel being in clojure.land's list. I can give a few more examples where including extra topics would help provide more results on clojure.land if that would be helpful.
The dewey search stuff doesn't try to curate and curation can mean different things to different folks.
I wasn't trying to call out Dewey here so sorry if it came across that way. My intention was that using GitHub topics without some kind of curation can get really noisy in the project cards in Clojure Land. I've intentionally tried not to curate Clojure Land projects other than the tags and trying to limit it to open-source frameworks and libraries rather than applications built with Clojure. I could definitely pull in the topics from GitHub without displaying them and put them in the search index. I was hoping that fuzzy searching on the combination of project name, description and tags would be enough but I guess there's always going to be misses.
And PRs to improve the project list or tags are always welcome 😉
Yea, I didn't take any offense. clojure.land looks great. I hope I wasn't coming off as negative.
There are some tradeoffs between comprehensiveness vs curation and I wasn't sure how clojure.land was navigating that. You can get all the tags that dewey knows about in the weekly https://github.com/phronmophobic/dewey/releases/ under deps-libs.edn.gz. I'd be happy to make a PR that updates all the tags, but I assume you would want more curation that just what is on github.
The whole point of the dewey project is to try to make it easier for tools like clojure.land so if there's some data that would be helpful, I can look into adding it.
I already have a process to pull data from GitHub daily so I could incorporate fetching the tags into that.
I noticed some deps don't have deps coordinates. Is that because they're not in clojars? If you pull git tags, you can often generate git dep coordinates for libs not in clojars.
Finally got to the part of the video about curation. It seems like it would be interesting idea to check the obvious places to see if a repo has commits authored using AI.
most AI-aided repos seem to have Claude as a contributor. knowing about those is a useful signal to me
I believe tools like Codex also attach co-author metadata to commits.
I think having little badges for the obvious stuff makes sense. I'll probably try to add that in the next update to dewey.
Seems likely if there is an AGENTS.md or CLAUDE.md file too.
Inverse document frequency of em dashes and delves
I just looked over some of my GitHub projects where I've definitely used AI in the last six months (including a PR entirely authored by Copilot/Claude) and there's no indication in the commits. A couple of commits have a note that they were Reviewed by Copilot (true: I often run my own code through a Copilot review to see if it suggests improvements to naming or docstrings). At work, I see stuff like:
WS-15438 wire up tx status call on redirect Co-authored-by: Copilot copilot@github.com Signed-off-by: Sean Corfield sean@worldsingles.com
in a lot of commits lately, so I think the "co-author" credit is something that is a fairly recent change in Copilot. So there's another signal to look for.Also, there are no AGENTS.md or CLAUDE.md or whatever files in any of my repos -- everything is under .github:
(I have that setup in several of my OSS projects locally -- I just haven't committed those files 🙂 )
Ideally, I would hope people would self-report AI usage, but some usage would be really difficult to detect for sure.
I think the people who care most about having a standard way to report AI usage are people who want to avoid AI altogether, which makes it harder to standardize. Further, companies like github are incentivized to avoid making it easy to discourage AI usage. I think it's ok if the signals aren't full-proof.
What would folks say is the current leading OAuth library to use for clojure? I need things from acronyms I don't understand like "DPOP" and "PAR" and to integrate with an app via these shenanigans https://clients.dev . I've been using ring-oauth2 and tried to vendor it to get it working for this but I'm at the limits of how much i can do without leaning forward in my chair
(defn- send-dpop-par-request
[par-endpoint proof-factory auth-req nonce]
(let [par-req (PushedAuthorizationRequest. (URI. par-endpoint) auth-req)
http-req (-> par-req (.toHTTPRequest))
_ (HTTPRequest/.setDPoP http-req
(DPoPProofFactory/.createDPoPJWT
proof-factory
(-> (HTTPRequest/.getMethod http-req)
(Enum/.name))
(HTTPRequest/.getURI http-req)
nil
nonce))
http-res (-> http-req (.send))
par-res (PushedAuthorizationResponse/parse http-res)
{:keys [par-res
nonce]} (or (and (PushedAuthorizationResponse/.indicatesSuccess par-res)
{:par-res par-res
:nonce nonce})
(let [error-object (-> par-res
(PushedAuthorizationResponse/.toErrorResponse)
(PushedAuthorizationErrorResponse/.getErrorObject))]
(if (and (nil? nonce)
(= error-object OAuth2Error/USE_DPOP_NONCE))
(send-dpop-par-request par-endpoint proof-factory auth-req
(or (some-> http-res
(HTTPResponse/.getDPoPNonce))
(Nonce.)))
(throw (Exception. (str "Pushed authorization request failed: "
error-object))))))]
{:par-res par-res
:nonce nonce}))
I've lost the mandate of heavenlgtm
former coworker of mine wrote this if it might be helpful: https://github.com/edpaget/oidc-provider
it’s been quite helpful for us
is this for making an oauth server? i'm more looking for a client https://github.com/weavejester/ring-oauth2
I recently dropped ring-oauth2 - we run into too many papercuts, so if you only have one provider to support implementing basic code exchange flow is not that complicated
i am still on the atproto thing I was in the thread a few posts above https://clojurians.slack.com/archives/C03S1KBA2/p1779200079212939
and there is something which i can base off of, but idk how i feel about every choice here https://github.com/atproto-clj/atproto-clj/blob/main/src/atproto/oauth/client.cljc
but unfortunately i do need everything else
plus the extra annoyance of not knowing the url to send people to until "its too late"
what did you drop it in favor of? just homerolling it?
Yeah, it wasn't that hard because I had most of the necessary bits already working - it's not the first time when I run into this, so the process boiled down to reimplementing the callback and code exchange flows - that file you linked to is more or less what you'll need
i'm gonna see if theres a way i can make spring security do this and then jam it in a ring flow
I have a sketch of a library to facilitate the oauth2 flow and the user provides ways of storing and recalling the session, handling routing - but it's far from being usable
spring security cannot do this
com.nimbusds.oauth2 is promising, i am ripping out the impl of ring-oauth2 and replacing it with that. If i can fully lobotomize it and keep tests working I will integrate dpop and par then i'm good
I'm so tired chiefs, but i got it