Sure you need to learn some hard stuff like async, but it seems to me that the most important thing is languages should work “like you expect them to”.
Python, javascript, they’re not perfect but they’re pretty obvious.
Zig aims to be obvious.
Rust is the opposite of obvious. C++, the small subset of it that I use is pretty obvious. Things are similar to other languages, there is minimal surprises.
Functional programming feels non obvious.
Quite the opposite to you, Rust is obvious to me because I'm used to low-level programming and careful memory management, where JS is more than often very surprising.
Another example: I wrote a Lisp-like language in my company for mathematicians to develop constraint systems; now this purely functional, bastard Lisp feels like a second nature to them, whereas they could barely put three lines of Java together to save their lives.
Does not mean that I'm more or less right than you are; just that just like human languages, no PL or paradigms are ‶naturally obvious″, we are just more or less used to them.
Also Raku is the most obvious language because it inherited the "it" variable ($_) from Perl, and "it" is unbeatable for writing obvious code (read a line, uppercase it, replace all numbers in it with dollar signs, and print it out again).
Janet on the other hand felt instantly familiar after reading the language introduction. It has all the usual imperative constructs from C-family languages – for, while, if – but also extremely expressive macros like `loop` and `as->` that are more common in the world of lisps, and `partial`/`comp` from the functional paradigm. For such a small language Janet really covers a lot of bases.
Functions/macros are often called exactly what I expected them to be, which has allowed me to figure out how to do most things by simply guessing in the REPL. If I need to figure out the details I can use the `doc` function (or Ctrl-g in REPL), and the docs work perfectly even for what would be keywords in non-lisp languages. It has been a really pleasant experience that I haven't really had with other languages, aside from maybe Julia which also has very intuitive syntax and function naming conventions.
Now I'm not recommending you use an unusual language, but I think most companies don't spend enough time on boarding new hires and expecting professional development happen on the employees time.
If I am going to use a tool every day for a couple years I want the best tool, not the easiest tool to learn how to use.
I remember the first time I encountered a C-like for loop, and felt as a most alien thing: until this day I still feel it's an unnatural construct, a third of it abstracted from the application logic, the other two halves low-level requisites. And of course, watching that classic video of Rich Hickey destroying Java's HTTPServletRequest class just once made it clear, at least for me, that OOP's stance to redefine every data input and output into its own understanding of the universe isn't the obvious approach at all.
It’s really strange to call JavaScript obvious… strange implicit conversions, broken equality operator, strange variable scoping… I still have no clear idea what the `this` keyword does in the majority of circumstances…
I was mostly used to C and Perl when I was young, and Python made a lot of sense.
I would not say that javascript was more obvious to me than rust was when I started to use them.
A lot of people seem to be hung up on borrow checker, but if you are used to low level C, you had to keep track of that with comments on functions and conventions anyway (and you had to keep it in your head).
The thing that I had to get used the most of in rust is using more functional style of programming, but since I liked python comprehensions and collections, It want such a long jump as it otherwise might be.
When you were a young perl programmer, you didn't use it functionally, as a list processor?
While TMTOWTDI, I've noticed Perl tended to have an imperative camp and a list processing camp.
Have also seen cases where people with background in Physics, Mathematics found functional programming more obvious. Obvious is relative.
Even for people with programming experience - once I got hang of Haskell, learning and using Scala professionally became more "obvious" for me.
It probably isn't a popular opinion, but I actually think C++ does a fairly good job of this, so long as you're disciplined in how you use it. That discipline is not necessarily obvious, but I've acquired it over 30+ years, and now I find that I almost never have memory safety issues (I do use sanitisers here, but they rarely show up anything). Perhaps more importantly, C++'s fairly strong typing means I'm generally pretty confident refactoring code as requirements change without getting unpleasant surprises. Sure, C++ could do better in many ways, but it is pretty good at avoiding many of the unpleasant surprises I care about, at least for single-threaded code.
Also I would like to know the correct transcription of the name.
Haven't done any practical comparison yet bc Janet has only caught my eye recently (;p), but it appears to have a culture of vendoring and an edge in implementation size and complexity which (ideally) enable diving into the language's implementation to scratch your own itches, learn how it (and the stack in general) work more throughly, and allow eg. low level code optimizations with less FFI boilerplate.
I wonder how executable size and performance pan out, but with C FFI available in each language (and the reality of how much time optimization is worth), I feel that comforts matter more.
Color me intrigued!
I readily admit, though, that not all software development needs to be flexible and big-application focused. To that end there are many things Common Lisp is not so good at. Common Lisp is not good at being pretty out-of-the-box, it's not good at minimalism, and it's not good at prescriptive or dogmatic programming styles. I've personally not been convinced of the various approaches to using Common Lisp as a scripting language (especially because of standard multi-stage execution). Common Lisp also has an unusual learning curve: there's an initial hump of learning Lisp's oddities (DEFVAR/DEFPARAMETER, CAR/FIRST, no hash table syntax, lots of EQ[UA]L-ity functions, systems vs packages, LOOP/DO/DOTIMES/DOLIST, "Neo, there are no lists in Lisp", ...), followed by a longer path of soaking in the extensive facilities offered by the language. There's just... a lot of content. And in part because of its idiosyncratic nature, no singular approach to learning it works for everyone.
Common Lisp is pretty anemic when it comes to extreme use of functional programming. It's wholly capable and serviceable, but most library writers don't go whole-hog with FP, and Common Lisp by default makes FP a little stuffy (it requires functions bound to variables to be called with FUNCALL, and it requires defined functions to be referenced with #' syntax). There is very little support for functional data structures; the FSET library seems to be what most people suggest.
https://old.reddit.com/r/lisp/comments/123edgv/im_considerin...
I’m debating whether to dive into Common Lisp for a new project. I’ve been a fan of Lisp for a while, much prefer parentheses over other notation, and have dabbled over the years by doing things like SICP and writing my own toy Lisp.
Common Lisp comes across as one heck of a battle-tested language and libraries. But I’m debating how much of a learning curve I’ll have on the front end just getting used to SBCL/Emacs/Slime and the various libraries. And I’m not sure that I “get” the interactive workflow yet but def would love to.
Newer dialects like Janet and Fennel address these deficiencies.
They are an everyday falsy value that you program _with_ not around. Nil doesn't signal that you forgot to initialize a value. It simply means stuff like "I don't have this" or "no more". You don't check for it at every corner like an anxious squirrel, but pass it around freely, knowing that your code knows, accommodates and embraces nils.
We used them at work (Clojure) and it didn't solve anything; normal idioms don't apply to monads and the mental overhead of having to know when they are used is the exact same as having to know when something might be nil.
Janet seems really tempting for tiny footprint, distributability etc.
But I'm currently leaning towards to Racket just because it would be more or less compatible with a whole host of Scheme books that I'd like to read (The Little Schemer/Typer/Learner, SICP, Functional Differential Geometry).
Does anyone familiar with Janet know if those books can be easily worked through with Janet for a newbie Lisper?
Janet has a more Clojure-inspired syntax but the semantics and general ideas should carry over. I think trying to work through the books in Janet would be a great extra challenge. You can always drop that and focus on Racket if it becomes it too much for you.
common lisp also has some really great books, beginner and advanced. paip is probably the most famous of these
"hellp world" being a 784k exe is bad
So I checked just now, and my hello world for go was 1.9M. And you're well within your rights to just declare "the fact that go is even worse doesn't make Janet good" but let's be honest - plenty of people have accepted go's compromises, so something coming in at better than half the size is relatively much better.
edit: out of curiosity i used gcc to make hello worlds in C and C++, which came in at 32k and 37k respectively.
It sounds as if Janet's run-time is statically linked; it could also be built as a .so?
Shared library calls (and variable references) are ugly, though. Unless you have a large number of Janet programs, the space saving isn't worth the shared library tax.
I think Lua is in the same range, so this seems reasonable (but obviously much larger than anything without an included runtime).
Common Lisp / SBCL equivalent for me is 41MB.
Clojure überjar size for the same is 4.6MB.
There is a cost to having so much language ("all the language, all the time") available at different stages of the program lifecycle (macroexpand, compile, runtime, ...). Not sure if a Janet program can run eval, but it still has a garbage collector, etc.
Even a Go equivalent is 1.9MB.
The issue is not so much that macros are not merely syntactic transformations, but rather than s-expressions alone aren’t sufficiently expressive to represent the syntax of any Lisp with a package system. The scheme macro system solves that by having macros return syntax objects where identifiers used in a macro can carry around a reference to the original context where the macro was defined. So referencing a function in a macro definition will look up the function in the context of where the macro is defined, and return a syntax object that refers to the correct function. To my knowledge, most modern macro systems work this way (Scheme, Dylan, etc.)
Code has to go out of its way to do something silly, like reference a variable which it does not define; and by coincidence this has to be defined by a macro that is used in the same scope (so that it simultaneously evades unbound variable and shadowing diagnosis).
Hygienic macros do not solve accidental name capture in code that contains no macros. Manually written code can contain a mistake of reference due to variable shadowing, creating a bug.
Yet beyond that, I’m really taken by author’s passion for the language. That’s one great language advocacy at work.
pattern matching,
lambda calculus consistency,
macros for defining domain specific languages,
optional lazy evaluation,
static type checking based on sequent calculus,
one of the most powerful systems for typing in functional programming,
an integrated fully functional Prolog,
an inbuilt compiler-compiler,
a BSD kernel under 15 languages (Lisp, Python, Javascript, C ...)
and operating systems (Windows, Linux, OS/X),
is extensively documented in a book
has nearly a decade of use.
Aditya Siram made two very good videos on Shen. You can program your front and backend in Shen given Shen is implemented in JavaScript and other PLs.I know somebody did a port to C, but I am not sure if you can then make an exe as described for Janet.
https://www.youtube.com/watch?v=lMcRBdSdO_U
https://www.youtube.com/watch?v=BUJNyHAeAc8
You implement Kλ in your PL of choice if it is not already available, and voila, you can program in Shen!
I've since landed on Gerbil Scheme (https://cons.io) which is a modern front-end to Gambit Scheme. It seems like the perfect mix of performance, (modern) features, R*RS, and FFI that I want/need. Baring some significant bugs, it will probably be my goto Lisp going forward.
I know there was Shen Professional that required a monthly subscription and it was on the server, however, Shen is available in different PL ports for free and local use. It is like a Haskelly Lisp. Very well thought out too with a great book that covers a lot of ground including PL history and logic.
I used to use Gambit Scheme, and although I have two linux machines, I work on Windows for my paying jobs. Gerbil doesn't seem to talk about a Windows install. I shy away from WSL, Cygwin, and MSYS2 in general.
I am learning Zig, and I have thought about porting Shen to Zig. The CL port of Shen is the main port (SBCL).
If you mean something like SLIME/SLY, than yeah, but I guess I never considered that a language server in the same way as all the other LSP backends.
https://github.com/tantona/janetroids/blob/master/main.janet
I see the appeal to embed in another app as a scripting language like Lua, but for writing standalone programs?
In terms of listed points.
1. Simplicity... perhaps? I guess Racket has a larger surface area, but just like all Lisps/Schemes it is built on a simple core. 2. Distribution. Janet probably wins here. Racket can produce static binaries, but they may not be as tiny. Relatedly, there is a Racket subset, Zuo, that ticks this box. 3. Parsing text - Racket's whole shtick is this, given it is a language for writing languages :) 4. Subprocess DSL. `sh` looks like a nice library. I can see the value for using this for quick scripts since you can shell out to bash whenever you want. 5. Embeddable. Janet is better here similar to Lua. Also see Zuo. 6. Mutable and immutable collections. Racket has these. 7. Macros. Racket has these. 8. Compile time to run time. I'm not sure about this, being a Racket newbie. 9 and 10 - very subjective :)
Cheers! Thanks for the great article!
https://docs.racket-lang.org/zuo/index.html
In some sense it is a little sad that the Lisp and Scheme like languages diverge so much within themselves, as it makes an already unpopular set of languages even harder to standardize on and evangelize.
Somebody had to.
I do see how it helps keep the contents focused and useful, though. So... Ok, I guess? I'll just have to express my sense of humour as part of something useful, not as the only thing. Thanks for pointing this out.
Thanks for doing this! Would it be possible to get a copy in EPUB form, so I can read through it on my e-reader?
https://janet.guide/janet-for-mortals.epub
That's a super hacky epub that I just generated from the markdown source and have not read over thoroughly. Formatting is a bit different, and code blocks have no labels, which are occasionally significant... but from a quick glance it looks pretty close.
Also https://janet.guide/all has the full text of the book, which you could save to HTML and convert to an epub with pandoc (or print to a PDF). Not sure if the results would be better or worse.
I'm super excited about it because I'm fascinated by the author's two other projects, bauble.studio and toodle.studio. It lets me revisit the joy of learning programming and art that I felt when I first go to play with Logo. But in a "serious" and "gorgeous" language and toolset so I can pretend to be an adult in the proper circles.
I specifically love the choice of PEG because I've always hated the "regexp are bad" argument and PEG seems closer to my compilers coursework as well as more advanced.
So much to love about Janet.