Janet – a Lisp-like functional, imperative programming language
github.com
github.com
Janet has os/ and file/ packages, as well as a PEG system which mostly stand in for the best features of Perl. Some low-level POSIX-specific operations (like daemonizing and forking) aren't practical but I can just use C for those.
I am also using it to teach my son very simple programming. He can't type well yet, so the terseness of the language helps (there are cryptic symbols but he likes learning their names and how to type them). Its REPL and functional style I feel help a lot too.
My complaints about Janet are not too big. I feel like there are many ways to do some things, and they are all basically the same. Most of this excessive flexibility is inherited from Clojure.
1. It's never clear whether I need `cond` or can get by with `case` until I've waffled around a bit
2. I don't know whether to choose `if`, `and`, or `when`, for conditionals, because there are many times when all three would work. Also, if the condition expression is trivially negated (e.g. change '=' to '!=') then even `or` would work, making 4 choices.
3. It's hard to choose between `->` vs. manually-nested expressions.
EDIT: formatting
1. The Janet docs suggest `cond` as a replacement for if-if-else-else chains in other languages and `case` for `switch`-`case` blocks. That's a pretty good rule, although maybe you've already encountered that explanation? My main rule of thumb is that if the important value is "enum-y", representing a particular kind of thing or state, try `case`. If not, use `cond`. Neither Janet nor Clojure have enums (although you might be able to hack them in through macros or interop), so keywords usually serve that role.
2. I would prefer `when` to `if` because it's usually clearer what's going on. I usually don't use `and` in place of `if`. The trivial negation problem is an issue in basically any programming language that has `and`, `or`, and `not`, but it does add more options. Whether to use `and` or the equivalent `or`, I think your first instinct is often best.
3. I tend to favor threading macros (`->` and `->>`) when something is three or more functions nested and manually nested expressions for the rest.
For a total beginner, I would actually suggest forgetting about the more specialized options until you're comfortable. In other words, just use `cond`, `if`, and manual nesting. (Obviously still use `and` within the branches of `cond` or `if`.)
When I was using Java in my day job, I toyed with Clojure a bit and it was a pleasure. My favorite features were protocols and multi-arity functions.
I may give it a closer look sometime.
The README briefly mention the C API. How easy is it to use it, and how easy is it to "lift" results from the C API into Janet? (e.g. wrapping an fd from the POSIX API into a file object)
The former. Piping with the shell, or C subprocess hosted by the Janet script, or vice-versa.
I also wanted a language that gave a straightforward mental model without an opinionated philosophy that tried to protect me from mutability and other real-world things (hence I didn't really want to go with Haskell and ~purely functional languages).
FWIW I also think the syntax errors in Janet are somewhat hard to grok.
> So I feel like my time is better invested learning Racket instead?
I think it depends. If you want a language you can always rely on for 99% of use cases, something that covers many bases makes sense. For me, I wanted more of a fill-in-the-gaps language where neither C nor /bin/sh scripting make sense (e.g. stuff I may run on Windows or something heavy in string-handling).https://news.ycombinator.com/item?id=28255116 (August 2021) [114 Comments]
https://news.ycombinator.com/item?id=23164614 (May 2020) [269 Comments]
- Threaded channels `ev/thread-chan` (`thread` has been deprecated [and removed in latest version [[1.18]]])
- JPM (Janet Package Manager) is now separate, not distributed together with Janet anymore
- Janet binary can directly run image files with `-i`
What is an image and why would I want to run one? My best guess is that an image would be platform independent bytecode vs compiled executable?
It's not necessarily portable. Some lisps will use machine-independent bytecode, others use native code. I'm not sure which one Janet does.
https://en.wikipedia.org/wiki/Smalltalk#Image-based_persiste...
Janet's object system seems to be modeled after ideas first popularized in the Self language -- a Smalltalk derivative -- so there could be some direct inspiration there.
Xerox PARC was pursuing three workstation based OSes in parallel, based on Lisp, Smalltalk and Mesa (later evolved into Cedar).
So the ideas regarding images, REPL with typo suggestions, OS wide debugging, code browser,.... were available across them all.
http://bitsavers.org/pdf/mit/rle_lisp/LISP_I_Programmers_Man...
page 67: SET card
'...the state of the memory as it stands is read out onto tape 8 as the new "base" image for the memory...'
Not in the @pjmlp kind of availability ("Why you say that? There's this commercial language envinronment from a company in rural Austria that all of 100 companies use and all of 1000 people ever heard of that gives you exactly that! A 2-person department in the basement, right across the toilets in Jane Street uses it for something. And somebody made a weather balloon controller with it. Plus some mid-90s research team had this in some research language that has these features, that over 10K students have installed!").
Not even in the Pharo, SBCL, etc. kind of availability.
It would nice to have it in the "competes with Java, C#, Python, Javascript, PHP" or at least Go and Rust, for popularity, adoption, libraries, websites and sources devoted to it, job opportunities, vendor support, enterprise adoption, and such" kind of availability...
So you get what is possible with long nights, weekends and abandoned university thesis after graduation, instead.
Yes, but v8, Grail, the JVM, .NET takes lots of money to develop as well. Even Swift (despite Apple being stingy, so that is probably just a 5 person team force-fed Jolt Cola) costs a lot. But companies seem to pay for those, but not for those other styles/features.
To be frank, even if we had this technology, but it was a closed source, proprietary affair, I wouldn't want it anyway. That's another must-have feature in 2021 - and it's not just about "not wanting to pay" (I pay for IDEA subscription, for example). It's about what such a license would mean for corporate adoption, ability to tinker with it, reliance on a single vendor, and so on.
For starters, I think that it's probably no accident that you generally only see these image-based development features in dynamically typed languages. For image-based development to make sense, you really want to have long-lived interactive sessions. In Smalltalk, they are effectively endless, and can span multiple decades and branch among thousands of people. In a static language, though, you've got an incentive to keep your interactive sessions short and do most of your development outside of it, because code changes inside the interactive session can lead to weird chicken-and-egg problems.
Second, there are some real ergonomic challenges. It's easy to get into a state where what you're doing isn't reproducible, because there's not necessarily any paper trail on how to rebuild the current state from scratch. This is a famous criticism of Microsoft Excel - arguably the world's most widely used fully interactive development environment along these lines - and a major reason why a major project at of one of my previous jobs was to replace one department's suite of heavily scripted Excel spreadsheets and replace it with a C# application. The company was rightly concerned that they were one file corruption away from a major business disruption. Similar criticisms, albeit on a less operatic scale, are often leveled against Jupyter notebooks, which aren't quite the same thing, but to retain many relevant features.
Not really — most people get into a dreadful mess when they try to do that.
For most people, it works better to version and export discrete change-sets.
It makes sense to be able to save the state of your work, go to lunch, and pick up in exactly the same place when you come back.
It makes sense to be able to save the state of your work, go home, and pick up in exactly the same place when you come back the next day.
Yes there is!
"As you define and modify classes or methods, Smalltalk/V is logging all of these changes to the change log.
…
Every Smalltalk expression that you evaluate with either do it or show it is also logged. In addition, every time you remove a method from a class, a message is logged."
p282 "Automatic Logging of Changes"
https://rmod-files.lille.inria.fr/FreeBooks/SmalltalkVTutori...
Do you use any of the image based programming environments that we do have? Pharo? VM Smalltalk?
The lack of image support is what prevents Clojure from competing on startup time (but see also grallvm and babashka)
Ultima Underworld inspired game I'm making for a game jam: https://youtu.be/1fWsV83P-S8
Demo of pixel editor: https://www.youtube.com/watch?v=KOBi805nxNc
Freja: https://github.com/Saikyun/freja
---
My experience has overall been very nice. I come from Clojure, and comparing to that:
- so nice and small! Clojure is great but has a lot of baggage from JVM, for good and bad
- very easy to get started interoping with C. You need to manually wrap libraries, but I think it's pretty easy. As a benefit, you are able to throw exceptions from C that can be catched from Janet. The C support is what sells me on Janet over Clojure
- repl:ing works well, but you can get tripped up by early binding and lack of namespaces. There are ways around it, but when coming from Clojure it was a bit hard to wrap my head around
- many libraries that you'd want are already wrapped, like http-servers and postgresql connections. But ofc this is an area that is thinner than older languages. It's decently easy to wrap C-libs though
- easy to understand data structures if you've used JS / Lua -- "everything is a hashmap" (but there are also arrays and strings)
Thanks so much for the info and commentary!
If you do try it out, I'd be happy for any feedback on Freja! :)
I should mention that Windows support is currently a bit behind, so if you're on Windows I could fix that then comment here again when it's done. :)
I didn't get to experience C64 / Amiga era, but after having listened to 100s of interviews with swedish game developers it seems that the automatic (or even required) access to a coding environment sparked a lot of creativity and imagination. :) I hope Freja can capture at least a part of that.
https://ianthehenry.com/posts/janet-game/
Not a whole lot of content yet, but I plan to keep going with it.
I am pretty firmly in the Haskell/OCaml "let me program with types" camp for most development, but it's a great to have a little scripting language to reach for instead of bash or Python or Perl. Using Janet (coming from Python) reminds me a bit of:
Except... without the rich standard library :)
https://github.com/swlkr/janetdocs
I also scraped together a large web framework and a small one:
https://github.com/joy-framework/joy https://github.com/swlkr/osprey
What's funny about tw is that it was kind of like a ghetto tailwind jit before it was released!
Janet can really help you move fast
From your blog it seems like you're not really using it yourself anymore, though?
I tried to add a styling thingy (sorry, really not familiar with the terminology) to `tw` to add support for "print" media queries (like this: https://tailwindcss.com/docs/breakpoints#styling-for-print), but didn't get it to work on the first try. If I do make it work I'll create a PR :)
I always look forward to any and all PRs!
Edit: Yeah I haven't used it lately, I currently use roda + sequel now (ruby libraries).
I have limited time after work and I really want to get some "SaaS apps" up and running, so the trade-off was spend less time on libraries and more time on applications.
I’m currently working on ruby stuff on stream but maybe I won’t be able to stay away from Janet for long
What I like:
1. The applications start really quickly, compared to Clojure.
2. It feels like I could learn everything about the language if I ever want to dive into the internals. With Clojure/Babashka I don't think that would be possible for me.
3. A bit vague, but it really feels nice to program in this language. I can easily get things to work, I can mess around in a REPL if I want to, and #4:
4. The community is great. Instead of searching on the internet, when I don't know how to do something, I ask a question on gitter / matrix and before long there's an answer from a more experienced person (sometimes even from the creator of the language).
5. The package manager (JPM) is really easy to use.
6. In terms of download size, Janet it tiny. But compared to Lua, it's way more "batteries included" due to the great standard library.
What could be better:
1. Tooling I guess, but that's logical for younger languages. I use Olical his "Conjure" Neovim plugin, which is great. I don't think it's possible to jump to the definition of a function name if my cursor is on it. Maybe that's normally done through LSP in Conjure, I'm not sure. I don't know if there are plans to add LSP support to Janet?
2. I just found out a couple of days ago that Janet has no `set` datastructure. You can work around this by using a hashmap where each key maps to a value of true, or you can use a library that adds the datastructure.
3. Not sure if this would have downsides in Janet, but something I really like about Clojure is that you can use a lot of standard library functions on all data types. In Janet sometimes you have to look up a function specific to a data type, where Clojure would be more agnostic - with functions that work on different datatypes in an expected way, and return a `sequence`.
4. Some library authors have the tendency to use "The Good Place" references in their library names. I know the name "Janet" is a reference to that series, and I know developers in all languages are always trying to be funny. But finding a library called "html" (by swlkr) is way more helpful than finding one named after some character from a series.
What I think I will like, but haven't tried yet:
1. Object Oriented Programming in Janet seems nice (https://janet-lang.org/docs/object_oriented.html).
2. PEG (Parsing Expression Grammars - https://janet-lang.org/docs/peg.html )
Some rough spots:
- No canonical http client. There are a few attempts at wrapping libcurl but nothing complete and well documented yet. However, the creator of Joy framework for Janet does have an http client library.
- The main http server circlet is MIT licensed, but it is built on top of Mongoose, which is GPL/paid commercial. Something to be aware of if you want to distribute binaries made with this library.
- I have never been successful getting any of the UI or drawing libraries to work.
- Naming of packages is a bit confusing even if you have watched the Good Place and are aware of all of the inside jokes.
Have you tried jaylib (built on top of raylib)? I have gotten that to work well on macos, linux and windows. :)
> The main http server circlet is MIT licensed, but it is built on top of Mongoose, which is GPL/paid commercial. Something to be aware of if you want to distribute binaries made with this library.
While I haven't used it outside of development, chidi is MIT licensed. :) https://git.sr.ht/~pepe/chidi
The http server used in Chidi is actually fairly new and I was not aware of it. It is part of the spork package. About two months ago I chatted with the creator of Janet about whether or not an http server will be added. He said it would not be added to core, but it looks like he added to spork about a month ago. It looks like it is a pure Janet implementation so there should be no licensing conflicts. I'll have to try it out.
Then we get to the personal preferences territory. You may prefer a Scheme(with its hygienic macros and other goodies). Or the way it generates C code (which is then compiled).
As an example. At one time I was experimenting with writing iOS games in Scheme. The usual option would be cross-compilation. I would cross compile my Scheme program as a library and add the to project. But XCode is annoying because it wants ARM code for the actual device, and X86 code for the simulator. Rather than building a complicated build system, I just told Chicken to stop at C code generation, then added the generated code to the XCode project. XCode could now compile the entire thing as if it was all C+Objective C code in the first place. Add a couple of instructions for the actual embedding and there's my hybrid Scheme+Objective C program.
Related to the example above, I was missing an OpenGL ES library. But I found a similar library from Gambit Scheme. Given that they are both Scheme, the code would work with almost no changes. The only changes were related to the foreign function interface (to call C), which were not standardized. So I just wrote a 'compatibility' macro, and the code would now run.
Ultimately though, it's all up to you. I'm just happy there's always some healthy baseline interest in Lisp-like languages.
Janet makes a good system scripting language, or a language to embed in other programs. It's like Lua and Guile in that regard. It has more built-in functionality and a richer core language than Lua, but smaller than GNU Guile or Python.
They are Schemes though.
Does it have a global interpreter lock (GIL)?
https://janet-lang.org/docs/threads.html
Janet doesn’t seem to have shared-memory multithreading. Each thread runs independently, so a GIL should not be necessary.
In Clojure, there are lists, but the most common structure for sequential data is actually a vector [1], which works more-or-less as a slightly modified version of the Clojure version of a hashmap. Advantages of doing it this way is that you get log32 indexed lookup times (vs linear for lists), and you also get log32 [2] append-to-the-end semantics instead of "push to the beginning" that you have with lists.
In practice it generally doesn't affect a lot, but occasionally when I do need to put an item in the front of a collection, I will wish I had used a list instead of a vector, but I feel like the far more-common usecase is to append to the end, since in Erlang there's been a few times where I end up having to reverse the list after I'm done building it.
[1] At least how I use it, I haven't done any statistics to prove this.
[2] Don't let the log scare you! Log32 is very good and also effectively constant time for most in-practice use cases. Log32 of a billion is basically six, meaning you're dealing with a constant factor times six hops for inserts and lookups on a billion elements.
Wikipedia had an article on it deleted, and I'm not getting much from the first few pages of google...
There's a copy I made of the original paper here: http://trout.me.uk/lisp/
Assuming the majority of your lists are read-only apart from appends, you can end up with something pretty performant overall.
Trade-offs all the way down as ever though.
The drawback is that appending / removing elements at the beginning of the array is O(n).
When it comes to interacting with the collections I mostly use the loop / seq macros, which I believe are pretty similar to Common Lisp. Ofc you also have map / filter / reduce.
I did several experiments for a side-project. The end result was that I moved from ChaiScript (no generators or fibers or lightweight concurrency) to Wren language.
I saw Janet, and I was very tempted, but nothing like WrenBind17 exists for it AFAIK.
Julia, Janet, Eve...
I understand the guy referring to his car as his "girlfriend" and give it a women's name. I just can't feel the same about tech.
I guess this may be the nerd's way of immortalizing their sweetheart, but if I was a woman I'd rather prefer to be a car and not a programming language.
A programming language doesn't feel much anthropomorphizable to me, let alone lady-like.
Women, what do you think?
given that the character of the show in question is an archetypal secretary arguing it's not a woman is a fairly big stretch. Not only is it a woman, it's a woman embodying a female stereotype, (in line with the purpose of the language, being an easy helper), and the 60s art deco woman's magazine style logo
kinda like if I made a software called arnold with a picture of he-man as a logo I doubt one would be considered backward for thinking I had a guy in mind
Interesting question, but it might just be a yellow volkswagon effect. Quickly looking at Wikipedia's list of languages, theres Babbage, Bertrand, Cecil, Darwin, Dylan, Escher, Euclid, Euler, Godel, Hume, Orwell, Tom, Zeno, and probably a few others I missed.
EDIT: Haskell and Pascal, how embarassing to miss those
and Curry, while we're at it, https://curry-lang.org/
> The practice of naming hurricanes solely after women came to an end in 1978 when men's and women's names were included in the Eastern North Pacific storm lists. In 1979, male and female names were included in lists for the Atlantic and Gulf of Mexico.
Why do you assume programming langauges with female coded names are because the creator is viewing it as a girlfriend?
Not all creators are heterosexual males (or otherwise oriented toward females as preferred or even acceptable romantic partners), and even those that are don’t necessarily view women exclusively through the lens of romantic involvement such that that would be the implication of a female name. Also, not all female-coded names are actually female in reference (Janet is named after a character that is expressly not a woman.)
> A programming language doesn't feel much anthropomorphizable to me, let alone lady-like.
Why “let alone lady-like”? Why are female-coded names a particular issue, if the problem is that programming languages aren't “anthropomorphizable”? (I don't actually think naming things after people is inherently about anthropomorphization; we do it to freeways and bridges and other things as an honor, not as anthropomorphization.)
I am not assuming that. I was just comparing it to the situation with cars.
> Not all creators are heterosexual males (or otherwise oriented toward females as preferred or even acceptable romantic partners), and even those that are don’t necessarily view women exclusively through the lens of romantic involvement such that that would be the implication of a female name.
I wasn't implying such a thing. But I knew a woke person was going to point it out to me anyway. Funny.
> Janet is named after a character that is expressly not a woman
The logo of the programming language is the face of an attractive woman wearing a lipstick and a fancy hairdo. Also, Janet is portrayed by a woman in the Good Place. Don't tell me that the name of the language has nothing to do with a woman.
> Why are female-coded names a particular issue
I don't think it is an issue. I was just thinking out loud.
Take it easy.
Well you did say: "this may be the nerd's way of immortalizing their sweetheart", which seems to me to strongly imply that your impression may be closer to assuming it than to not assuming it.
I have no horse in this race (I'm not the one who replied to your original comment), but it did seem like you were assuming something awful close to what you are denying.
For me the thinking & debating exercises doesn't have to involve taking a position personally. Sometimes I just want to focus on the argument. My own position & assumptions are irrelevant.
For that reason I don't appreciate it when somebody makes an assumption regarding my assumptions to attack myself instead of focusing on the argument.
An argument doesn't assume, but it states. Assumption is something people do. When somebody makes a statement regarding "what an argument assumes", the meaning is that they are speculating beyond what the argument is stating.
So I don't agree with you, but thank you for the discussion.
Thank you for playing the role of the logic police. I guess it can sometimes be unreasonably hard to talk in a simple forum like this in a chill manner without having to deal with people's (IMO) misplaced pedantry. So be it. I'll try to be more scientific the next time.
Lisp-family languages (both CL and its immediate predecessors, and Scheme) for a long time were the textbook example of functional languages. The relatively novel (and still niche) attempt to redefine things so that only pure functional languages are “functional languages”, and particularly the people that insist on this usage as if it were firmly established and abuse those who don't conform to their preferred redefinition, is a bit annoying.
(union (lambda (x y) (cond ((null x) y) ((member
(car x) y) (union (cdr x) y)) (t (cons (car x)
(union (cdr x) y))))))
It had a MAPLIST (today called MAPCAR): maplist[x;fn]=[null[x]->NIL;
t->cons[fn[x];maplist[cdr[x];fn]]]
Or APPEND: append[x;y] = [null[x] -> y; T -> cons[car[x]; append[cdr[x];y]]]
Et cetera. Sure, it also had, say, NCONC. But it had these operators and style from the beginning, not surprisingly, as Lisp came from lambda calculus.Sure you can write functionally in Python with generators, first class and higher order functions, but its not really how the community writes (all the time).
Scheme isn't pure functional, it is mutable, but people write _as if_ it is immutable.
Point being, its very hard to say "X is or is not functional", as there isn't an objective consideration.
(FYI, I love Haskell and use it daily and write it professionally)
There may be some other examples in there. Personally I like things with controlled side effects. But it’s just my preferred set of trade offs.
Anyway I mean its just tough to be dogmatic about it. I get what you’re saying but why is that the dividing line?
Language features which are only possibly used in a non-functional style make me want to say that describing the language as 'functional' is either wrong or saying a language is 'functional' is a meaningless claim. If every language is functional, saying it is completely redundant.
I like your idea of saying FP style is idiomatic in that language might qualify the language as being described as 'functional', which I think agrees with my intuition that mutable strings being built in is evidence that calling that language 'Functional' is misleading or silly.
Nope. There are no cons cells here.