Why Janet? (2023) (ianthehenry.com)

338 points by yacin 7 hours ago

krinne 7 hours ago

This post is refreshing - smells of the pre AI discussions on the internet. A new language, a new syntax, heavy debate with people who have spent years writing code. I think someone should start a community online where AI isnt allowed.

probably_wrong 7 hours ago

> I think someone should start a community online where AI isnt allowed.

In case you haven't followed the saga, the latest[1] digg.com relaunch failed because they couldn't deal with the bot onslaught [2]. Whoever finds a reliable way to keep AI out of an online community first is likely to become a very rich person.

[1] Second-to-last, actually, seeing as there seems to be a new homepage right now.

[2] https://www.techspot.com/news/111698-digg-relaunch-fails-two...

andrewflnr 2 hours ago

I suspect real ways to keep AI out of a community, or really to have an online community at all, are going to be structurally incompatible with making anyone rich. The possibility of getting rich poisons the incentives.

flir 5 hours ago

We've already got models that can handle it - eg web of trust. We don't want to use them.

Plus "AI" is a spectrum, with "the AI fixed a typo for me" at one end, and "the AI writes my posts for me" at the other.

makkes 3 hours ago

layer8 4 hours ago

> Whoever finds a reliable way to keep AI out of an online community first is likely to become a very rich person.

I believe it’s the opposite: You have to pay competent human moderators. Like here on HN.

LtWorf 3 hours ago

dwedge 6 hours ago

> In case you haven't followed the saga, the latest[1] digg.com relaunch failed because they couldn't deal with the bot onslaught [2]

Given that they wrote their goodbye post using LLMs and gave up after such a short amount of time, I don't take that at face value the same way I don't believe AI layoffs

Cassell an hour ago

Altman’s orb is as terrifying as it is because businesses might see it as a solution to a real problem—a problem he helped to create.

ponkpanda 5 hours ago

Perhaps requiring webauthn credential for any post/comment with a whitelist of permitted webauthn hardware devices which must have touch/interaction enabled.

I'd have to read the FIDO specs, however the only place I've seen webauthn hardware pinning in the wild is with Azure AD/Entra which is ostensibly based on token GUID. If this is the only enforcement mechanism available, it's spoofable.

nkrisc 4 hours ago

tialaramex 5 hours ago

wbl 3 hours ago

layer8 4 hours ago

21asdffdsa12 5 hours ago

Pay per interaction model? 1 cent per post.

intrasight 3 hours ago

kitd 4 hours ago

Age verification with facial ID? ;)

geokon 6 hours ago

lobste.rs uses a web-of-trust referral system. I guess it still involves a moderator killing off bad nodes, but it seems to scale well

dust-jacket 5 hours ago

pbronez 5 hours ago

Isn’t the solution high-quality identity verification? There are piles of digital identity companies out there. They make money selling to banks for KYC compliance. Heck, there are background check as a service companies designed to add trust to gig economy platforms.

I used to think that a small payment could accomplish the same thing, but X selling blue check marks proved that doesn’t help much. Well, at least it’s a much weaker signal than the previous curated version.

The challenge is any barrier to entry high enough to discourage motivated spammers is also high enough to discourage casual users. That disrupts the network effects you’ve traditionally needed to bootstrap a social website.

If I was trying to get a new social site off the ground right now, I would try:

1) secure a good brand from the pre-AI era. Twitter, Digg, Friendster, MySpace. Something that motivates a first look.

2) Require third party identity verification on sign up, configured so the social site is never the custodian of PII, though require enough demographics to support high-value advertising later. Verification is free to the user, ideally provide multiple verification options- one US and one EU at minimum.

3) Target a few core communities and invest. Find the people who moderate historically great subreddits, were active in twitter communities during the good years, etc. get them in your platform. Maybe even pay them.

That should be enough to tell you if it’s going to work or not.

Decabytes 3 hours ago

Blizzard's Real ID system would fix all of this. It was ahead of it's time /s

wmedrano 33 minutes ago

Ironically, the top comment (this) is now about AI.

matheusmoreira 3 hours ago

> I think someone should start a community online where AI isnt allowed.

That's lobsters I guess. AI posts got banned there after a 300+ comment discussion, probably the biggest ever on the site.

The exact rule the moderators settled on was "meaningful human authorship" but don't be fooled: a lot of people on lobsters are ideologically opposed to LLMs. Doesn't matter how "meaningfully" the technology was applied. My work was classified as slop simply because AI touched it. People referred to me as an exhibitionist and fetishist when I talked about using AI. Just a heads up for anyone who's thinking of joining.

crumpled 3 hours ago

on my way!

9dev 5 hours ago

> a community online where AI isnt allowed.

This is something I think about a lot, especially how one could pull it off without tearing down anonymity online. Having some sort of "proof of humanity" is a hard problem to solve.

mr-pink 4 hours ago

not really, you can just ask people to do a shit job identifying traffic lights and motorcycles and they'll do it.

DoughnutHole 6 hours ago

The amazing thing about AI is that you don’t even need AI superfans to shoehorn it into a conversation that doesn’t even touch on AI. Detractors will do it for them.

NuclearPM 6 hours ago

Yes, it’s similar to Trump. But that makes sense right? AI is changing the world drastically, and so is Trump and his fascist friends.

shevy-java 6 hours ago

> a new syntax

How is the syntax new?

It looks like lispy - see the outer parens in the examples given.

embedding-shape 6 hours ago

Heh, every time you show a average developer lisp for the first time the reaction is the same. Little do they know conditionals, GC, REPLs, macros and more comes from the syntax and language dreamed up in the 50s/60s.

maleldil 4 hours ago

soomtong 7 hours ago

It’s been a few months, but I built a tool by Janet lang to communicate with an LLM via HTTP. Of course, I probably had Claude Code write it for me. It turned out better than I expected.

I was really impressed by how small the executable file was. I’d only ever done web development with Node.js up until then.

janetacarr 3 hours ago

I have my qualms with Janet. Mostly, it's lack of package management versioning and lack of libraries in general (advanced HTTP routing, etc).

I do LOVE that Janet can create binaries with JPM, scripts, and is very portable. I once put the Janet programming language on the Playdate game console as POC.

I actually do enjoy writing Janet, but every time I do people think I created the language (I did not).

veqq 29 minutes ago

> advanced HTTP routing

What do you concretely mean by this? I use https://github.com/joy-framework/joy for all web stuff and can probably get your missing features in within the week.

FelipeCortez 3 hours ago

Julia Evans has a fun blog post using Julia to visualize Gunzip: https://jvns.ca/blog/2013/10/24/day-16-gzip-plus-poetry-equa...

you should totally do a "Janet writes Janet" version

1313ed01 5 hours ago

There is also fennel, earlier language originally by same developer, that is similar, but compiles to, and is fully implemented in, Lua. No standard library of its own so missing many nice things like the parser library from janet, but it is good for writing scripts for things that embed Lua.

https://fennel-lang.org/

ux266478 3 hours ago

Fennel really is great, and a great way to get into the clojure family. My biggest gripe with it is that debugging is the typical transpilation bed of needles. The bridge between Fennel and the Lua VM is super fragile, and it just doesn't have half the quality of the Janet debugger and REPL. It's a real shame, because Fennel is way more portable, and thanks to LuaJIT is capable of breaking SBCL's jaw, which is absolutely fucking insane. But the transpilation experience just completely kneecaps it imo. There are workarounds you can do, but even if you mess around with implementing a debug.setinfo you still run into less-than-fun edge cases like with match-blocks.

I think there's a lot of value in forking LuaJIT2 and reworking the debugging and error structures within to make it more suitable for language transparency. Doing so would make languages like Fennel much more attractive.

chamomeal an hour ago

Is fennel clojure-ish? I’m vaguely aware of it being a lisp, but I didn’t know it was clojure-ey. Pretty neat sounding!

veqq 26 minutes ago

JHonaker 3 hours ago

> capable of breaking SBCL's jaw

What exactly do you mean by this? Speed? Portability? Ease of use?

ramblurr 6 hours ago

Always nice to see janet getting some attention.

shout out to one modern feature: sandbox

"Disable feature sets to prevent the interpreter from using certain system resources. Once a feature is disabled, there is no way to re-enable it."

https://janet-lang.org/api/misc.html#sandbox

declan_roberts 4 hours ago

It's a really cool feature but what is a scenario when your average programmer needs such sandboxing?

myaccountonhn 4 hours ago

I sandbox all my utilities and programs in case some compromised third-party dependency decides to run lose. It's a way to limit the blast radius.

petee 3 hours ago

The TIC-80 game engine embeds Janet, and if i recall sandboxes the games created

briaoeuidhtns 4 hours ago

you're embedding it as a scripting api and want to limit permissions to just what's needed

xrd 5 hours ago

The author made these using Janet (discussed on HN in the past):

https://bauble.studio

https://toodle.studio

Those two fascinating art tools got me very excited about Janet a while back.

a-french-anon 5 hours ago

> SETQ is def

At first I said "what" out loud, since SETQ doesn't create bindings, it only updates them then I read the doc (https://janet-lang.org/docs/bindings.html) and the author is indeed wrong ("bindings created with def are immutable"). He probably meant "SETQ is set".

I really want to like Janet, as it seems to be the sweet spot between Guile, Tcl and CL (minus the speed/maturity of SBCL) but I have a visceral reaction to square brackets (so vectors) being used in lambdas and control flow operators. Same as Clojure, I simply can't get over it. Maybe I will with enough effort?

Also, what's the current LSP/SLIME status? Really important these days.

nlitened 4 hours ago

Square brackets’ use is very consistent and rather logical in how they are used in Clojure’s syntax.

When round brackets are used, the first element in the list defines how the rest of the list is interpreted, for example:

(func a b c) — run a function with its parameters

(macro x y z) — expand a macro with its parameters

([p q r] …) — “bare” function body that starts with a vector of parameters, and executable forms follow.

Square brackets are used where elements are the same “kind”, and the first one is not special, e.g.:

(defn f [a b c] …) — a collection of same-kind parameters, the first parameter is not special

(let [a 1 b 2] …) — a collection of bindings, the first binding is not special

The only exception that comes to mind is grouping multiple matching elements in `case`, but it for ergonomics.

Once I got the logic, when which is used, I changed my mind, and ever since I’ve felt it’s beautiful.

a-french-anon 3 hours ago

Thanks a lot, that's a very interesting way of seeing it.

veqq an hour ago

You can... just not use square (and curly) brackets. Instead of `[1 2 3]` just write `(array 1 2 3)`. Instead of `(fn [x] (+ 1 x))` just write `(f (x) (+ 1 x))`. They are never necessary.

BoingBoomTschak 16 minutes ago

Huh! So like some Schemes and Racket? Yet I must read them in code that isn't mine, which is a large part of the problem.

mackeye 5 hours ago

janet has replaced sh, python, awk, etc. for me, for system scripts over a certain length! it has a very fast startup time (on my system, 1.4ms via hyperfine vs. 1ms for dash) for scripts (not compiled executables), and its sh-dsl module allows typing shell commands very elegantly, like ($ cmda w x | cmdb y z). the ability to load an image to debug is a big help, too. i've started using it very recently but it's probably one of my favorite languages now, and the only other lisp i've used is mit scheme for sicp.

soomtong 5 hours ago

This document was really helpful when I first met Janet:

https://janetdocs.org/tutorials

https://janet.guide/ (the author's one)

veqq 37 minutes ago

I'm really happy you liked it! It's still a work in progress.

uka 7 hours ago

> But by allowing you to unquote literal functions, Janet makes it possible to write macros that are completely referentially transparent.

These lisp guys really get excited over very abstract things. If you say this to an average person on the street they will probably try to run away.

fredrikholm 5 hours ago

> very abstract things

A C macro with literals that lacks referential transparency:

  #define MULTIPLY(x, y) x * y
  int result = MULTIPLY(2 + 3, 4); // 14
Not knowing what something means does not make it bad, which is what I'm assuming you meant given how you phrased your sentence.

Having a shared language of patterns and problems that occur in programming is a good thing. Ridiculing such terminology on the basis of "that group of programmers sure are weird" is pointless and counter productive.

uka an hour ago

Thank you for the lesson. Very productive of you and on point.

Now if you relaxed just a little bit - the world would be much nicer place.

bryanrasmussen 7 hours ago

you ever try to explain object oriented programming languages and their benefits to the "average person on the street"?

rambrrest 7 hours ago

somehow i also never got the idea around these languages like lisp. I remember i studied them in school - but i quickly forgot and never got around to relearning it.

barrell 6 hours ago

xigoi 7 hours ago

zelphirkalt 6 hours ago

skeledrew 5 hours ago

Pretty straight-forward, as the world as we perceive it is made of objects with attributes, interacting with each other via their methods. OOP easily fits the brain of the average person in the street.

prerok 6 hours ago

Average programmer too /j

Frankly, though, I think lispy community has benefited from being smaller. For example, even though the now ancient Design Patterns already warned programmers to prefer composition over inheritance, the OO programmers still created 15 levels deep hierarchies.

the_af 4 hours ago

> These lisp guys really get excited over very abstract things. If you say this to an average person on the street they will probably try to run away.

Referential transparency is a funny name for a very powerful feature which helps you understand what the program does better, it's not a deeply abstract thing. Don't let the name scare you.

You could ask "why the funny name"? Well, specialized professionals use specialized jargon, even for "normal stuff". It's unreasonable to expect otherwise. Car mechanics also have weird names for car parts that are absolutely essential for the car and not that hard to understand if they explained them to you.

wodenokoto 7 hours ago

I've been drawn into the Janet posts that surface every once in a while here on HN, but found the otherwise highly praised "Janet for Mortals", not being for mortals at all.

veqq 35 minutes ago

I have some gentler introductory stuff like: https://janetdocs.org/tutorials

lelanthran 6 hours ago

> I've been drawn into the Janet posts that surface every once in a while here on HN, but found the otherwise highly praised "Janet for Mortals", not being for mortals at all.

I'm surprised: the language is very straightfoward, simple, very few rules to remember. It's a Lisp but with a very small surface area.

I mean, compared to other languages, Janet really is easier to lean, so I'm surprised that the book for it is difficult (did not read the book, but familiar-ish with the language. I don't have anything but praise for it, TBH).

petee 6 hours ago

Personally I get hung up on the macro syntax being near the beginning, but there is so much valuable stuff past that

shevy-java 6 hours ago

> not being for mortals at all.

I had that with Haskell. Although, while Haskell is too hard for me, I actually like its syntax.

Janet seems to be Lisp 2.0, so the syntax is lispy.

0x0203 6 hours ago

Seems some of the listed advantages for Janet would also apply for tcl (small/simple, easy to learn, embeddable, usable as a shell, great for domain specific languages). It would be interesting, to me at least, to see a fan of Janet compare the two.

embedding-shape 6 hours ago

I've only used Tcl briefly, mostly for automation which it's great at. But it's a Algol-like imperative language, doesn't have any type of macros and makes everything based on strings (which makes sense for automation) instead of lists, with all the tradeoffs that comes with.

It seems easier to figure out what the similarities are, because I think they're pretty few, they seem to differ more than they are similar.

adrian_b 4 hours ago

Tcl being based on strings creates the same problems like in bash scripts, i.e. it is too easy to misuse the quotation rules, leading to subtle bugs.

Using for scripting LISP-like languages is much more foolproof, especially for more complex scripts.

packetlost 2 hours ago

ux266478 3 hours ago

Janet is faster, but lacks anything like tk. It's probably also quicker to learn, as you don't get into complex evaluation structure until you start messing around with quasiquotes, while tcl requires you to understand mixing 3 different layers of evaluation right off the bat. tcl's vm imo is easier to understand as well.

tcl if you want a UI, janet if you want an embedded scripting language.

1vuio0pswjnm7 4 hours ago

The author does not mention that Janet comes with _built-in_ networking

Having tried many tiny interpreters over the years, that's relatively rare IME

skeledrew 7 hours ago

This got me thinking of Hy. I wonder how syntactically close they are; there might be an exploitable Python -> Hy -> Janet path here.

[0] https://hylang.org/

cfiggers 2 hours ago

I use both. They're similar for simple use, but above a certain level of complexity Hy has a lot of Python-isms that bleed through. It really doesn't ever let you forget that underneath all the parentheses you're really writing Python. Janet feels like its own stand-alone language in that respect, where Hy is more like a syntax swap.

I have the impression that Hy's user base is larger, though (not that either one is huge).

rcarmo 7 hours ago

I used Hy for a long time, then tried Janet, and ultimately realized that I wanted more batteries included but didn't want Python... So I forked https://github.com/rcarmo/go-joker and am tinkering with it until it does all I want.

zabzonk 5 hours ago

Thought this might be about JANET, the rationale for which I have never really understood. The wikipedia article on it is not very explanatory: https://en.wikipedia.org/wiki/JANET

AHTERIX5000 5 hours ago

Does embedding Janet still lean on global state?

OskarS 2 hours ago

My first question too, and I checked out the linked book [1], and sure seems like it! There's global functions like `janet_init()` and `janet_loop()` all over the place.

A language shouldn't advertise itself as "embeddable" if it does this. It means you can't have multiple interpreters, you can't use it on multiple threads, etc. GNU Guile does this too, and it's a baffling decision! For my field (audio plugins like VSTs), it means it's absolutely a no-go, because hosts can load any number of instances of your plugins and potentially run them in parallel in the same address space, they can't rely on global state like this. Each interpreter has to be separate.

Lua does this right, as does Python (as of 3.12, when they made the GIL local to each interpreter) and I think most of the JavaScript engines. And it's not hard, instead of a global `janet_init()`, just have an opaque pointer bundle all the state, like `janet_init(interpreter)`. If you want a global interpreter, just stick it in a global variable.

[1]: https://janet.guide/embedding-janet/

petee an hour ago

Janet global state is thread local;[1] janet_init() is called once per thread.

[1] official docs: https://janet-lang.org/capi/embedding.html

defrost 7 hours ago

Previously (April 2023) | 140 comments: https://news.ycombinator.com/item?id=35539255

lindig 7 hours ago

> Instead of regular expressions, Janet’s text wrangling is based around parsing expression grammars. Parsing expression grammars are simpler, more powerful, and more predictable than regular expressions.

I would dispute that this is the case. In PEGs, alternatives are not commutative, unlike in regular expressions. This can lead to quite frustrating debugging. While a valid choice, the advantage over REs is overstated.

jsmith45 an hour ago

I'd argue they are not commutive in regexes either, at least as implemented in practice. Implemented regexes favor the leftmost alternative even when both sides of the alternative match. This matters in cases like: capturing groups, and backtracking implementations. There absolutely are cases where one ordering of alternatives could yield catastrophic backtracking for some input, while the other will avoid it completely.

I personally don't like this at all. This means that regex engines that try to generate optimized matching code for an expression can end up generating suboptimal code if you don't want alternative order to matter, since the engine needs to keep that invariant, except in the case when it can prove that the alternatives won't overlap, and a later one can be checked in constant time. If both are true, it is legal to reorder them to do the constant time check before the big complicated wildcard-filled alternative.

But personally, I have never written a regex where I actively cared about the alternative evaluation order. I've used some other people made where order is important but never written one myself.

I'd love to be able to tell the engine "feel free to swap the evaluation order of my alternatives while optimizing", but few if any such engines offer that as a feature.

Now I get that PEGs have commutivity problems are that are different from regexes', which make the issue worse, but that doesn't mean regexes do things right either.

xigoi 6 hours ago

The non-commutavity is a feature, not a bug. It allows you to have clearly defined parsing for grammars that would traditionally be considered ambiguous.

layer8 3 hours ago

But that’s not how humans writing code generally think about ambiguous expressions. You can see that by how few precedence rules programmers tend to internalize, and often prefer extra parentheses to make sure that the parser interprets it the way they mean.

xigoi 2 hours ago

zelphirkalt 6 hours ago

PEGs are just soooo much easier to read than regexes for anything more complex than a few words or single line matching. REs are a hammer that tempts people to see everything as a nail, but once one progresses beyond that phase one usually wants as few REs as possible.

layer8 3 hours ago

That’s mostly an artifact of concise regex syntax I believe. When you write regular expressions in an ABNF-like form, they become much more intelligible.

petee 6 hours ago

"small peg tracer"[1] is really helpful for breaking down a PEGs operation

[1] https://github.com/sogaiu/small-peg-tracer

bmn__ 6 hours ago

Came here for this comment. Janet would score positively in my mind if the evolutionary dead-end PEG were replaced with a grammar parser that is known to work under all circumstances.

6LLvveMx2koXfwn 7 hours ago

Maybe needs a (2023) in the title?

hlude 3 hours ago

The shell DSL is what made me want to try Janet

veqq 33 minutes ago

We recently migrated to a standard library version: https://janet-lang.org/1.41.2/spork/api/sh-dsl.html

gspr 7 hours ago

The embeddability sounds very appealing. Does anyone have experience with using this somewhere one might traditionally reach for Lua?

xigoi 6 hours ago

I have built a markup language with embedded scripting in Janet. I originally tried to use Lua, but found the verbosity extremely frustrating.

boltzmann64 3 hours ago

if those are the reason why you love janet, then you will love tcl because you will be able to do all the same things without drowning in parenthesis and weird syntax.

WalterBright 2 hours ago

Dammit, Janet, I love you!

IshKebab 7 hours ago

Pretty compelling, especially "Janet does not adhere to the ancient customs. CAR is called first. PROGN is called do. LAMBDA is fn, and SETQ is def." - a sign of good sense for sure!

How fast is it?

Also my main objection to Lisps is still the horrible bracket syntax. Yes it's unambiguous and easy to parse, but it's HORRIBLE to read and edit. I wish this project had been a success (or something similar to it): https://readable.sourceforge.io/

Also I don't think static typing is really optional for me at this point.

adrian_b 5 hours ago

Actually not all those are ancient customs, and not all that Janet uses is newer.

In the first description of the language LISP, from March 1959 (AIM-008), John McCarthy had used the names "first" and "rest", instead of what later will be called "CAR" and "CDR".

The names of "CAR" and "CDR" appear to have come from the students who worked at the practical implementation of the LISP interpreter on an IBM 704, and unfortunately we have remained stuck with them, like also with other features that were intended only for a temporary use, until being replaced in the "final version" (which was abandoned).

setopt 7 hours ago

> Pretty compelling, especially "Janet does not adhere to the ancient customs. CAR is called first. PROGN is called do. LAMBDA is fn, and SETQ is def." - a sign of good sense for sure!

Just FYI, many of these are also done in Scheme and its derivative Racket. They kept lambda (but even Python did that), but progn -> begin, setq -> set!, car -> first, and so on.

> Also my main objection to Lisps is still the horrible bracket syntax. Yes it's unambiguous and easy to parse, but it's HORRIBLE to read and edit.

I have pretty mixed feelings at this point. I don’t mind it for normal programming, but when I do numerical programming (physics models, etc.) you often get extremely long and verbose expressions that are IMO difficult to parse compared to the math-like infix operator notation used in other languages.

aeonik 7 hours ago

I'm starting to prefer the s expression syntax when dealing with tree structures like json.

I wonder if we were raised on tree based algebra if math would be easier to do, or harder.

Like, solve for x.

   (= (+ (* 2 x) 3) 11)
   (= (* 2 x) (- 11 3))
   (= (* 2 x) 8)
   (= x (/ 8 2))
   (= x 4)
Though this isn't too bad.

    (= (+ (pow x 2)
          (pow y 2))
       (pow r 2))

setopt 4 hours ago

setopt 4 hours ago

krupan 3 hours ago

tmtvl 2 hours ago

All programming languages have horrible syntax (except maybe Forth). Some examples:

Statements are terminated by either a dedicated graphical character, in which case it's easy to forget the character and have a problem, or by a newline (or maybe a different white space character, but I haven't encountered that yet) in which case decent formatting of code may require a dedicated graphical character to indicate that the newline DOESN'T terminate the statement, in which case we have the same problem. Having newline-terminated statements without continuation character would be consistent, but would hamper readability because identifiers would need to be strictly limited in length to keep certain lines from exceeding available screen space (or alternatively readability would suffer from lines only being partially readable).

And that's before getting into the weeds of how mathematical notation is tricky (most people have learned infix notation at maths class in school, so they mightn't appreciate how horrible it is), how different types of brackets (round, square, curly) can have inconsistent semantics, the downsides to the various ways of indicating lexical blocks (brackets, white space, keywords,...), et cetera.

The ideal programming language would probably be one which allows switching between different syntaxes based on what works best for the user (for example, someone could write code in S-expressions, another person could have that code automatically translated into SRFI-119 Wisp expressions and work with it like that, a third person could then have it rendered into something more Lua-like,...). Which is something I think the Racket people are working on, but I may be mistaken.

packetlost 2 hours ago

It's a scripting language, so it's not going to compete with anything compiled or JITed, but it has a pretty efficient threaded bytecode interpreter (that is almost more interesting than the language itself!). It's certainly good enough for most situations where you would reach for a scripting language.

graemep 7 hours ago

Syntax is not that important to me. I prefer Python style indentation, but its really not that important - its just something to get used to for me.

Is static typing that important for a scripting language? From the intro to the book:

> And to be clear, I’m not going to try to convince you to bet your next startup on Janet, or even to use it in any sort of production setting. But I think it’s an excellent language for exploratory programming, scripting, and fun side projects.

xigoi 6 hours ago

> Also my main objection to Lisps is still the horrible bracket syntax. Yes it's unambiguous and easy to parse, but it's HORRIBLE to read and edit.

I use Parinfer, which allows me to edit Janet as if it was an indentation-based language.

IshKebab 5 hours ago

Yeah I mean I guess if you have to use that syntax, it's nice to have a better editor for it. But IMO the existence of that tool clearly demonstrates that the syntax is pretty bad.

adrian_b 4 hours ago

imtringued 4 hours ago

ux266478 3 hours ago

> How fast is it?

Roughly as fast as puc-rio Lua. It won't blow your socks off, but it's more than respectable.

zelphirkalt 5 hours ago

Out of those renames, I agree with car->first and progn->do. setq is ugly, but I think using def is maybe questionable. lambda I would have just kept the same.

e12e 6 hours ago

netbioserror 4 hours ago

Janet is ALMOST an incredible tool...but what I want is a very clear bifurcation between the standard library's stateful mutating procedures, and stateless value-returning functions. I ran into that wall hard trying to make something non-trivial.

It also turns out that the mix is due to the standard library leaning on raw C loop iterations underneath whenever it can. Which is great! But it confuses the library's interface paradigms.

anthk 6 hours ago

Luxferre.top has some Janet based softwrae.

veqq 12 minutes ago

> I never thought it could happen to me.

But I am truly biased. I have basically forgotten how to code everything else (besides APL family languages) in the past _checks notes_ 10 months since I started Janet. I even run a community [docs site](https://janetdocs.org/) and am writing [my own tutorial](https://janetdocs.org/tutorials/learn-to-program) (albeit slowly). I even use it in production for all new software (within 3 weeks of starting, I had rewritten all personal scripts etc.)

> Janet is simple

You can do literally everything with just hashmaps. The whole language is basically a hashmap, implementation wise. `(keys (curenv))` prints out all locally defined symbols. `(keys (getproto (curenv)))` prints the parent hashmap of the current environment i.e. all the core symbols. I don't, but you can basically do CLOS via hashmaps (and there is a [fuller implementation](https://git.sr.ht/~subsetpark/fugue) too.)

> Janet is distributable

I have like 20 websites and another dozen or so services running on Janet (with the [Joy webframework](https://github.com/joy-framework/joy) which I wrote a [tutorial](https://janetdocs.org/tutorials/Joy-Web-Framework) for), on a single free-tier VPS with 512mb of RAM.

> Janet has ... immutable collections

...not really. In reality, the whole standard library constantly returns mutable versions from everything. There's no reason to really try to be immutable at this point. Although there are cool [combinator libraries](https://git.sr.ht/~subsetpark/apcl-janet) and I've even made combinatorish versions of basic functions:

    (defn better-cond
      [& pairs]
      (fn :bc [& arg] # names for stack traces
        (label result
               (defn argy [f] (if (> (length arg) 0) (apply f arg) (f arg))) # naming is hard
               (each [pred body] (partition 2 pairs)
                 (when (argy pred)
                   (return result (if (function? body)
                                    (argy body) # calls body on args
                                    body)))))))
Combinatory inspired cond, which allows for pairs. The test does not need an argument and the body may be a simple value or a function:

    (map (better-cond
             string? "not a number"
             odd? "odd"
             even? "even") 
      [1 2 3 "cat"]) # the args!
    (map
        (better-cond
         1 (fn [arr] (array (min ;arr) (max ;arr)))) # (recombine array (unapply min) (unapply max)))
        (partition 2 (range 10))) # these are the args!
    ((better-cond
          < "first is smaller"
          > "second is smaller")
         5 3) # these are the args passed into the func! I am excited!
> Janet lets you pass values from compile-time to run-time

That's what got me hooked, in a few ways. In Racket or Go, I had to do a lot of work to process data at compile time so the runtime could literally just be a lookup table. In Janet? That's the default behavior of any `def` outside of main. The following turns a .tsv of the bible into a hashmap in the binary, when compiling:

    (def verses  (reduce (fn [acc line]
                           (let [parts (string/split "\t" line)]
                             (if (= (length parts) 5)
                               (let [[_ abbrev ch vs text] parts]
                                 (put-in acc [abbrev ch vs] text))
                               acc)))
                         @{}
                         (string/split "\n" (slurp "kjv.tsv"))))
    
    (def abbrev-array (keys verses)) # also makes an array of the abbreviation column
So the rest of the program is literally just accessing the hashmap ([twice as fast](https://codeberg.org/veqq/verse-reader#performance) as the Golang version using `embed`):

    (defn main [_ & args]
      (if (or (empty? args) (= "-h" ;args) (= "help" ;args))
        (do (print "Usage: kjv <book> [chapter:verse]") (os/exit 1))) # show help
      (let
       [Capitalized (string (string/ascii-upper (string/slice (first args) 0 1)) (string/slice (first args) 1))
        book        (find |(string/has-prefix? $ Capitalized) abbrev-array)]
    
        (pp (match args
              [_ chap verse] (get-in verses [book chap verse])
              [_ unsure]     (match (string/split ":" unsure)
                               [chap verse] (get-in verses [book chap verse])
                               [chap]       (get-in verses [book chap]))
              [_]            (verses book)))))
The equivalent go program was 5x longer and required an extra program to convert data into a 40k line .go file with a giant literal hashmap, to be faster than the naive Janet.

...but actually Ian Henry means Janet e.g. keeps closures synced across images/sessions:

    (defn timer [t] 
      (var t t) # this is slightly annoying, must shadow as params are immutable
       [(fn [] (set t (+ t 1)))
       (fn [] (set t (+ t 2)))])
    
    (def tx (timer 0))
    # call like this:
    ((tx 0))
    ((tx 1))
    
    # make an image and save it to file
    (def my-module @{:public true})
    (spit "test.jimage" (make-image (curenv)))
Exit and start a new REPL session:

    (defn restore-image [image]
      (loop [[k v] :pairs image]
        (put (curenv) k v)))
    
    (restore-image (load-image (slurp "test.jimage")))

    ((tx 0))
It saved the closure and all relevant image in the `(curenv)` hashmap.

Condensed from my longer response: https://lobste.rs/s/y0euno/why_janet_2023#c_lspe6n

bjourne 3 hours ago

Damn it, Janet. No proper namespaces. Hard pass.

makach 6 hours ago

Excellent. Although I suspect the author of the programming language invented this Janet for all the perfect puns. Yes, Janet. No. Janet.

shevy-java 6 hours ago

    (defn foo [first & rest] ...)
So basically Lisp 2.0.

Although, this here is a good idea:

"pass values from compile-time to run-time"

Would be nice if some kind of "scripting" language be as fast as a compiled language, but without ruining the syntax. Just about 99% of the languages that are shown, have a horrible syntax. Syntax is not everything, but most language designers don't understand that syntax also matters. So tons of horrible languages emerge. Nobody will use those languages, so 99% of them will die off quickly.

xigoi 6 hours ago

What would be a better syntax according to you? I have found Janet’s syntax very pleasant to work with as opposed to JavaScript, Lua or even Python.

Imustaskforhelp 6 hours ago

can't there theoretically be a language which transpiles to Janet to get all the benefits without additional paranthesis too?

Not sure if such transpilation would have a perf hit though, I hope somebody responds who knows about it more.

I don't deny that syntax matters itself too but there are some ideas of janet like sandboxing and other features which seem to me to be worth implementing in other languages too.

Personally, I would be really interested in a language like lua/wren which can transpile to Janet too.

rmunn 3 hours ago

"... all the benefits without additional parenthesis too?"

I guess you don't like Lisp's syntax. I didn't either until I realized the key insight: when you're writing Lisp, you're basically writing an AST. Which is why it's so easy to manipulate your code. Want a new feature the language doesn't have, such as the pattern-matching they added to C# a few versions back? You can add it yourself; you don't need to wait for a language committee to implement it years after you needed it. That's all that macros are: functions that take AST and return AST, which is then executed.

And once I realized that Lisp's syntax was basically an AST, I no longer saw the parentheses. Now I just see blonde, brunette, redhead... Oops. Sorry. Wrong reference.

petee 6 hours ago

I guess you could transpile direct to Janet bytecode, and performance would be in theory the same as native Janet?

flintenmuschi 4 hours ago

Why, Henry?

wolfi1 6 hours ago

why is it called Janet? perhaps to prevent it to be identified with the acronym for Lots of Irritating Single Parenthesis?

petee 6 hours ago

It was named after the sentient computer system in the TV show "The Good Place"

A humourous clip: https://youtu.be/etJ6RmMPGko?si=W98LdG1jDdUCXsHV

embedding-shape 6 hours ago

Also mentioned in the GitHub repository:

> Why is it called "Janet"? Janet is named after the almost omniscient and friendly artificial being in The Good Place

https://github.com/janet-lang/janet#why-is-it-called-janet

xigoi 6 hours ago

If it was called [Something] Lisp, Lisp enthusiasts would complain that it’s not a lisp because it does not use linked lists as the primary data structure.

Imustaskforhelp 6 hours ago

I know that Lisp has lots of paranthesis and I don't have enough experience with Lisp at all.

But from the looks of it, Janet has some great ideas like the one that @ramblurr shared here about sandboxing ("Disable feature sets to prevent the interpreter from using certain system resources. Once a feature is disabled, there is no way to re-enable it.")

Lisp from my understanding is incredibly polarizing and many people love it and many people hate it and that's fine, but at a certain point wouldn't it feel repetitive for statement like this and I am unsure of how healthy discussion about programming concepts can be done this way.

There are so many interesting things from lisp-y languages like Janet and Julia is technically lisp-y too and Julia's compilation to GPU is awesome and Nim too which can compile to C/C++/JS!

It's just so many interesting concepts overall in programming that paranthesis don't seem a concern to me as the underlying concept can be translated to something else, like sandboxing feature, transpilation to GPU or multiple targets!

And there are many unique concepts in non-lispy languages like golang (cross-compat, portability with static binaries), elixir (concurrency!) too.

It's just good to see the amount of innovation within programming from all spheres of influence :-D

adrian_b 6 hours ago

While I do not like the excess of parentheses of LISP and similar languages, their syntax is very consistent and predictable. Moreover, while LISP has an excess of parentheses, it omits a greater number of commas that are required in many other programming languages.

I am much more annoyed by the random syntax inconsistencies of most popular programming languages, which are either caused by original language design mistakes, or, more frequently, by the late addition of some features that were not planned in the original language, so they had to be squeezed in with the help of various ugly workarounds.

While during the last years I have not used much LISP like languages, there have been times when I used them a lot, for several years, in scripting applications, e.g. the LISP variant of old AutoCAD, the Scheme-like scripting language of the Cadence EDA applications, or the scsh Scheme dialect that is usable for replacing UNIX shell scripts.

In all cases, these languages allowed a greater productivity associated with rarer bugs than the more popular scripting languages, like Python, Perl, TCL, bash.

While aesthetically I might prefer the look of a Python program, for solving a practical production problem I would prefer to write scripts in one of the LISP derivatives. Obviously, the productivity in various programming languages depends a lot on individual preferences and previous experiences.

It should be noted by all those who believe that the LISP-derived languages have too many parentheses, that the C programming language and all languages with syntax derived from it, like Java or Rust, have a great excess of parentheses in comparison with the older languages that had better designed syntaxes, e.g. ALGOL 68 or IBM PL/I.

For example, compare

  for (i = 1; i <= 100; i += 5) { ... }
with

  for i from 1 to 100 by 5 do ... od
or

  if ( ... ) { ... } else { ... }
with

  if ... then ... else ... fi
The first example has 12 syntactic tokens instead of the minimum required, which is 6.

The second example has 8 syntactic tokens instead of the minimum required, which is 4.

If I cannot have a decent programming language with a minimum number of parentheses, I would rather have a programming language where all the places that need parentheses are predictable, like in LISP, instead of having a language like C and its derivatives, which require parentheses in random places, for no good reason at all.

NetMageSCW 2 hours ago