Jeaye
7 months ago
Hey folks. I'm the creator of jank. I didn't expect to be on HN today, but I appreciate the interest.
In short, jank is Clojure, but it's on LLVM and has seamless C++ interop. You still get full nREPL capabilities, can redefine anything on the fly, and we can actually JIT compile C++ code alongside your Clojure. The seamless C++ interop is first of its kind, for a lisp, and is done by JIT compiling C++ alongside the LLVM IR we generate for jank and then stitching them together into one IR module.
Note, jank isn't released yet. I'm targeting the end of this year for the first alpha release. I put out monthly development updates on the jank blog, with the next one coming out this week.
benreesman
7 months ago
I don't have anything terribly insightful to say other than that I really like this project. I loved using Clojure back during it's brief time in the sun, it was a great time, and jank has all the same awesome vibes. I haven't used it on anything serious yet, it seems like it would be great for like a `zig`-flavored approach to C++ builds for example. I'm doing a big C++ project ground up at the moment, I think I'm going to get jank into it and see if I can automate some stuff.
The compilation strategy is very much what Carmack did in Trinity and whether you got your inspiration there or independently had a great idea, that's good company to be keeping.
Keep it up!
Jeaye
7 months ago
I hadn't heard of Carmack doing something similar, but I will take that company any day. Thanks for the kind words!
dzonga
7 months ago
beautiful work. clojure is very nice. one of the most impactful talks I have ever seen was from Rich Hickey - simple made easy.
however my only gripe with clojure while it's easy to write and comprehend at first - it's difficult to read. & yet most our time we read code not write it. but then again it might be my lack of brain power.
Jeaye
7 months ago
I agree with you, but perhaps in my own way. Jumping into an arbitrary Clojure program can be tough, since the data shapes may not be defined anywhere. Hopefully the program uses spec or malli, but even then, unless they annotate every function with the shape it expects, you may be left needing to REPL in and poke around. However, REPLing in to check just a function or two may not be easy if the program requires some setup and doesn't use integrant or similar.
Once Clojure parity is achieved, I'm interested in static typing, pattern matching, value-based errors, and some other opt-in improvements that I think will greatly improve both readability and toolability (i.e. how well tooling can work with the code, based on what it knows of the code). Stay tuned. :)
thethimble
7 months ago
What’s your take on Hickey’s talk titled “Maybe Not” which fundamentally criticizes static types? Is there a middle ground where some form of static typing makes sense in a Clojure-esque world?
Jeaye
7 months ago
Rich has many great ideas and he founded Clojure. I respect him deeply. On typing, however, we do not agree entirely.
For a practical example of a Clojure-like language with a completely static type system (with affine typing), see Carp. https://github.com/carp-lang/Carp
I don't see why there can't be a Carp mode in jank, with bridges in place to connect the Clojurey world from the Carpy world. This would allow jank users to develop interactively to start with, figure out their shapes, use the REPL, etc. Then, if they want, they can lock down some parts of the code for both performance and correctness gains.
narnarpapadaddy
7 months ago
FWIW, (I have one Clojure project I inherited at work that my team maintains) I love this direction.
frogulis
7 months ago
Been a while since I've watched/read it, but I remember the ideas in Maybe Not being quite interesting.
To me, the really important idea wasn't a criticism of static types in general.
Instead it was the idea that static typing in most (all?) mainstream implementations conflates concepts that should be separate, specifically the shape of the information that we have (e.g. what fields of what types), and whether a particular bit of information is available and required (e.g. nullability).
He contends that the former belongs in our usual "type definition", whereas the latter relates instead to a given context. For example, my PassportForm type always has a date-of-birth field in its _shape_, but whether it's statically required/present to exist depends on whether we're at a HTTP API boundary, an internal domain function boundary, writing into a database.
It sounded like that kind of "nullability masking" was intended as a feature of Spec, but I don't get the impression it was ever implemented.
didibus
7 months ago
I think they were ideas being experimented for Spec2, but I think that's a bit on a hiatus.
rkangel
7 months ago
I need to watch this video, but I'd argue that the debate is over on typing. You can make all the theoretical ideas you want, but JS and Python have added typing, it has been received enthusiastically and it has been beneficial. Real world results out rank any philosophical views.
That doesn't mean they're appropriate for all programming languages (it is nice in Python when knocking up a quick and dirty script to not bother with types) but for general purpose languages, types add too much value.
drob518
7 months ago
I don’t think Rich was criticizing static types as much as saying that they aren’t giving you as much benefit as you think they are and that they complicate program evolution over time.
lenkite
7 months ago
They might not give much execution benefit and they may indeed complicate program evolution, but they DO aid readability, document-ability and refactoring!
ljm
7 months ago
I don't think static types (or lack thereof) complicate (or simplify) program evolution over time, bad architecture does.
alternatex
7 months ago
Still quite the hot take.
drob518
7 months ago
Indeed.
fithisux
7 months ago
If you need confidence in the operation of a function you make code testable. If you need it to execute in Repl you need to make code Replable and I am not joking.
KingMob
7 months ago
Heh. Hickey once debated with me at length about visual neuroscience, a subject I have a master's degree in and he doesn't. At no point did this stop him from confidently asserting things.
I have to wonder if "Maybe Not" is similar, since he's not actually an expert in types, either afaik.
Alexis King wrote a partial rebuttal to Maybe Not: https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-typ...
richhickey
7 months ago
Well, at least I know how to form an argument that is not ad hominem. Remind me who you are next time 'KingMob' so I don't waste my time chatting with you again.
roenxi
7 months ago
I'd personally say typing or not is a style choice, but your criticism here seems to be that Hickey doesn't have a visual neuroscience Master's degree which seems a bit arbitrary.
If your argument is you are an expert but Hickey is not, criticising him on his language design skills seems like a logical mistake. He's one of the foremost language designers of the current era. "Maybe Not" is a speech by an expert talking in his field of expertise.
If your argument is that his confidence is unfounded, again, he's an expert talking in his area. He can reasonably take a confident attitude in that, even if he has unfounded confidence in other fields he isn't an expert in. Lots of experts do that, it is a well founded stereotype of smart people.
He doesn't need to be an expert in implementing types to judge whether they are a good language feature.
KingMob
7 months ago
The point of my story that you're missing is that Hickey's confidence in giving a talk shouldn't be confused with knowing what he's talking about.
> "Maybe Not" is a speech by an expert talking in his field of expertise.
Writing a dynamically-typed language does not mean he's an expert in types.
> He doesn't need to be an expert in implementing types to judge whether they are a good language feature.
Hah! Love it.
Asimov: "There is a cult of ignorance in the United States, and there has always been. The strain of anti-intellectualism has been a constant thread winding its way through our political and cultural life, nurtured by the false notion that democracy means that 'my ignorance is just as good as your knowledge.'"
roenxi
7 months ago
> Writing a dynamically-typed language does not mean he's an expert in types.
That isn't a reasonable criticism here. To take it to the extreme, it wouldn't be accepted that someone has to be an expert in homeopathy before deciding they want to disregard the approach. It is up to experts to proactively demonstrate the worth of their domain - he's a language design expert and he's saying that he doesn't think the people formalising the type system have proven it generally powerful for designing new languages.
He's not claiming to be an expert in types, in "Maybe Not" he's specifically arguing that in programming languages they are an attempt to solve a problem where we should be looking for a stronger approach. And in this case, that approach is open schemas.
KingMob
7 months ago
Homeopathy can be quickly disproved, because its core tenets violate basic physics. Expertise is not actually needed.
Whereas type systems are complex enough that "a monad is just a monoid in the category of endofunctors, what's the problem?" is a recurring in-joke.
That suggests expertise is warranted.
roenxi
7 months ago
> Whereas type systems are complex enough... [t]hat suggests expertise is warranted
Well no hang on. Homeopathy is very complex; quackery typically is. You can't use complexity as a defence here [0].
You justified rejecting homeopathy by saying that it can be dismissed with expertise in an unrelated field; so you have to accept that a non-expert can dismiss a field based on general knowledge in other domains. Which is almost exactly what Rich was doing in Maybe Not; except he is actually a domain expert in the area he was talking from. It was a long talk about how he, with his official world-renowned language designer hat on, is not convinced types can demonstrate the strongest advantage in programming and isn't including them in his language [1].
Now that is a very easy opinion to disagree with and I doubt Rich is even particularly dogmatic about the idea, but it is an expert opinion being delivered by an expert who is supremely qualified in his field.
[0] And, as an aside, I'm not going to pursue it but you're attempting to justify dismissing a medical belief based on knowledge in an unrelated field - physics.
[1] Although, as he is proud to point out, if you want a new language feature in Clojure you can add it in a library - as Ambrose has with Typed Clojure.
KingMob
7 months ago
> Homeopathy is very complex
But it's not complex at all; a better word is "elaborate". And it doesn't matter how elaborate a system spun from incorrect premises is, if the premises fall, the whole thing does, and homeopathy's premises are easily dismissed.
Very basic tenets of homeopathy involve things like excessive dilution to the point that mere molecules of an active ingredient remain, but which somehow attach "memories" to water molecules to achieve their effects.
Atomic physics points out that this is nonsense. And medical knowledge is under-girded by physics, obviously, so it's not unrelated.
----
We can keep torturing the analogy if you like, but at the end of the day, Hickey's experience is not in static typing. His original background was in C++/Java, which have much simpler type systems, unlike Haskell/ML/Rust/Scala/Swift/etc.
Many people in the static typing world seem to think his criticism is just not that deep or subtle. Unnamed union types exist, existed at the time of Maybe Not, and just represent a different set of tradeoffs than named wrapper ones like Maybe. (Mostly around being explicit.)
It plays well at the conj, but most Clojurians are also inexperienced with modern type systems.
spjt
7 months ago
Being an expert in type theory or language design is not particularly relevant either. The most relevant expert would be the lowly maintenance developer.
raspasov
7 months ago
This "rebuttal" is a mix of subtle ad hominem attacks and strawman arguments. Not recommended.
nathell
7 months ago
If you can run it, then you can REPL it, no matter how deeply nested. Scope-capture (https://github.com/vvvvalvalval/scope-capture) has been probably the most important tool in my box. Hope jank supports it eventually.
Thanks for jank! It’s great to be reading about it, listening to you talking about it at conferences, and I can’t wait to try it out!
fithisux
7 months ago
Core spec is enough. Types do not tell the truth. Contracts do the data type casting and data testing. A ubiquitous combo in data engineering.
barrell
7 months ago
> pattern matching, value-based errors
I did not know these were in the cards, that makes jank even more exciting!
barrell
7 months ago
My comment to code ratio is magnitudes higher in Clojure than in other languages, which helps a lot with this.
Also writing Clojure can be incredibly terse, resulting in quite high-effort when reading. Conversely, a lot of time I can condense hundreds of lines of equivalent python into 5 or 6 lines of Clojure. Having all of this functionality condensed into something you can fit in a tweet really helps for grokking larger parts of the dataflow or even larger system. So there are tradeoffs
Plus structural editing and the repl really help with the “reading” experience (reading in quotes because it’s much more interactive than reading)
NeutralForest
7 months ago
> Conversely, a lot of time I can condense hundreds of lines of equivalent python into 5 or 6 lines of Clojure.
I'm curious if you have any example of this? Even if it's an hyperbole, I don't really see how.
d4mi3n
7 months ago
In my (limited) experience with Clojure and other functional languages, this is usually true under situations where:
1. You’re mapping or reducing some dataset
2. Your iteration logic does not branch a lot
3. You can express your transformation logic using higher order functions (e.g. mapping a reduction operation across a multidimensional array)
Some domains have a log of this style of work—finance comes to mind—others do not. I suspect this is why I’ve personally seen a lot more of Clojure in finance circles than I have in other industries.
barrell
7 months ago
Maybe hyperbole on the frequency, but not the condensation. I meant more along the lines of “most of the complicated code I write in Clojure is an order of magnitude more dense.” _Most_ of the code I write would be 1:1 or 1:2 with other languages, it I don’t think it’s the type of code OP was referring to.
The 1:20+ is definitely not hyperbole though. Using transducers to stream lazy reductions of nested sequences; using case, cond-> and condp->; anywhere where you can lean on the clojure.core library. I don’t know how to give specific examples without giving a whole blog post of context, but 4 or 5 examples from the past year spring to mind.
It’s also often the case that optimizing my clojure code results in a significant reduction of lines of code, whereas optimizing Python code always resulted in an explosion of LoC
Personally I find Python particularly egregious. No map/filter/reduce, black formatting, no safe nested property access. File length was genuinely one of the reasons I stopped using it. The ratio would not be so high with some languages, ie JavaScript
Even with Elixir though, many solutions require 5-10 times the amount of lines for the same thing thing in Clojure. I just converted two functions yesterday that were 6 & 12 lines respectively in Clojure, and they are both 2 pages in Elixir (and would have been much longer in Python)
geokon
7 months ago
I find 95% Clojure has the right tools to write very terse code. But in some cases the functional transducer/piped paradigm can't be contorted to the problem.
Usually these are problems where you need to run along a list and check neighboring elements. You can use amap or map-indexed but it's just not ergonomic or Clojure-y (vs for instance the imperative C++ iterator model)
The best short example I can think of is Fibbonacci
https://4clojure.oxal.org/#/problem/26/solutions
I find all the solutions hard to read. They're all ugly. Their performance characteristics are hard to know at a glance
barrell
7 months ago
Personally, I would normally reach for loop to check neighboring elements very ergonomically.
(loop [[a b c & more] coll] (recur (apply list b c more)))
There’s also partition if you're working with transducers/threads/list comprehension (partition 3 1 coll)
Or if you need to apply more complicated transformations to the neighbors/cycle the neighbors (->> coll cycle rest (map xform) (map f coll))
Using map-indexed to look up related indices is something I don’t think I do anywhere in my codebase. Agreed that it’s not ergonomicEDIT: those Fibonacci functions are insane, even I don’t understand most of them. They’re far from the Clojure I would advocate for, most likely written for funsies with a very specific technical constraint in mind
geokon
7 months ago
Yeah, I guess partition to me always looks like a dangerous tool - for instance I have a sequence of numbers and I want to do a 5 point rolling average
You could do `(partition 5 1 coll)` and then average each element in the resulting seq.. It's very easy to reason about. But I'm guessing the performance will be abysmal? You're getting a lazy list and each time you access a 5 neighbor set.. you're rerunning down you coll building the 5 unit subsets? Maybe if you start with an Array type it'll be okay, but you're always coercing to seq and to me it's hard
Taking the first 5 elements, recurring on a list with the top element dropped is probably better, but I find the code hard to read. Maybe it's a familiarity issue..
barrell
7 months ago
Yeah like I said I reach for loop first and foremost. This is what it would look like with comments if it were actually something complicated (although the comments are quite trivial here):
(loop [coll [1 2 3 4 5 6 7 8 9] memo []]
(if (< (count coll) 5) memo ;; Stop once there are less than 5 items
(->> (take 5 coll) ;; Take the first 5 days
(reduce +) ;; Sum them
(* 0.20) ;; Divide by 5
(conj memo) ;; Append to the end of the memo
(recur (rest coll)) ;; Recur for remaining elements
,,,))))
Realistically if performance was a consideration I would probably do: (loop [[a b c d e :as coll] [1 2 3 4 5 6 7 8 9] memo []]
(if-not e memo
(recur (rest coll) (conj memo (/ (+ a b c d e) 5))))))
Should be ~15 times faster to avoid the nested loop. If you want to change the min size it's still pretty clean: (loop [[a b c d e :as coll] [1 2 3 4 5 6 7 8 9] memo []]
(if-not c memo
(recur (rest coll) (conj memo (/ (+ a b c d e)
(cond e 5 d 4 c 3))))))))geokon
7 months ago
Thank you for the detailed response! Really thought provoking. It wouldn't occur to me to write code like this. It seems like it'd be harder to parse than an imperative index-based solution, but I'm not sure. Do you find it easy to immediately grok? I'm figuring it's just familiarity
- What's the nested loop in the first solution that you've avoided? The `reduce`? the `count`?
- `conj` feels very Lispy (I mean in contrast to Clojure, not C++) .. Isn't it going to have to run down the list every time to add an item?
My outstanding concerns are what I think are the constant coercion to lists/vectors. You also in effect know the result's size, but your runtime/compiler doesn't know that. So you aren't preallocating `memo` and it feels .. wrong haha
Just curious to hear your thoughts :)
Its probably impossible to keep everything so nicely abstract and composable, but I wish it was smoother to just work with arrays, with random access. The current way of dealing with array is always a bit unwieldy in Clojure. And everything coerced to lists. Working with vectors, with mapv filterv etc is helpful, but they don't have random access so it's not always the solution you want.
barrell
7 months ago
> It seems like it'd be harder to parse than an imperative index-based solution, but I'm not sure. Do you find it easy to immediately grok?
These examples are incredibly easy to grok. It takes me a lot longer to grok any index based solution ;-) Especially handling nils, missing indices, checking bounds... you quickly get buried in obvious issues that aren't a concern in clojure.
If it's more complicated, I use comments like the first example. I would say that when grokking more complicated loops, you definitely read it slower on a per character basis, but as I've stated elsewhere I don't think you're grokking the functionality any slower.
> What's the nested loop in the first solution that you've avoided? The `reduce`? the `count`?
Correct, reduce is a loop
> `conj` feels very Lispy (I mean in contrast to Clojure, not C++) .. Isn't it going to have to run down the list every time to add an item?
No, vectors are closer to hash-maps than lists. There are two main sequential collections in clojure, lists and vectors. Lists are O(n) to append, vectors are O(1). Lists ore O(1) to prepend, I'm not actually sure the perf characteristics of prepending to a vector.
> My outstanding concerns are what I think are the constant coercion to lists/vectors.
There is only one coercion from a vector to a list in the code (the first time `rest` is called on the input vector). That's if your input was a vector to begin with. Chances are it will be a list. Also, I'm not sure it's even technically coercion in the computer science sense.
> You also in effect know the result's size, but your runtime/compiler doesn't know that. So you aren't preallocating `memo` and it feels .. wrong
This is not a concern in JavaScript (it always preallocates a bunch of memory for every vector IIRC) and I'm not sure the Java implications, but given the performance people get out of clojure I'm sure it's not an issue ;-)
> The current way of dealing with array is always a bit unwieldy in Clojure. And everything coerced to lists. Working with vectors, with mapv filterv etc is helpful, but they don't have random access so it's not always the solution you want.
I can count on one hand the number of times I use nth in my project. To me it's crazy that people build solutions based on random access XD
That being said random access in O(1) on vectors, including those returned from filterv and mapv (technically O(log32n))
geokon
7 months ago
Oh good point on the hashmapiness of vectors, I forgot about that. That would really change the calculus here.... buuuut
> There is only one coercion from a vector to a list in the code (the first time `rest` is called on the input vector)
So actually.. the whole thing is operating on linked lists.. Is that like a "yikes" moment..? No happy clever vectors? It does seem to still work fine in this case, b/c we pop from one list and append to another and the example is simple.. but generally you want the O() guarantees of vectors or arrays
I'm also a bit surprised reduce is going to give you so much overhead, and destructuring doesn't. It really hardcodes the function to a window size which is a shame.. Last I had to optimize a hot loop in Clojure I found all destructuring terrible for perf (but I think it had to do with type boxing)
> I can count on one hand the number of times I use nth in my project.
Random access is sometimes necessary, but like you say hash-like-vectors cover you here okay. I guess arrays are more about having contiguous memory access. It generally gives you much better cache locality and it will typically make a very sizeable performance difference in a hot loop
barrell
7 months ago
Well it’s only iterating on lists because I’m recurring with a function that returns a list. I could use
(recur (subvec coll 1) …)
If I needed to maintain the vector throughout the loop. However you need to change your base case and there’s no reason to have a vector at all in this case.There is a reason most of the Clojure core functions return lists and not vectors. When you start learning Clojure, you use vectors for everything — after a while you realize how sufficient a list is for 90% of programming.
Also, I believe you could still get the performance guarantees and dynanicism with a dotimes and a volatile. You can definitely get it using nth on the remaining collection with a conditional if you don’t mind a bit of verbosity. Regardless of the goalposts, I promise you there is an ergonomic solution ;-)
geokon
7 months ago
I've been writing it for >5 years and I still reach for vectors haha. I think it's my C++ background which makes me a bit allergic to lists. The reality is that if I moved more transducer based solutions this would be a non-factor most of the time. (unfortunately this example doesn't suit transducers very well)
I gotta use transducers, lists and benchmarking more..
Thanks for sharing how you approach these problems
geokon
7 months ago
I thought another option would be to use a `partition-all` transducer (and conj'ing a result list that way), but it seems those don't support step sizes. Maybe that internally could be as efficient as running along iterators in an imperative style
https://ask.clojure.org/index.php/2422/transducer-for-partit...
https://ask.clojure.org/index.php/13187/there-currently-opti...
https://clojure.atlassian.net/browse/CLJ-2797
Hopefully gets added to the language some day..
drob518
7 months ago
Exactly. I would use loop or partition+map/reduce for that case. I almost never use map-indexed. In fact I almost never use indexing at all. Mostly, when I have a sequential collection (vector, list, or generic seq), I need to iterate over all the elements, and I’m doing that with map or reduce. IMO, map-indexed has a code-smell that indicates that you’re reaching for an imperative algorithm when perhaps a functional algorithm would be better. Surely, there are times when map-indexed is just what you need, which is why it’s there, but typically not in my experience.
barrell
7 months ago
I would agree that map-indexed + *nth* is a code smell. I use map-indexed all the time though, just when I need the index, not other elements in the list
NeutralForest
7 months ago
Maybe I'm just too used to Python (and I only know some Clojure) but I don't have the same experience. Usually using generators and itertools will really help you shorten your code. I'm working in a data science adjacent field so a lot of code is just frameworks anyways but I don't feel limited in pure Python either.
If you come across a post or an example that shows those differences, I would be very interested!
cess11
7 months ago
Could you show an example or two between Elixir and Clojure?
barrell
7 months ago
This is not the best example, it's just the most recent example (what I was doing last night) that can fit in one screen:
(defn report [date]
(let [[d w m q y] (-> (comp tier* recall* (partial c/shift date :day))
(map [1 7 30 90 365]))]
(reduce (fn [memo {:keys [card code]}]
(cond-> memo
true (update code (fnil update [0 0 0 0 0 0 0 0 0 0]) (q card) inc)
(<= 4 (d card)) (update-in [code 6] inc)
(<= 4 (w card)) (update-in [code 7] inc)
(<= 4 (m card)) (update-in [code 8] inc)
(<= 4 (y card)) (update-in [code 9] inc)))
{}
(k/index :intels)))))
The elixir code I was able to condense down into: def report(facets, intels, day) do
[d, w, m, q, y] = for x <- [1, 7, 30, 90, 365], do: Date.shift(day, day: x)
Enum.reduce(intels, %{}, fn intel, acc ->
facet = Map.get(facets, intel.uuid, :zero)
[q0, q1, q2, q3, q4, q5, d4, w4, m4, y4] =
acc[intel.code] || [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
quarterly_tier = tier(facet, q)
Map.put(acc, intel.code, [
if(quarterly_tier == 0, do: q0 + 1, else: q0),
if(quarterly_tier == 1, do: q1 + 1, else: q1),
if(quarterly_tier == 2, do: q2 + 1, else: q2),
if(quarterly_tier == 3, do: q3 + 1, else: q3),
if(quarterly_tier == 4, do: q4 + 1, else: q4),
if(quarterly_tier == 5, do: q5 + 1, else: q5),
if(tier(facet, d) >= 4, do: d4 + 1, else: d4),
if(tier(facet, w) >= 4, do: w4 + 1, else: w4),
if(tier(facet, m) >= 4, do: m4 + 1, else: m4),
if(tier(facet, y) >= 4, do: y4 + 1, else: y4),
])
end)
end
It was much longer prior to writing this comment (I originally used multiple arity helper functions), but it was only fair I tried my best to get the elixir version as concise as possible before sharing. Still 2x the lines of effective code, substantially more verbose imho, and required dedicated (minor) golfing to get it this far.Replacing this report function (12 lines) + one other function (6 lines) + execution code (18 lines) is now spread across 3 modules in Elixir, each over 100 lines. It's not entirely apples to oranges, but trying to provide as much context as possible.
This is all just to say that the high effort in reading it is normally a result of information density, not complexity or syntax. There are real advantages to being able to see your entire problem space on a single page.
lisbbb
7 months ago
That's great, and I agree, but nobody really cares is the problem. They don't care about brevity and LISP is a really hard sell outside of those who "get it."
beders
7 months ago
You need a REPL to truly read Clojure code. Could be a weakness or could be a strength. In my day to day work I consider a strength since I’m working at the REPL the whole day anyways
lisbbb
7 months ago
It's not difficult to read once you get writing it. My problem was getting other developers on board with it, which, ultimately, I failed at.
no_wizard
7 months ago
Why call it jank? It is a negative associated word in most contexts that’s why I’m curious about it
amelius
7 months ago
I guess it stands out. "Git" is similar. "Rust" isn't a very positive word either. Perhaps it's a new trend. Maybe the answer is "all the good names have been taken" and/or they are simply lazy.
graemep
7 months ago
Neither is as negative. This sort of name is more like Gimp.
It is definitely a lot less negative than Nimrod (which is actually positive in origin - but Americans do not get biblical references) which changed its name.
You can also get away with a lot more if you are Linux Torvalds or Mozilla.
Quarrelsome
7 months ago
all code is somewhat jank though.
While jank is technically a negative term, its quite playful as opposed to scathing. My favourite usage was in MTG where large control decks that just slap together strong cards are referred to as "jank piles".
barrenko
7 months ago
I view "jank" similarly to "cracked", not necessarily negative.
bgro
7 months ago
Yep I won’t use anything with a negative self deprecating name like this. Because some tech bro will use it as a a basis to disqualify my entire resume or sabotage an interview after solving the leetcode trivia troll questions and whatever other video game battles they add to the interview process in the future.
Project manager fires the entire team except 1 intern to finish the project with 1000 points of stories in 1 sprint? Heh or did you just figure out jank wasn’t capable of doing the job what did you expect?
Hotfix to fix a bug with the stage environment because the SREs set it up wrong? No bro it’s jank it’s that jank thing. Source: ctrl F “jank” in the message analytics and copilot says all matches are in the stage environment and that jank is also a tech thing. It also bright up every engineers profile that lists jank as a skill. Time to pick a scape goat.
athorax
7 months ago
Is tech bro in the room with us right now?
binary132
7 months ago
I’m a bit curious why you chose to implement this as a different language (even though it implements Clojure) instead of an alternative Clojure backend and/or C++ syntax extension.
Do you plan to make Windows support first-class? I think a lot of people looking at LLVM based languages are interested in alternatives to C++ for games.
Jeaye
7 months ago
> I’m a bit curious why you chose to implement this as a different language (even though it implements Clojure) instead of an alternative Clojure backend and/or C++ syntax extension.
jank is Clojure. However, the Clojure name is trademarked and using it requires permission which I don't have. Furthermore, I want to build upon the Clojure base to provide more, going forward. That may include static typing, value-based error handling, first class pattern matching, and so on. Those would be opt-in features on top of Clojure. All of these reasons lead me to not use Clojure in the name (like Clojure++, ClojureNative, etc).
> Do you plan to make Windows support first-class? I think a lot of people looking at LLVM based languages are interested in alternatives to C++ for games.
Indeed, a lot of game dev folks use Windows. Right now, jank's Windows support is limited. My initial audience is Clojure devs who want native access and lighter binaries. Once that launch has stabilized, I will focus on appealing to existing native devs who want to embed an interactive, functional language into their C++ applications. That will requires strengthening the Windows support, establishing stable native APIs, and writing the onboarding material for lisp, REPL-based editing, data-driven design, and so on. This is a much larger task, which is why I'm focusing on existing Clojure devs first.
neutronicus
7 months ago
I commented to this effect on Reddit, but my interest is entirely conditional on ability to embed Jank into a pre-existing C++ application as a shared library.
Ideally without controlling the code of the main application (e.g. to implement a plug-in).
Jeaye
7 months ago
Yep, this will be an important use case and will be officially supported. For the first alpha release this year, I'm focusing on Clojure devs, but support for existing native devs will come once things stabilize.
twism
7 months ago
Shouldn't it be an 'if' instead of 'when' in the first example?
Jeaye
7 months ago
Yes it should. Thanks for the keen eye and taking the time to point that out.
zamalek
7 months ago
I'm very excited about jank, and it's on my backlog.