got hit by manual moderation i figure, but someone sent me a screenshot
so back to the off-topic: > you'll note i also have some recipes from my grandmother on there Tried the "Decandent French Toast" this weekend. Used some nice ciabatta bread (but didn't bother to cut of the crusts) and used almonds instead of pecans. Very sweet, but quite nice. Can recommend š (served it with some homemade blackcurrant jam)
I thought I recognised the name!
(2025-09-16.10:18:03)-(~/java)
(!1997)-> cat Hello.java
record Greeting(String name) {}
void main() {
println("Hello, " + new Greeting("Java").name() + "!");
}
(2025-09-16.10:18:15)-(~/java)
(!1998)-> java --enable-preview Hello.java
Hello, Java!
I like that you don't even need to compile it to run it now š (this is Java 24 tho').I am of the opinion that a rising tide lifts all ships here
You don't even need the IO. do you?
you do
i think you might be on 23 if you don't
24, as I said above.
the better people's views of Java, the better their views on the JVM, the easier of a sell JVM clojure is. Clojure isn't threatened by Java getting better like Kotlin and Scala are. Those are "Java, but", Clojure is just a different thing
well you definitely need the IO. in 25
here is me starting the train that I think led to that https://www.barlasgarden.com/openjdk.html?mode=text-search&list=amber-dev&q=Do%20we%20really%20need%20an%20implicit%20%22import%20static%20java.io.IO.*%22%3F&order=desc&limit=25
Just installed OpenJDK 25 and tried it out. Yup, IO. is required now. Interesting. Will read those posts next...
Interesting thread!
We certainly need to address the javaphobia many are irrationally afflicted by, probably back from the days of college and java < 8
Many people I talk to are still under the impression that Java is "slow" because of poorly written Swing apps of yore. Hard to shake!
They're also under the impression you need an AbstractFactoryProxyBean for everything
Haha! Yeah, that's also hard to shake.
I might write that sentiment into a post on the website - but you see what I mean right?
Sorry if this is too off-topic or irrelevant, but "dead" threads on HN are still available via search. The "dead" thread in question can be read here: https://news.ycombinator.com/item?id=45258098
Ya, that was so annoying. It's hard to appreciate, and I know sometimes people have criticized Clojure for having a "bloated" core namespace, instead of organizing things neatly under many smaller namespaces. But honestly, requiring/importing is just annoying. That choice of a big core namespace means you can pull up a repl or write a small script and get going.
IMHO if a programming language as a language (a tool that humans use to communicate ideas) you will be stuck with the baggage of the culture around that language. LISP has all the ingredients to be a great low level procedural language but almost nobody who uses LISP today uses it that way. Java carries the baggage of "enterprise" and cargo cult design pattern nonsense because that is how Java is traditionally written, even though the individual parts of Java as a language make none of that neccessary
to me clojure is a B grade language with an A+ culture around programming
@noisesmith Yeah, Java for green field projects can be pretty nice these days -- the language has come a long way in removing the need for boilerplate and has slowly added more support for a more functional style. But existing codebases -- especially ones with a long history -- can be pretty awful. When I've had to go delving into the source code of some of the JDBC drivers to figure out some weird behavior reported against next.jdbc, I usually feel like I want to gouge my eyes out, or at least take a long hot shower to clean myself off! š
I sorted of parted ways with Java when Java 5 came out. I didn't like some of the changes in that release. But Java 8 made me reconsider (I even wrote a blog post about that... back in 2014) and these days I don't think I'd hate using modern Java to build a new app. I think I'd probably still prefer Kotlin, if I couldn't use Clojure, but at least I wouldn't rule Java out completely now.
@noisesmith > to me Clojure is a B grade language > Shots fired! 𤣠I'm kind of curious what is B grade about it? From your point of view.
hehe, I saw the domain name (yesterday), and slammed upvote even before reading it ... and then read it and came away not disappointed š
If Clojure is B grade, I would love to hear what is A grade š
> people have criticized Clojure for having a ābloatedā core namespace, instead of organizing I feel like thatās one of the biggest non-issues thatās almost a folk legend at this point š . Iād love to hear about a problem caused by a single big namespace. Slow loading in certain niche cases? Not sure. Iām struggling to think of an actual, tangible problem caused by this other than pure personal aesthetic preference.
@emccue: do you have an rss feed hidden in there somewhere?
I do feel it could have been nice if it had organized the core functions under a namespace that follows the cheatsheet. And that ns would default to requiring them all with a default alias, like all string functions are auto required :as "str" and so on.
But I also understand not doing that, as forcing aliases on people and such.
@gar i do - i dont think i did it quite right for embedded reading though
Thanks!
here is the script i use to generate the site, if anyone is curious
(ns build
(:require [babashka.process :refer [process]]
[babashka.fs :as fs]
[clojure.string :as string]
[clojure.edn :as edn]
[clj-rss.core :as clj-rss])
(:import (java.nio.file Files)
(java.nio.file.attribute FileAttribute)
(java.time LocalDate ZoneId)
(java.time.format DateTimeFormatter)))
(defn load-posts []
(reverse
(sort-by
(fn [post]
(LocalDate/parse (:date post) (DateTimeFormatter/ofPattern
"M/d/u")))
(for [markdown-file (map str (fs/glob "./pages" "**{.md}"))]
(let [[metadata contents] (string/split (slurp markdown-file) #"\n" 2)
original-contents contents
metadata (edn/read-string metadata)
temp-file (Files/createTempFile "" ".md" (into-array FileAttribute []))
contents (str contents "\n\n----\n #### [<- Index](/) \n")
contents (str contents "<script src=\"\"\n repo=\"bowbahdoe/mccue.dev-comments\"\n issue-term=\"pathname\"\n theme=\"github-light\"\n crossorigin=\"anonymous\"\n async>\n</script>")
contents (if (= (:type metadata) :post)
(str "<div style=\"display: inline\"> by: <b style=\"display: inline\"> "
(or (:author metadata) "Ethan McCue")
"</b></div>\n "
contents)
contents)
contents (str contents "<style>div.sourceCode { padding:10px; } code { font-size:85%; } code.sourceCode > span { -webkit-text-size-adjust: none; }")
_ (spit (str temp-file) contents)]
(merge metadata
{:original-contents original-contents
:contents contents
:temp-file temp-file
:markdown-file markdown-file}))))))
(defn pandoc [{:keys [in out title]}]
(let [cmd ["pandoc" "-F" "mermaid-filter" "--from" "gfm" "--to" "html" "--standalone"
"--highlight-style=zenburn"
in
"--lua-filter=links-to-html.lua"
"--metadata" (str "title=" title)
"-o"
(str "./target/" out)]]
@(process cmd {:inherit true})))
(defn write-index [posts]
(let [index-contents (slurp "index.md")
{posts :post
questions :question
libraries :library} (group-by :type posts)
questions (map (fn [question] (update question :title #(str "Q: " %)))
questions)
temp-file (Files/createTempFile "" ".md" (into-array FileAttribute []))
post-lines (fn [posts]
(string/join "\n"
(for [post posts
:when (not (:hidden post))]
(str "\n* `(" (:date post) ")` ["
(:title post) "](" (:markdown-file post) ")"))))
contents (as-> index-contents _
(string/replace _ #"&posts&"
(post-lines posts))
(string/replace _ #"&libraries&"
(post-lines libraries))
(string/replace _ #"&questions&"
(post-lines questions)))]
(spit (str temp-file) contents)
(spit "out.md" contents)
(pandoc {:in (str temp-file)
:out "index.html"
:title "mccue.dev"})))
(defn post-path
[post]
(str (subs (:markdown-file post) 0 (- (count (:markdown-file post)) 3))))
(defn write-posts
[posts]
(doseq [post posts]
(pandoc {:in (str (:temp-file post))
:out (str (post-path post) ".html")
:title (:title post)})))
(defn write-recipes
[]
(doseq [recipe (map str (fs/glob "./recipes/mary_mathews" "**{.docx}"))]
(let [cmd ["pandoc" "--to" "html" "--standalone"
recipe
"--lua-filter=links-to-html.lua"
"-o"
(str "./target/"
(subs recipe 0 (- (count recipe) 5)) ".html")]]
@(process cmd {:inherit true}))))
(defn write-rss
[posts]
(let [content (apply clj-rss/channel-xml
(cons {:title "mccue.dev"
:link ""
:description ""}
(map (fn [post]
{:title (:title post)
:link (str " " (post-path post))
:description (apply str (take 50 (:original-contents post)))
:pubDate (.toInstant
(.atStartOfDay
(LocalDate/parse (:date post) (DateTimeFormatter/ofPattern
"M/d/u"))
(ZoneId/of "EST" ZoneId/SHORT_IDS)))})
(filter (complement :hidden)
posts))))]
(spit "./target/feed.xml" content)
(spit "./target/feed" content)))
(defn write-sheepmaster
[]
(let [work "sheepmaster.docx"
cmd ["pandoc" "--to" "html" "--standalone"
work
"--lua-filter=links-to-html.lua"
"-o"
(str "./target/writing/macaulay/"
(subs work 0 (- (count work) 5)) ".html")]]
@(process cmd {:inherit true})))
(defn write-404
[]
(pandoc
{:in "404.md"
:out "404.html"
:title "404"}))
(let [posts (load-posts)]
(write-posts posts)
(write-index posts)
(write-rss posts)
(write-recipes)
(write-sheepmaster)
(write-404)
(fs/copy-tree "assets" "target" {:replace-existing true}))
That's pretty damn cool!
you'll note i also have some recipes from my grandmother on there
Cool, I'll definitively try this out this weekend! Love the name of the dish
ive not made any yet
But I suppose you've tried it?
nope - i just got all the recipes out of her i could before she š
she's currently hooking up with some rich lighthouse owner who doesn't know she has kids or grandkids. really a wild life for that one. i just liked the breadsticks from 3rd grade
Oh wow. Well, I'll report back with the results of your gran's recipe then š
> I'm kind of curious what is B grade about it? From your point of view. the impedance mismatch between the VM and the language semantics. I find myself frequently living in two parallel conceptual worlds, the world of my abstractions and the world of the VM that makes them efficient. so many leaky abstractions around interop and in popular libraries like core.async. but even core things like laziness have a long list of gotchas and special cases. but my bigger point was that the community and the typical style accepted in codebases is often more important than language design alone. julia and ocaml have much more coherent designs as languages, but I have learned far more transferrable skills from clojure, and can reasonably expect better quality in arbitrary clojure codebases
I agree about community. I think with the benefit of hindsight, there are a few things in Clojure that could have been done ābetterā, specifically around lazy sequences. I believe in the Clojure paper Rich himself mentions that heād put transducers at the bottom rather than lazy sequences. I agree lazy sequences can be quite clunky, if used pervasively for things like control flow, delaying evaluation, etc. So I personally exit the lazy world as fast as possibly and use transducers almost exclusively.
Without getting into direct language comparisons, I think itās also easier to do ācoherentā design when the goals are less ambitious. I think this also directly reflects on the learnings one would get from the community: a less ambitious language that does a few things a bit better than its predecessors is unlikely to offer much new insight. Also, with the most recent advances in JVM garbage collectors, I think the downsides of being āhostedā are shrinking fast, relatively speaking. From what Iām seeing in my experiments, ZGC on recent JVMs (24+) is a free, effectively pauseless (!) garbage collector. I donāt think many other garbage collected languages that are not on the JVM can claim to have such a thing. As with everything, utility is contextual. But I think this a big one: a pauseless garbage collector yields benefits in a variety of use cases.
mostly agree, but I'd hardly call implementing your own GC and getting it to adequate performance "less ambitious", it's just a difference in where the work hours are spent
Oh, I am no GC expert but I think I anecdotally know enough to realize thatās one of the hardest things to probably do well, in terms of programming a single machineā¦
but even without the complexities that come from being hosted on another language's VM, clojure is a intentionally a hacker's language, all the abstractions leak because it's a lisp and we want escape hatches to violate them
Say more about āabstractions leakā
a leak in an abstraction is the exposure of the details the abstraction covers over. clojure's design intentionally treats its own abstractions as hackable things. this is part of the lisp heritage.
Right, from a certain point of view, they are not hackable enough lol⦠I.e. not everything is protocol-based, etc.
But one could definitely āoverrideā them, with a bit of hassle. I.e. make your own vector, maps, collections etc.
among other lisps, the clojure community has the reputation of being uptight about the lispy modifiability of the language - eg. the community consensus to avoid macros
Right, thatās probably a good thing, most of the timeā¦
And the lack of an āAPIā to extend the reader to non-standard characters is an intentional design choice.
but compared to any non lisp (with notable exceptions like ruby, perl), it's a very hacky and hackable language, often preferring ease of manipulation and simplicity of implementation (which makes hacking easier) over clean abstractions
Right⦠most languages wouldnāt allow you to make a collection that feels almost entirely first class.
java being a notable exception :D
except no data is really very first class in java
Ah⦠well lol⦠I guess in an OOP language, itās all possibleā¦
but even amongst the OOP languages, few are as principled as java in letting you replace its building blocks with compatible classes
Thinking about it, maybe thatās not a correct statement about collections and most languages that I made
I guess in most languages one could have a collection that behaves quite first-class
depends what you mean by "first class" - literals? operators? destructuring syntax?
Ah⦠right lolā¦
I guess most collections wouldnāt have operators specific to them?
what I mean is can you use foo[3] to access an index on your replacement to arrays (clojure fails that even on the base case), can you use #foo[1,2,3] to make a new one (clojure is one of the few languages that supports such a thing)
Destructuring is interesting, I donāt know how it really works in other languages
Right, that kinda syntax is kinda goofy anywayā¦
foo[3]
I know itās āpopularā
But I assume it translates to some get call or whatnot in reality
having been forced to go back to algol family syntax, that is the one of two real advantage over a lisp. intuitive and syntactically clear collection indexing, and infix notation so you can write the kind of math expressions you learn in a math class
haha, I think Iāve been totally lisp-ed š I canāt do math class syntax anymore
it's probably the only reason my current project can't use a lisp. all our users are scientists
Haha, ok⦠but I would argue thatās just familiarity bias
My brain thinks in fns and argumentsā¦
Not in āimperative math operatorsā
Yeah, I would say, you didnāt sell the algol languages very well, if those are the main advantages š But I get it: familiarity is a strong force
I heard recently that it's hard to rent a car as an american in england. it's not that right side of the road vs. left is superior, it's that having to switch between the two is a huge source of errors
Haha⦠Iāve rented a car in the UK⦠itās pretty trippy for the first couple of days driving; after that itās mostly normal
And most English rental cars are stick shift (manual) š
Haha, I made sure I got automatic, even though my first car was manual⦠I realized dealing with one big new variable was enough lol
I hope my metaphor is clear though - every cognitive hurdle is an avoidable source of error, and infix is for better or worse the lingua franca
As a long-time ex-pat Brit (26 years in America), whenever I visit the UK and rent a car, I nearly always end up on the wrong side of the road at least once a week...
Yeah, itās a real habit thing
worst thing about going back to algol world is the structural editing is very primitive and hard to set up
and in lisp I could just leverage the ability to recognize matching parens for nearly everything lol
once again, lispy means accepting a more rudimentary and hackable implementation
there's a concept I am very fond of in the world of performance optimization: "mechanical sympathy". I think many of the best and worst aspects of clojure are a result of trying to balance a mechanical sympathy to the vm, with a "linguistic sympathy" (there's probably a better term) for the surface language toward language internals
Right, I think Clojure really hits that sweep spot, that if you wanted to leave all the abstractions behind, you can. I.e code everything with, say loop/recur and mutable Java hash maps or Java util concurrent hash map. Arguably, itās barely āClojureā at this point but the language is not going to get in your way. In that sense, it achieves effectively 0 cost abstraction over Java.
And Java itself⦠you can likely code Java as if itās C++ but then itās barely Java lol⦠(I have never tried the latter but have done the former: Clojure like itās Java)
I think the JVM specifically, once project Valhalla becomes a reality finally might have an answer for higher performance numerics⦠ācodes like a class, works like a primitiveā
Presumably, things like List<int> will be possible
If i'm not mistaken, we'll be able to have multidimensional values to be stored in memory similar to primitive value arrays and have the same performance characteristics?
Presumably yes, but I havenāt done a deep dive, so thatās a guess. AFAICT, āplannedā is also still changing, for that project.
I like that you don't even need to compile it to run it nowthis was supported a while back, I believe since 11? https://dev.java/learn/single-file-program/
its only since 22 that you dont need to compile to run a multi file program
sure, I was talking a single "script", which works since 11