13 ms·
Why Janet? (2023)
- nicechianti 4mo ago[dead]
- AHTERIX5000 4mo agoDoes embedding Janet still lean on global state?
- OskarS 4mo agoMy 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/ https://janet.guide/embedding-janet/
- petee 4mo agoJanet global state is thread local;[1] janet_init() is called once per thread. [1] official docs: https://janet-lang.org/capi/embedding.html https://janet-lang.org/capi/embedding.html
- OskarS 4mo agoSo you can't execute a Janet script on a different thread than it was created on? Still not good: if you're making audio plugins, you don't control the threads which your program runs on. It's just not good enough, IMHO.
- petee 4mo agoNot sure exactly what you're getting at, you mean transfer mid-execution to another thread? You can load and run a script on any thread you can load janet on, and you can coordinate across threads if need be. To clarify, janet_init just sets up the VM I'd also go take a look at the actual docs and code, I'm not sure I know the exact answer, but assumptions won't help Edit: there was someone on the Zulip that mentioned working on audio plugins, and there are a couple other audio-related projects you could check out. Someone there might have a better answer -- https://janet.zulipchat.com/ https://janet.zulipchat.com/
- OskarS 4mo agoThe UI for audio plugins generally work in an event driven manner: you get events like mouseMove, keyDown, repaint, etc. from your host. In response to those events, you run your script to figure out what you need to do. You have no control over which thread calls these things, it can be the GUI thread that runs all of them, it can be run on background threads in parallel, etc. Different hosts do it differently. If Janet using thread-local state, this just doesn't work: the state for one instance is completely different from another, and there's even no guarantee they're running the same scripts. Consider the most famous embedded language, JavaScript in browsers: you can have any number of tabs open at the same time, and if the JavaScript interpreters for each of those used a bunch of thread-local storage, it would place huge restrictions on how the browsers could schedule and parallelize the callbacks for the JavaScript in those tabs. The only way I can see this working is if you spin up a thread for each instance, and when these events come in, you wake up those threads, send over the event information, block until the interpreter thread finishes. But that's both inefficient and a real architectural hassle. All I want is an object that's like `janet_interpreter *interpreter = janet_make_interpreter();` and then you pass that to the functions instead of doing all these magic things with global variables and thread local state. That's it.
- IshKebab 4mo agoPretty 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/ https://readable.sourceforge.io/ Also I don't think static typing is really optional for me at this point.
- setopt 4mo 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 4mo agoI'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 4mo agoI definitely prefer s-exps over both xml and json myself too! Interesting question. Much of the difficulty does stem from mentally translating back and forth between conventional notation and s-exps too, since you can’t really avoid the standard notation when reading and writing math and physics papers. And current-day math and physics notation has been optimized to some extent for the infix notation; perhaps one would have invented more expressive higher-order functions or macros to denote s-exp math if that was what everyone used for centuries.
- krinne 4mo agoThis 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.
- soomtong 4mo agoIt’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.
- probably_wrong 4mo 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-months-ai-agents-spambots.html https://www.techspot.com/news/111698-digg-relaunch-fails-two...
- geokon 4mo agolobste.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 4mo agoyeah but I can't post there because I don't know anyone with an account and frankly CBA traipsing around looking for someone who has an account. does seem like more things will have to go this way though
- 4mo ago
- uka 4mo 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.
- bryanrasmussen 4mo agoyou ever try to explain object oriented programming languages and their benefits to the "average person on the street"?
- rambrrest 4mo agosomehow 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.
- xigoi 4mo agoThe idea is that instead of having to learn tens of different syntactic constructs with subtle and often arbitrary differences, you just have parentheses and use them to build everything.
- embedding-shape 4mo agoThis is such a undervalued benefit, once you've learned s-expressions, you can basically learn a bunch of languages without having to learn completely new syntax. It'll be slightly different, with different idioms and names, but a hell of a lot easier than doing the same across every "It's like C but 50% of the syntax is different actually" language out there, which is most of them.
- jurip 4mo agoIs the syntax really the stumbling block for most languages? Would Rust's lifetimes or Swift's isolation rules be easier if they used more parens? Are the scoping rule differences between Emacs Lisp and Scheme easier to comprehend because the syntax is similar?
- skeledrew 4mo agoThis 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/ https://hylang.org/
- rcarmo 4mo agoI 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 https://github.com/rcarmo/go-joker and am tinkering with it until it does all I want.
- cfiggers 4mo agoI 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).
- gspr 4mo agoThe embeddability sounds very appealing. Does anyone have experience with using this somewhere one might traditionally reach for Lua?
- xigoi 4mo agoI have built a markup language with embedded scripting in Janet. I originally tried to use Lua, but found the verbosity extremely frustrating.
- lindig 4mo 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.
- bmn__ 4mo agoCame 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.
- xigoi 4mo agoUnder what circumstances does PEG not work?
- bmn__ 4mo agoMany. I don't have enough room on HN to show a representative sample of the shortcomings. Read the relevant literature or converse with an LLM to learn more. Typical example, ported from <https://news.ycombinator.com/item?id=16600224 https://news.ycombinator.com/item?id=16600224>: (pp (peg/match '(capture '{ :main (* :B) :B (+ (* :A "x" "y") :C) :A (+ true (* "x" "z")) :C (+ (* :C "w") "v")}) "xzxy")) This almost trivial grammar works without any problem in known good parsers. If you want to try out grammars in the wild in Janet, it is nearly guaranteed that they are complex enough for peg to shit itself.
- xigoi 4mo agoOf course this grammar does not work; you violated the two rules of writing PEGs: • do not use left-recursive rules; • put alternatives in such an order that none can be a prefix of a subsequent one. These may seem limiting, but can always be fixed by a simple local change. In contrast, transforming a PEG into a conventional grammar often requires complex, wide-scoped changes. I’ve had the Tree-sitter compiler “shit itself” many times at grammars that PEG accepted with no problem, and had to introduce several ugly hacks to work around the problem of Tree-sitter not allowing ambiguous grammars.
- 6LLvveMx2koXfwn 4mo agoMaybe needs a (2023) in the title?
- defrost 4mo agoPreviously (April 2023) | 140 comments: https://news.ycombinator.com/item?id=35539255 https://news.ycombinator.com/item?id=35539255
- wodenokoto 4mo agoI'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.
- shevy-java 4mo 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.
- lelanthran 4mo 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 4mo agoPersonally I get hung up on the macro syntax being near the beginning, but there is so much valuable stuff past that
- veqq 4mo agoI have some gentler introductory stuff like: https://janetdocs.org/tutorials https://janetdocs.org/tutorials
- shevy-java 4mo 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.
- Imustaskforhelp 4mo agocan'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.
- petee 4mo agoI guess you could transpile direct to Janet bytecode, and performance would be in theory the same as native Janet?
- rmunn 4mo 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.
- 4mo ago
- makach 4mo agoExcellent. Although I suspect the author of the programming language invented this Janet for all the perfect puns. Yes, Janet. No. Janet.
- wolfi1 4mo agowhy is it called Janet? perhaps to prevent it to be identified with the acronym for Lots of Irritating Single Parenthesis?
- Imustaskforhelp 4mo agoI 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 4mo agoWhile 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.
- 0x0203 4mo agoSeems 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 4mo agoI'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 4mo agoTcl 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 4mo agoOk, but now I want to embed Janet in a TCL program
- nrclark 4mo agoTcl is pretty good at functional-programming type stuff, and it can absolutely do anything that you could do with a macro. It isn't Algol-like at all imo, maybe beyond some superficial syntax. It feels a lot more like if LISP and Bash had a baby out of wedlock. (I've written a lot of Tcl over the years and it'll always have a spot in my heart)
- ux266478 4mo agoJanet 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.
- ramblurr 4mo agoAlways 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 https://janet-lang.org/api/misc.html#sandbox
- declan_roberts 4mo agoIt's a really cool feature but what is a scenario when your average programmer needs such sandboxing?
- myaccountonhn 4mo agoI 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.
- briaoeuidhtns 4mo agoyou're embedding it as a scripting api and want to limit permissions to just what's needed
- petee 4mo agoThe TIC-80 game engine embeds Janet, and if i recall sandboxes the games created
- anthk 4mo agoLuxferre.top has some Janet based softwrae.
- deleted 4mo ago[deleted]
- rohitsriram 4mo ago[flagged]
- 1313ed01 4mo agoThere 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/ https://fennel-lang.org/
- ux266478 4mo agoFennel 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.
- JHonaker 4mo ago> capable of breaking SBCL's jaw What exactly do you mean by this? Speed? Portability? Ease of use?
- ux266478 4mo agoWhat I mean by that is that it's in the same weightclass of speed, depending on the problem being tackled. In the case where data's shape is mutable, SBCL will scream ahead thanks to CLOS. You can cheat LuaJIT with dynamically-defined C-structures via abuse of the FFI lib instead of native tables, but it's not as nice as CLOS nor is it very safe. In the case that the shape of data is changed extremely frequently? CLOS might actually end up falling behind here. Another area where SBCL will likely win out is when the hotpath is bottlenecked on string operations. Where I'd say it advances into breaking SBCL's jaw is that the runtime, interpreter, jitter, etc. are all much smaller than SBCL's runtime and compiler. If you're looking for a complete system, I'd say SBCL wins out obviously. You're talking a world-class REPL, debugger, a high quality stdlib, etc. All it's missing is a text editor like LispWorks (emacs and pretty much every other FOSS Lisp editor I've seen is a massive downgrade.) With that in mind, SBCL is not something you embed in an application written in another language. The holy grail is getting something as fast as SBCL, as flexible as SBCL, but as a 50k loc self-contained runtime. LuaJIT is the reigning heavyweight champ there, so having a Lisp-adjacent language like Fennel running atop it is a pretty damn compelling idea. Interestingly with regards to text editors, Lua doesn't have that problem technically. Lite-XL is dangerously close to being zmacs/LispWorks for Lua. Poetically, just like Lua it's fairly bare bones and requires extension to be a decent IDE. But the underlying structure is absolutely fantastic, being based around a fairly cohesive object model rather than coats of paint over text buffers.
- xuzhenpeng 4mo ago[flagged]
- xrd 4mo agoThe author made these using Janet (discussed on HN in the past): https://bauble.studio https://bauble.studio https://toodle.studio https://toodle.studio Those two fascinating art tools got me very excited about Janet a while back.
- soomtong 4mo agoThis document was really helpful when I first met Janet: https://janetdocs.org/tutorials https://janetdocs.org/tutorials https://janet.guide/ https://janet.guide/ (the author's one)
- veqq 4mo agoI'm really happy you liked it! It's still a work in progress.
- mackeye 4mo agojanet 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.
- iLemming 4mo ago> janet has replaced sh, python, awk, etc.... babashka did that for me.
- yolkedgeek 4mo agoThis is a great comparison and I've been wondering about it for a while. Between babashka, janet(i discovered it just now), fennel, guile. Which one would be a better scripting language? Please tell me you experience, and if you are interested, we can work on a small article and benchmark about this.
- iLemming 4mo agoIt's platform-dependent isn't it? Otherwise, the practical differences between Lisp dialects are negligible. For me writing either in Janet or Fennel or Clojure feels almost like writing in the same language. Babashka has replaced bash-scripting for me. I don't hate Bash, but why would I ever choose to use a language that has no true REPL, if I don't have to? bb is pretty much Clojure, which is the greatest choice if you're dealing with data - any data. Clojure is incredibly data-driven, which wins me over Janet. I also reach out to nbb whenever I need to deal with Node. e.g. scraping scripts driven by Playwright. Janet is great when you need tiny runtime or you're dealing with subprocess-heavy scripts - Janet feels closer to actual shell syntax; or when you have to embed it to C/C++ program. Fennel is indispensable for any Lua - mpv, Hammerspoon, AwesomeWM and Neovim configs, etc.
- zabzonk 4mo agoThought 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 https://en.wikipedia.org/wiki/JANET
- edwinbalani 4mo agoFrom memory, it was for Joint Academic Network. I'm surprised the Wikipedia article doesn't mention it at all, but it seems hard to find an authoritative source.
- a-french-anon 4mo 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 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.
- veqq 4mo agoYou 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 4mo agoHuh! 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.
- xigoi 4mo agoNot quitek in Scheme, parens and brackets are completely interchangeable.
- nlitened 4mo agoSquare 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.
- netbioserror 4mo agoJanet 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.
- didibus 4mo agoWow I never realized Janet was released more than 10 years after Clojure. Clojure: 2007 Janet: 2019
- flintenmuschi 4mo ago[dead]
- deleted 4mo ago[deleted]
- 1vuio0pswjnm7 4mo agoThe author does not mention that Janet comes with _built-in_ networking Having tried many tiny interpreters over the years, that's relatively rare IME
- deleted 4mo ago[deleted]
- boltzmann64 4mo agoif 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 4mo agoDammit, Janet, I love you!
- sph 4mo agoThere are three languages worth learning that expand your mind: Lisp, Forth and Tcl. Despite all exhibiting homoiconicity, they couldn't be more different from one another. (I'd include Rebol but it's as mind-blowing as it's dead technology from a lost timeline)
- oldes 4mo agoRebol is not dead. It's just invisible to the mainstream. There was a new release (3.22) last week.
- hlude 4mo agoThe shell DSL is what made me want to try Janet
- veqq 4mo agoWe recently migrated to a standard library version: https://janet-lang.org/1.41.2/spork/api/sh-dsl.html https://janet-lang.org/1.41.2/spork/api/sh-dsl.html
- janetacarr 4mo agoI 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).
- FelipeCortez 4mo agoJulia Evans has a fun blog post using Julia to visualize Gunzip: https://jvns.ca/blog/2013/10/24/day-16-gzip-plus-poetry-equals-awesome/ https://jvns.ca/blog/2013/10/24/day-16-gzip-plus-poetry-equa... you should totally do a "Janet writes Janet" version
- deleted 4mo ago[deleted]
- veqq 4mo ago> advanced HTTP routing What do you concretely mean by this? I use https://github.com/joy-framework/joy https://github.com/joy-framework/joy for all web stuff and can probably get your missing features in within the week.
- cptmurphy 4mo agoTLS. There are some curl wrappers but last time I checked they did not work
- bjourne 4mo agoDamn it, Janet. No proper namespaces. Hard pass.
- xigoi 4mo agoWhat do you mean? When you import a module in Janet, it adds a namespace prefix to all symbols. What more do you want?
- veqq 3mo agoIt would be really cool to declare multiple modules in the same file, somehow. Also, the Janet community's generally against the word namespace, saying we don't have them. (I don't fully grok why not.)
- ianthehenry 3mo agohttps://github.com/ianthehenry/janet-module/blob/master/init.janet https://github.com/ianthehenry/janet-module/blob/master/init... This is a very very barebones version of this, but it’s not too hard to construct environment tables dynamically
- bjourne 3mo agoNamespaces as first class objects. Janet namespaces symbols by renaming them. It's not the same thing.
- ianthehenry 3mo agoThe `import` macro extends the current environment with prefixed symbols from another environment. But the environment is a first-class object that you can hold and manipulate and use in arbitrary ways — `require` is the lower level primitive that `import` is built on.
- deleted 4mo ago[deleted]
- veqq 4mo 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/ https://janetdocs.org/) and am writing [my own tutorial](https://janetdocs.org/tutorials/learn-to-program 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 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 https://github.com/joy-framework/joy) which I wrote a [tutorial](https://janetdocs.org/tutorials/Joy-Web-Framework 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 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 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 https://lobste.rs/s/y0euno/why_janet_2023#c_lspe6n
- doug_durham 4mo agoDSLs. Creating a language that only you know that will double the learning curve for the folks coming after you. It's fine for personal projects, but almost always an anti-pattern.
- ifidishshbsba 4mo agoDsls are always in the codebase either explicit where it’s made plain or implicit as design patterns and apis
- frwrfwrfeefwf 4mo ago[dead]
- frwrfwrfeefwf 4mo ago[dead]
- p1necone 4mo agoFirst time I've seen 'wat' used as a noun, I like it. (for those unfamiliar with the reference: https://www.destroyallsoftware.com/talks/wat https://www.destroyallsoftware.com/talks/wat)
- chronolitus 4mo agoI wish these posts had more code snippets and examples, seems like a cool language though!
- hishiviitd 4mo ago[flagged]