Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Janet: a lightweight, expressive and modern Lisp (janet-lang.org)
408 points by galfarragem on May 13, 2020 | hide | past | favorite | 269 comments



This looks so awesome! It's got the best parts of a lot of languages. This is what sticks out to me:

- Really simple lisp like scheme, but reminds me of lua (and not bloated like CL)

- Has resumable fibers, no callcc like scheme

- Not missing the lack of lists tbh

- A module system that doesn't feel awkward like CL

- A built-in package manager (unlike CL)

- Good lua and C support

- Threads have a shared-nothing approach with message passing (reminds me of Erlang actors)

- Destructuring

- Good PEG support, encourages it over regex

- (Im)mutable versions of data structures (ie tuples vs arrays, structs vs tables) for maximum flexibility

- Prototypal inheritance

- Docs are clean and easy to read

I'll definitely have to try this out, it looks really cool.

Some stuff I'd like to see:

- Pattern matching support (could be a library I guess)

- Multimethods

- Full numeric tower with arbitrary precision types

- Javascript compilation -- I could see this language being really useful for web and game dev

Does it have good debugging support? I'm thinking of something like slime, swank, etc. Can I set up emacs and/or vim to work the same way I can use CL with slime + swank?

I'm also wondering about the stack traces -- one of the downsides to CL is sometimes the stack traces are nasty to read


I wrote a tiny multimethod library for Janet: https://github.com/staab/janet-multimethod

The stack traces have so far been really clean and helpful.


> stuff I'd like to see [...] Pattern matching support

It looks like it does have a match macro, and it does destructuring and _: https://janet-lang.org/api/index.html#match


I never thought I'd see a Lisp without lists. Oh wait, I didn't because this isn't a Lisp.

I know naming is hard, but this is getting out of hand. Don't say you're a Lisp when you're clearly not. Say Lisp-inspired. Don't use the term 'modern Lisp', 9/10 it's signaling the wrong thing.


I'm so tired of this whole "xyz lang isn't a REAL lisp". First off, what's your definition of "real lisp", second off, who cares? What point are you trying to make? Is the language somehow less useful because it doesn't meet your arbitrary definition of what it means to be a "real" lisp? I imagine you sitting at home in a smoking jacket with some smug smirk typing this out.

Saying that a language isn't a "real lisp" because it doesn't use cons cells is like saying a map implemented using a red-black tree isn't a "real" map because it doesn't use an array of nodes.


The definition of what is Lisp does not have the moving goalposts you imply.

> Is the language somehow less useful

It may in fact be the most generally useful programming system in the world.

But does it have to be misleadingly named?

Would you make a four-wheeled bicycle and call it a "skateboard"?

> saying a map implemented using a red-black tree isn't a "real" map

Rather, it's like writing a map using an array of nodes, and then calling it "red black tree" and using identifiers like rbtree_t and rbtree_node_t all over the code, complete with a rbtree_lower_bound function that calls bsearch, followed by a linear scan to find the lowest duplicate key. Why? Because all the popular maps use red-black trees and the designer doesn't understand what that actually is, believing it to be a synonym for any ordered map.

Lisp is not abstract, like "map"; it started as the name of a concrete computer program with a reference manual describing specific features. It spawned descendant systems and imitations. Those which are too far from the original concept aren't Lisp, simple as that.

"Lisp" is an implementation word, not an abstraction word.

Lisp is a specific implementation of symbolic and list processing with specific shapes of data structures, names of functions and their semantics, treatment of Boolean conditions, syntax, and everything else.

Another implementation of symbolic processing, no matter how well or how badly it works, is something else.

People who make Algol-like languages understand this.

Wirth made some very similar languages yet prudently gave them different names. The result is that anyone who says that Oberon is Pacal or that Pascal is Modula 2 is promptly and correctly regarded as an idiot.


> Don't use the term 'modern Lisp', 9/10 it's signaling the wrong thing.

I think it's signalling exactly the right thing. We know exactly what to expect when someone claims they've made a "modern Lisp". That phrase has come to mean "something with parentheses that demonstrates that its author doesn't understand Lisp at all."


Hilarious, but true!


From the examples:

# A simple fizz buzz example

(loop [i :range [1 101] :let [fizz (zero? (% i 3)) buzz (zero? (% i 5))]] (print (cond (and fizz buzz) "fizzbuzz" fizz "fizz" buzz "buzz" i)))

Is this not lisp?


Lisp comes from LISt Processing, and prefix notation with enclosing parentheses alone doesn't mean processing lists.

With that said, the underlying datastructures don't actually deter from the fact that you can quote and unquote what appear to be lists, and it walks and quacks more or less like a lisp (like clojure, as a sibling pointed out), so I don't actually mind.

In fact, Janet just might be my favorite scripting language, even if I haven't had occasion to use it for anything impressive yet.


I'm not clear on the exact reasons, but I think some people see it as "Python with S-expressions" rather than "Lisp". I think it's because Janet does not use lists implemented with cons cells and historically that's just what Lisp always used. (Janet uses arrays instead.) But by that logic Clojure isn't a Lisp, if has no cons cells...

Another criteria used is homoiconicity.

I think the argument is about if a language satisfies the historical definition of "Lisp" rathen than being a Lisp-inspired language with parens.


A good reason to regard this as "Python with parentheses" is because not only does it use arrays instead of cons cells, but there's no non-mutating `append` function. Instead, Janet has an `array/concat` function that behaves like a Python array's `.append` method, and that's it.

And no, Clojure isn't a Lisp. Perhaps it would be appropriate to say that Janet is "a Clojure".


Clojure isn't a Lisp, and really screws up some of the concepts.


Can someone explain why this comment was downvoted?


[flagged]


Category error maybe: CL is a language standard with several very extremely different implementations and a diverse tool landscape. Janet OTOH is a single language&implementation.


Likely for bashing CL (however lightly). People get tetchy.


See some previous HN threads on Common Lisp.


Okay, so when Clojure came along, it came with a strong underlying philosophy of what the language was supposed to be/do. Among them were design decisions such as, e.g.,:

  - VMs, not OSes, are the platforms of the future, so target the JVM
  - Object Orientation is overrated, but polymorphism is a good thing
  - Multi-core is here to stay, so use immutable data structures to greatly facilitate writing correct concurrent programs
  - Leverage the strengths of LISP but give it a modern overhaul (most notably, throw in different parenthesis)
Now, you may or may not subscribe to any of these but my question is just: how does Janet compete on that front? What is the problem it is trying to address specifically? I looked at the web page a bit but it's still not clear to me.


What I like in Janet (I'm just a noob so take this comment with a grain of salt as it will sound superficial):

- Easy to get started: one click install, great website and concise docs. No matter what some people say, getting started in Clojure is a nightmare.

- Lightweight and fresh. No JVM, no Node.

- Freedom and expressivity. Mutable or immutable data structures, ultimately is up to me. It might bite me down the road but for now it feels great.


I've posted this quote before, but it might help explain why making Clojure easier to get into maybe hasn't been a priority for Hickey et al.

So we need players. I would rant here, but I won't. But look at this guitar player with blisters. A harpist has blisters, a base player with blisters. There's this barrier to overcome for every musician. Imagine if you downloaded something from GitHub and it gave you blisters. - Rich Hickey (Design, Composition and Performance)

Clojure seems more concerned with power, expressiveness, and reach, rather than ease of use. Does this alienate new users, sure, but the Clojure community seems OK with that as a trade-off.


I am full to my neckline with Clojure kool-aid: I love the language and programming experience. But the "getting started" your parent is referring to, IMO, is getting the tooling set up, getting the repls connected, figuring out what a full-stack app looks like with its 2 distinct repls and toolchains and lein config mashups, hooking up an editor... and then having it break the next day. After 5 years of Clojure I still get frustrated if I'm away for more than a month.

It's like you were getting blisters from stringing your guitar. You buy a guitar, get home, and it takes three days to open the case. Then another week to figure out which strings go where. God help you when you realize you have to tune the thing.

Two, five, seven days pass and you haven't played a note. I don't think that's what Hickey meant.

That said, it's worth it.


Spot on with your amusing guitar analogy. Getting to play notes seems like an afterthought, but when it does it plays for you as if it reads your mind to do so. The problem is the setup and that's the part that I dislike these days, the patience to to configure the tools is not there, a lot of the tools are like black boxes, something goes wrong and it isn't visible or or it is too complex to enjoy the process.


If you're not familiar with the JVM, I think this is a big ask for most people as far as ease is concerned. I have created a template on my github to use clojurescript with your typical NPM setup that would allow you to get started right away. I know of no equivalent in the JVM world mostly because of how tightly Oracle controls the download experience of the JDK.


Well, Oracle JDK is a non-starter outside enterprise settings these days. You should be using Zulu or Corretto, and both are much easier to download.


No, you should almost certainly be using openjdk, which oracle jdk is based off, unless you have a compelling reason an alternative implementation is better.


OpenJDK releases become unmaintained as soon as a new release comes out, not ideal for anyone who cares about stability. Zulu and Corretto backport bug and security fixes to supported releases.


Zulu and Corretto are both distributions of OpenJDK.


That assumes it is a tradeoff. Maybe you can have both. I see nothing obviously preventing having power, expressiveness, and reach, and ease of use


I agree that it doesn't have to be a trade-off, but that mindset might explain why there is less focus on creating one-click installers.

An example would be Clojure's newish approach to managing projects via the CLI and deps.edn. It's very powerful, and expressive, but definitely not a one-click approach.


Clojure is an inspirational language that has some insightful opinions about how things should be done. A month spent learning Clojure is a great investment. A lifetime programming in Clojure would be a joy.

But as I recall the original Rich Hickey talks on "Why Clojure?" he was explicit that dependency/project management were very hard problems that Clojure didn't really attempt to solve.

I'm not aware of any Clojure tools that are best-of-breed for project management. Java has a better model for locking everything down (or maybe how the Linux Distros manage their repositories) and Python has a better model for just keeping up with the current state of things. I'm not aware of any half-and-half model that gives satisfying results.

Clojure is inferior to Java in that aspect because the number of things to keep track of roughly doubles (for every concept there seems to be a Clojure version and a Java version) and library management is bizarre and warty - see also the horrible import/require/use/some other one I can't even remember mess. And then there is unpicking the syntax differences between using things in the (ns) macro vs calling direct. I dunno; maybe something changed but that part of the experience is a mess. Unusually poorly done in Clojure too, everything else was so lovely.

I never got on top of it and I tried for months; as evidenced I can't even remember how to load a library after putting it down for a little while. Even assuming the classpath is correctly set. What would have to happen for a C programmer to forget #include "name.h" ?


> I'm not aware of any Clojure tools that are best-of-breed for project management. Java has a better model for locking everything down (or maybe how the Linux Distros manage their repositories)

Uh? Clojure locks everything down as a well. In fact, deps.edn solves many issues experienced with Maven.

Personally, I've found Clojure dependency management to be the best I've ever used in any language. It's simple, explicit, gets out of your way, and lets you just do your work.

> and Python has a better model for just keeping up with the current state of things.

I'd like to hear about this? Pip doesn't even have a command to update all dependencies in one go. And figuring what even are the dependencies of any given project is a mess.


> he was explicit that dependency/project management where very hard problems that Clojure didn't really attempt to solve.

For what it's worth, having used Clojure for work for a couple years now, and ditching leiningen for deps.edn (built in package and dependency management that hooks into maven), I've not had an issue with it. You just add the dependencies to your deps.edn, and run your program. If your namespace needs something from a package, you add it to the (:require ...) section of your ns declaration.

Here's an example ns from one of our messiest modules:

    (ns profusion.core
      (:require
       [clojure.core.match :refer [match]]
       [clojure.set :as set]
       [clojure.string :as string]
       [clojure.walk :as walk]
       [clojure.zip :as z]
       [instaparse.core :as insta]
       [profusion.infix :refer [infix-to-prefix]]
       [profusion.standard-library :refer [comparison]]
       [profusion.utils :refer :all]
       )
      (:import
       (org.jgrapht Graph)
       (org.jgrapht.traverse TopologicalOrderIterator)
       (org.jgrapht.graph
        DefaultEdge
        SimpleDirectedGraph)
       ))
To be clear, projects like this can be run with the base Clojure distribution, no additional tools required.

> I'm not aware of any Clojure tools that are best-of-breed for project management. Java has a better model for locking everything down (or maybe how the Linux Distros manage their repositories) and Python has a better model for just keeping up with the current state of things. I'm not aware of any half-and-half model that gives satisfying results.

deps.edn generally asks you for specific versions if you're getting things from maven, so you don't risk not "locking everything down". If you really want to lock everything down however, you should check all your dependencies into your source tree, this is an option that deps.edn supports well, through the :local keyword instead of :mvn/version for maven dependencies. That namespace declaration I showed you is from a subproject that we import into one of our other subprojects using the :local keyword, and the deps.edn for it just declares the source directories and and the dependencies, everything else is handled automatically.


That is a perfectly reasonable way of doing things. However, the official reference [0] recommends:

(1) Your way (but also throw in an :only and doesn't mention :refer)

(2) (require 'clojure.contrib.def 'clojure.contrib.except 'clojure.contrib.sql)

(3) (require '(clojure.contrib def except sql))

And leaves open a lot of room for someone to try to use (use) which is probably a mistake since you don't use it.

And sometimes your way has [] and sometimes it doesn't. Pretty much at random considering '[profusion.utils :refer :all]'. And the official docs add in a dizzying number of combinations of quoting requirements to recall.

It isn't the end of the world but that is a far cry from how good all the other parts of the language are. It is really sloppy and confusing to learn. There is no recommended approach and I personally can't remember which easy way I used last time so my personal projects cycle randomly. I think Clojure has the worst library loading mechanism of any language I think is good. Even your way has two completely different code paths (import vs require) to bring in dependencies.

[0] https://clojure.org/reference/libs


> Even your way has two completely different code paths (import vs require) to bring in dependencies.

import and require have different purposes though, it makes perfect sense to me that they are separate. Import, here, is used to import Java classes, which don't hook into Clojure's namespaces directly; require is for bringing in symbols from Clojure namespaces. In general, a beginner or intermediate Clojure user will not touch the :import feature at all, nor the :use feature.

> It is really sloppy and confusing to learn.

That's one opinion of it I guess; though in my experience, the way that I do it is very common in publicly-visible Clojure modules. I've been doing it the same way since day one, without issue. There may be other ways, but you don't really need to care about them.

With this you'll understand most modules you come across, and you'll be able to accomplish basically anything you'll want to.

Given the average completeness of documentation on publicly-visible packages (though to be fair most big ones have what most would consider adequate documentation) including the core, you're going to have lots of opportunities to read other people's modules, and these minutiae will fade into obscurity and be overshadowed by your own program's problem space.


> For what it's worth, having used Clojure for work for a couple years now...

> ...it makes perfect sense to me that they are separate...

> A lib’s container is a Java resource whose classpath-relative path is derived from the lib name [0]

> No matter what some people say, getting started in Clojure is a nightmare. ~ galfarragem (earlier in the thread).

I'm hoping this smorgasbord of quotes is making my point for me - the fact that there is an easy way is good, but the problem is that the easy way is very well hidden and Clojure is competing with systems like 'pip install foo', 'import foo' in every single piece of documentation on Python there is on the internet.

I'm not budging from Clojure's import system being byzantine. I don't care that the people who stuck with Clojure figured out something that works for them, it is stupid and it is a very effective filter on keeping people out of the language. The lesson 'ignore half the reference manual page' is not an appropriate lesson to be learning while working out how to load dependencies.

EDIT If all the other parts of the project build were easy then obviously this wouldn't be an effective filter; but this combined with a misconfigured classpath issue for someone who doesn't know Java well would be the end of it.

[0] https://clojure.org/reference/libs


Yep..I've found Clojure to be pretty difficult as I have very little Java experience. The language itself is fine, but needing to install and configure so many tools to get anything done is a major turn off. In my opinion, Clojure/Scala/Kotlin on the JVM and F# on .NET all have the same problem of assuming you're a Java or C# developer. I have zero interest in either and asking someone to spend a large amount of time learning them so that I can hop to a language I do like is a stretch. That's why they'll never beat Python in adoption which doesn't require 20 years of knowledge of an entire ecosystem.


No, these are not making your point for you. You bring up :import and :use, but in truth, a beginner or intermediate Clojure user is not going to use either of them at all; and when they choose to, it'll be because they have some deeper knowledge which will make the documentation appropriate to them. :import is primarily for people who understand Java and the JVM. If you know what you're importing, you will understand :import quickly; if you don't know what you're importing, you have no use for :import anyway. In the case of :use, nobody needs :use; it is useful primarily to the people who know why it is useful, and the documentation for it is suitable for that audience.

Getting started in Clojure is not a nightmare, ordinary people do it all the time, the fact that somebody said it was a nightmare says very little.

Clojure's mechanism for requiring packages is far from byzantine, whether you "budge" on that or not; many Clojure libraries have the snippet for including the current stable release of that library at the top of their README on GitHub. It isn't matter of "sticking with it" or "figuring out something that works for you", it is literally one relatively simple piece of knowledge that is learned once and works forever.

In short, if you find namespaces and importing byzantine in Clojure, you are probably at the level of struggling to write fizzbuzz in any language. The fact that there are features other than :require in ns does not indicate that :require is complicated, it just indicates that ns has features.


And people wondered why Clojure is dying...

Somehow a language designed from scratch with all the purported wisdom of Lisp ended up with a nastier import solution than JavaScript, which had none out of the box for its first two decades of life, and then had to reconcile as many as 5 different module systems.


Am I missing something here? The experience is basically the same as the best available in JavaScript/TypeScript, how is it "nastier" or even "nasty" at all?

Also Clojure is doing just fine, not sure what you mean by "dying". The core and the tooling continue to improve, there are lots of libraries, more people writing than ever. I just don't see this death you're describing.

The comments here just strike me as ignorant and incoherent as a whole. Why do people who clearly have no experience actually trying to do something in Clojure have so much to say about it?

Clojure may support things that look daunting to you, but if you actually had any goal other than critiquing the variety of things you actually have no use for, you would not have the difficulties you describe.

Maybe I'm spoiled, I remember JDK setup being a pain on Windows a decade ago when I used it last, if that's the problem then I get it. My spoiled experience on a system with a functioning package manager is basically install-and-go, and zero configuration required to get CIDER going after that.

I just don't get what's so bad about it, it's basically the same experience as Rust with Cargo; is that another "nasty" experience? What language with package manager has a better experience?


>For what it's worth, having used Clojure for work for a couple years now, and ditching leiningen for deps.edn (built in package and dependency management that hooks into maven), I've not had an issue with it.

>...

>To be clear, projects like this can be run with the base Clojure distribution, no additional tools required.

Didn't you just say that dependency management hooks into Maven? So doesn't that imply you need to get Maven set up before you can run a Clojure project?


> So doesn't that imply you need to get Maven set up before you can run a Clojure project?

Not sure what is meant by "get Maven set up". With Clojure it does not involve any additional work, it's just built in to the Clojure runtime, and it goes ahead and fetches your dependencies either when you ask it to, or right before loading your program for the first time.

I think they just use the Maven repositories, as in the servers themselves, not the Maven client software for Java, whatever that may be (I've never used it).


Then surely, you need to set up a Maven server before you can use Clojure.


What turned me of from clojure when I tried it last time was "Why my trivial syntax errors like unmatched brackets or easy semantic errors like wrong number of parameters to 'built in' functiins blow up in my face with java stack that involves some parser or something only after I 'lein run' the program?" I used vs code with most popular clojure plugin and I was expecting more.


Thanks for sharing this... I'd never seen it before. It does make me wonder how many languages we use that could be compared to bag-pipes... in that even when you learn how to play them expertly, they still sound like an animal being killed.


> No matter what some people say, getting started in Clojure is a nightmare.

You just need Java installed on your system, then you install clojure (via brew, or sh install) and that's it, you're good to go.

I've written a guide to starting with Clojure covering installation to REPL & IDE configuration: https://grison.me/2020/04/04/starting-with-clojure/


This process might seem easy for you, but maybe not for people installing Clojure for the first time.

In an extreme case, someone who doesn't know Java ecosystem might want to stop to research: what is JVM, why is it necessary, which version would be the best, what's the difference between Oracle's Java and OpenJDK, etc...


If you don't know what Java is, you are not in the Clojure target market.


Then they should stop talking about it in /r/Lisp. Nobody there wants anything to do with Java, but Clojurians won't shut up about it.


There is a subtle difference between knowing what river is and knowing how to get to the other bank.


One could say the same for a plethora of programming languages some research is needed and we don't have to resort to extreme cases.

Out of the top of my head:

- There is the Python 2.7/3.X situation still going on, then there are the several distributions of python.

- GNU's R has a similar problem

- Installing gcc on windows was (perhaps still is) a confusing mess for a newcomer.

IMO I did not find installing clojure any harder than installing Scala for instance, and yet I have never heard (anecdotal, I know) complaining about Scala.


The user above mentioned that for _them_ the installation experience of a programming language matters a lot, and that they found the installation experience of Janet superior to Clojure.

You might not share that users preference of caring about the installation experience of a PL, but arguing that this user's preference is somehow unfair because many other languages also have bad installation experiences makes absolutely no sense.


the comparison was to Janet, not Python, R, C or Scala


Isn't GCC on Windows just unzipping a zip file and adding that location to path?


> you install clojure (via brew, or sh install) and that's it, you're good to go

The vast majority of PCs are Windows, not Mac or Linux. Though WSL2 (available with Windows Insiders) makes working with Linux software a breeze, it's not the standard flow. Java isn't installed by default, either.

Your guide suggests using WSL and IntelliJ. Does IntilliJ actually work with Closure running in WSL? For that matter, does the VS Code plugin?

These are all headaches a new programmer won't have when getting setup with PHP, Python, JavaScript or most popular languages.


The Windows story for Clojure is disappointingly weak, especially if you are going by the "Getting Started" section on their website.[0] They have reasonable instructions for Linux and MacOS, but for Windows they point you to an "alpha" level tool.

I think this is especially disappointing because there are mature, cross platform solutions available but whoever manages the Clojure project seems hell bent on pushing their "clj" tool which is, clearly, treating cross-platform concerns as an after thought. The Clojure organization conducts a survey every year and this is hurting their adoption with developers on Windows.

"One new question we asked this year was about the primary developer operating system to give us better guidance about tool-related work. As expected, MacOS was the leader (55%), followed by Linux (35%) and Windows (9%)"

Getting started with Leiningen, in my opinion, is much easier although, as has been pointed out, installing a JDK is an extra step that other languages don't have.

[0]: https://clojure.org/guides/getting_started

[1]: https://clojure.org/news/2020/02/20/state-of-clojure-2020

[2]: https://leiningen.org/


At least lein and boot-clj is available for/via scoop.sh. Seems a bit of a missed opportunity that "official" clojure "clj" isn't just using scoop.


Agreed, the Scoop instructions are simpler and many developers will already have it installed.


I have both a MacBook Pro and a standard PC running Windows, and yes on Windows I run Clojure both on Windows and inside WSL, both IntelliJ and VSCode works pretty well with WSL there's really no problem running it.

I'm doing a lot of Clojure since years both on my MBP and on Windows and there's no problem at all doing that way. Besides VSCode even has a plugin for WSL which will let you directly hook into it.

So yeah there are some questions like which Java to install, which IDE, .. but these questions are the same for other programming languages.

If you have any hint regarding how to improve my guide don't hesitate to tell me if something is not clear in it :)


I'm not familiar enough with Clojure to help (ironically because I myself was put off by the tooling). The reason I asked about WSL and editor issues is because I encountered them myself when working with Elixir on WSL and VS Code and saw related issues on SO about Clojure. This was almost 2 years ago, though.

Probably the biggest thing would be to assume the person reading the guide has a brand new computer and hasn't installed anything yet (including Java and Homebrew).

It's really hard to assume much about the background of someone just trying a new programming language. Maybe they've worked with other languages and you're familiar with, maybe not.


This I think is a fundamental impedance mismatch between Lispers and everyone else.

A Lisper sees a REPL, and thinks they have "gotten started." When I think most language learners don't think they have "gotten started" until they, on the low end, have printed something at a familiar terminal, or, on the high end, pointed a web browser to localhost:8080 and seen some text there.


A lot of tutorials assume the use of lein, which isn't part of Clojure, and isn't properly packaged for windows.


Running the batch file that comes with the project works for me and has worked on a variety of versions of Windows. I haven't had as much luck with the "clj" tool.

I don't see who the "clj" shell script is somehow "part of Clojure" any more than Leiningen or Boot. IMHO it's just a shell script and it works similarly to the other tools, except not in any way cross platform.


Yes but Leiningen is not distributed with Clojure. And as far as I remember there is no installer for Leiningen on Windows. Right now I find it is available through Chocolatey, but the main Leiningen page asks users to download and run a batchfile. That's a very strange instruction to ask a windows user to do: Running batch files downloaded from the internet goes against so much advice on computer security that is given to users.


Neither is clj, for some reason, and it installs the same way as Leiningen. ;-) From their website:

"Then run:

Invoke-Expression (New-Object System.Net.WebClient) .DownloadString('https://download.clojure.org/install/win-install-1.10.1.536....

There's also no clj installer for Windows although there is a Scoop package.

https://github.com/clojure/tools.deps.alpha/wiki/clj-on-Wind...


tried to install Clojure on Windows. Just flat out gave up.


I still feel dreadful when doing all these integration with leninigen. Work for whole month then get it. But there is limit of my personal time budget.

And how to work with vscode as it all fails, at least windows.

But love clojure syntax. In particular I found it hard to see all () and the simple added some syntax sugars help the eye a lot.

Hence jump to janet. In fact the community is very helpful.

Not sure but the size is small enough to see whether one can do embedded as I try picolisp but it is bounded to a low end Env.

Very promising.

Btw the whole point I later schedule more time to try clojurescript to ... May be that can wait a bit.


1 - Download a JVM and make it available on the PATH

2 - Dowload clojure.jar

3 - Start command.com or PowerShell

4 - cd into clojure installation directory

5 - java -jar clojure.jar


Of course, that's not the official instructions. These are the official instructions: https://github.com/clojure/tools.deps.alpha/wiki/clj-on-Wind... - they've managed to work out what the powershell is for "run untrusted shell script directly from HTTPs".

What there should be is an exe or MSI installer like every other Windows program.


The official instructions are here, https://clojure.org/guides/getting_started#_installation_on_...

Not directly the link you provided.

Yes, they require Java knowledge I guess, but then again using guest languages without caring for the host platform is a fallacy.

Now I do agree that an installer would be the right experience to provide.


So according to that page, there are 3 ways to install on Windows.

1. Alpha version of "clj on Windows", which seems to be the recommended approach, and requires going to another page to get the instructions.

2. Homebrew on Linux or Windows with WSL, linked to under "Installation on Linux".

3. Build from source, under "Other ways to run Clojure", which assumes git, Java, and Maven installed, with no instructions on how to install them.

I would say it's pretty confusing from that what is the optimal, recommended way to install Clojure on Windows. It might be better to have subheadings under "Installation on Windows" like "For Java developers" (assuming Java tools already installed), "For WSL users" (Homebrew), "For Powershell users" (clj on Windows). This would consolidate the Windows installation strategies and reflect the diversity in the Windows developer population. Some are already full time Java developers, some live in WSL as much as possible, and some fully embrace Windows specific tools like Powershell.


Where can I get such a clojure.jar? What worked for me was

...

2 - Download clojure-1.10.1.jar, spec.alpha-0.2.176.jar, core.specs.alpha-0.2.44.jar from mvnrepository.com

...

5 - java -cp clojure-1.10.1.jar;spec.alpha-0.2.176.jar;core.specs.alpha-0.2.44.jar clojure.main



Maybe I'm blind, but I don't see any such download link. Or did you use

2 - Dowload clojure.jar

as a shorthand for

2.1 - Install Maven

2.2 - Install git

2.3 - Clone https://github.com/clojure/clojure.git

2.4 - Run mvn -Plocal -Dmaven.test.skip=true package


I wasn't aware of the page update, there has been like two years since I used Clojure last time.

It used to be so that they provided a zip to download.

In any case as mentioned on a sibling comment, I assume that anyone willing to use a guest language should get comfortable with the host platform.

So git, well for better or worse that is what most are using nowadays.

Maven, number one build tool on Java world, even if using another one, most likely those jars are coming from Maven Central.

Just like, regardless of my opinion on C, I keep my C knowledge up to date, because that is what I eventually might need to reach to when on UNIX like platforms.


> 3 - Start command.com or PowerShell

command.com? Is it 1995?


Unfortunately not all Windows shops got the message.


They're (probably) alluding to the fact that command.com is spelled cmd.exe since Windows NT became the mainline.


Then you might like CL by trying it with Portacle, a one-click install away: https://portacle.github.io/ (there's also a good plugin for Atom, SLIMA)

CL is a breeze. Even installing new dependencies into a running image is a breeze, and no need to restart anything.


>Even installing new dependencies into a running image is a breeze, and no need to restart anything.

You don't even need to leave the REPL!


I like lightweight and fresh too, but the JVM and Node both provide a tremendous amount of “batteries included” ecosystem value.


See I get why you’d say that about the JVM, but as someone working in Node daily I don’t find the server side JavaScript ecosystem particularly useful. As soon as you need to do anything else than database CRUD, you’re basically on your own.


> Easy to get started: one click install, great website and concise docs.

Idunno, I read the whole homepage and I don't really know where to go to download it. Is "clone and run Meson" what you meant by "one click install"?

> No matter what some people say, getting started in Clojure is a nightmare.

On my distro I just do pacman -S clojure and I have a working clojure environment, on everything else it's usually available as one package. Not much of a "nightmare" to my eyes, at least when compared to Janet, which doesn't tell you how to install it anywhere on the website.


Janet's purported focus is that it's easy to embed in a larger C program. I haven't really seen that in practice, but I have used the C api to write some language extensions, and it is a joy. In comparison with Clojure, I much prefer using either C or janet to using Java and lisp side by side; with Clojure running on multiple hosts now the differences in language semantics between clj and cljs are just annoying.

As far as language features, I miss a lot from clojure: immutable/persistent data structures, protocols, not to mention concurrency primitives. But Janet's approach to performance and design is "do less". With the C api you can easily add bindings to some C library that provides a desired feature. But even without that stuff, Janet is for me a python-killer, if not a clojure killer.

Edit: at any rate, I think it's not meant to be a competitor to Clojure, its similar syntax just often invites the comparison. It's much more fair to compare it to Lua.


> Janet's purported focus is that it's easy to embed in a larger C program.

There is Guile https://www.gnu.org/software/guile/ that is an implementation of Schema with the focus on being an extension language that integrates with C and C++. Wouldn't it more practical to adopt and contribute to it, rather than invent an own wheel?


Author here.

Firstly, this is a rather silly argument in general. One could say that Embeddable Common Lisp and Guile address the same niche and all developers from one project should consolidate in the other.

I have not used Guile to any extent, nor was Janet really influenced by it, but the comparison has been brought to my attention several times in the past. Janet is not scheme nor common lisp, so there are of course many differences that I cannot address here. However, as an extension languages, there are several things that I believe Janet does better than Guile.

Guile is not an ideal extension language for C projects (this is distinct from an extension language for GNU projects). Firstly, they address garbage collection with the Boehm GC, which takes some control over your runtime and is not known for great performance (the runtime/non standard C is the bigger issue). This was of course a decision made by Guile devs to make it easier to write correct bindings to C libraries in Guile, but it is not without it's downsides. Secondly, the project is not easy to embed in foreign build systems. Janet is more like Lua - built in tracing GC that is not conservative nor platform specific, no external dependencies beyond libc, and can be added to a project as an amalgamation (a single C file and 2 header files). This has enabled people to get Janet running more easily on a variety of platforms like mobile phones and even recently someone has been doing some work on getting Janet running on the Nintendo Switch. I believe Janet is also significantly smaller than even the smallest build of guile. Basically, Guile is not simple, which is (IMO) antithetical to an ideal extension language.

At the same time, Janet should feel like a much less minimal language compared to Lua. While core Lua limits itself to pretty much only standard C, Janet goes a little further to provide some abstractions over different platforms (that can be turned off via compile time options).


Thank you for elaborating on the reasons to create Janet!

My argument can sound silly, particularly considering the fact that you do not have any obligations to justify your project that you opened for others. I am very thankful to open source contributors, but I have a feeling that many open source languages, frameworks, libraries, and desktop environments would be more popular, polished, and maintained, if we could compromise, tolerate, and control our ego or creativity, and contribute to existing projects.

I think, we can see the benefits of focus and discipline by comparing Rails and its gem ecosystem with “blossoming thousand flowers” of Node.js web frameworks.


It's not just silly. Sometimes this concern comes across as scolding people for how they spend their free time.


I'm in no way associated with Janet, so I can't answer for them. But as someone who is implementing their own language which shares a lot of goals with Guile, the reason I didn't contribute to Guile is that Guile is an implementation of the Scheme Standard, and it bound at least loosely to that. Certain modern features just aren't compatible and might not be accepted if you go through the effort of implementing them. There are real benefits to implementing a standard, and I'm not criticizing Guile for doing that--I use Guile. But if your goals aren't compatible with the standard you have no choice but to go a different way.


Guile is more than a Scheme implementation. You can implement a completely new language for it (somebody recently did Python) and reuse their embedding capabilities. Guile comes with an Ecmascript implementation, just to show that it can be done.


You can do that in most (all?) Scheme implementations. Macros are powerful, yo.

This goes back to another discussion here: just because you've implemented a language doesn't mean you've implemented it well. Sure, I can implement all the syntax of what I want. But is it fast? Does it report errors well? Am I enforcing my type system, or can I accidentally drop back into normal Scheme?

I think a lot of times new Lispers learn macros and, drunk with their newfound power, start writing 20% implementations that get them 80% to mature languages. And if you don't need or want the other 20% of a mature language, then that's fine. But if you do need or want the other 20% of a mature language, then you'll quickly discover that the Pareto Principle[1] applies. Lisp metaprogramming can be super powerful, but it's not a magic bullet: not only does it not kill werewolves but you can shoot yourself in the foot with it.

[1] https://en.wikipedia.org/wiki/Pareto_principle#In_computing


Janet uses an MIT license, so you've got a pretty big divergence right there.


On the VM front, I love the JVM, but a vast majority of languages gaining popularity are generating static binaries. GraalVM may help close the gap here.

OO is overrated but most apps have some state and I found the entity-component systems solid but not still unwieldy.

Clojure has really great concurrency primitives; all the semantics I could want built right in. Except my concurrency often occurs in the persistence layer, and unless I'm also writing that layer, Clojure can't help me. Persistent/immutable data structures are amazing.

The delimiters IMO are a vast improvement over most other lisps (especially for creating specific data types).

Clojure's startup time is pretty brutal for a lot of things I'd like to use it for, and no amount of REPL-driven development can make Clojure appropriate for CLI tools.


> Clojure's startup time is pretty brutal for a lot of things I'd like to use it for, and no amount of REPL-driven development can make Clojure appropriate for CLI tools.

If you got the repl-driven development down, you wouldn't test your CLI by launching it, you would just evaluate the same functions as the CLI uses. Only time when you'd want to launch the CLI would be before releasing a build or for E2E tests. Once you got the code down for your repl and the startup time becomes a hassle, you compile down your Clojure program to a binary with GraalVM and now it starts up fast.

Otherwise, give babashka a try. Basically single-binary Clojure environment. https://github.com/borkdude/babashka


It's not about testing, it's about using it. I know how to use a REPL :)

GraalVM simply is not production-ready, though I follow its progress with great interest. That said, it's hard to compare the process of integrating with GraalVM against a language that actually just has fast startup.

Babashka is pretty cool, but it's another example of something that isn't quite Clojure. Nice if you can tolerate various "almost the same" languages, but Clojure itself is still weak in these domains.


I hear you, GraalVM is pretty new and while I've managed to write and deploy plenty of clis using it without any problems, it's a big hassle to deal with anything GUI or server related. So it does have a long way to go.

And while Babashka is not Clojure, it's interpreter (sci - https://github.com/borkdude/sci) is in fact a "Small Clojure Interpreter" that tries to stay as close to Clojure as possible. Unless you're using the Java interop, I've yet to hit anything I cannot do in Babashka that I could do in Clojure (I barely use Java interop)


Maybe check out https://github.com/borkdude/babashka if you have bash script esque workloads


I think we'll see a flip back to VMs once we start seeing a variety of architectures in production - ARM, intel, risc-v.

Managing distributions for a bunch of different architectures is difficult.


That would be interesting, though I wonder whether containers will (sadly) be the answer there.


Mobile-readable version of your list of Clojure design decisions:

- VMs, not OSes, are the platforms of the future, so target the JVM

- Object Orientation is overrated, but polymorphism is a good thing

- Multi-core is here to stay, so use immutable data structures to greatly facilitate writing correct concurrent programs

- Leverage the strengths of LISP but give it a modern overhaul (most notably, throw in different parenthesis)


My apologies, it was too late to correct my post when the complaints started coming in.

...but then again, who reads on a mobile device anyway (j/k) ;-)


‘Starts up faster than Clojure’ and ‘doesn't need the setup+compilation voodoo of ClojureScript’ are pretty good value propositions for lightweight runtimes.


The first point clearly didn't pan out. In reality, applications ended up bundling their own OSes along, so Clojure is stuck with antiquated VM on its ankle for no good reason.


> antiquated VM

I'm always flabbergasted about the ignorance against the JVM on this site. There is nothing out there that has seen similiar research and optimisations and has so good monitoring/debugging tools.


My position on the matter might be summarized as "No TCO, no copyable stackful coroutines, no buy." Separately from that a lot of my experience with the JVM involves things like trusting the JVM's claim that it spent 0 microseconds GCing a certain thread that was running code specifically designed to produce no garbage, then running the code in production for one day and losing tens of thousands of dollars, then learning that even if the JVM pauses your thread for 1ms for heap compaction it will still tell you it spent 0 micros on GC.


In other words, on the first day day of testing under realistic load conditions you learned how to measure GC pauses; then, presumably, you were able to improve performance.

It shouldn't have happened in a production environment, but setting up a way to lose tens of thousands of dollars if performance is bad, trusting unreasonable numbers, trying to produce no garbage, and requiring no GC pauses because margins are too tight are all your faults, not a shortcoming of your JVM.


Right, we improved the performance by moving the work out of the JVM.

It's my fault that if you trade US equities based on old information you lose money?

Edit: Actually, I'm still not sure how to correctly measure GC pauses, other than from inside the application.


It's your fault if you trade equities, or you do anything else in which milliseconds matter, with a software platform that is aggressively optimized for average performance and for server stability at the expense of hard real time guarantees and with a language that privileges enforcing constraints (both to ensure correctness and allow optimizations) over allowing the user explicit control. ("Your fault" at the organizational level, not personally, of course.)

Can you explain how Java was chosen for your trading application? Also, why were you unable to test it properly?


The org chose Scala for the trading application in the hope that developers and researchers could both use it, when previously they had been using very different languages. I thought this went mostly well. Scala enables you to use some shenanigans to pretend that a byte-array is an array of structs, and the 31 other threads of the application ran acceptably on the JVM. As an aside, my impression is that Java is somewhat common in the industry.

The application was tested in an environment that's a lot like production, except that we had substantially fewer cores than an exchange running the same number of matching engines, so the incoming ticks were a bit less bursty. That burstiness was important in this instance. A big reason for the differences between the production environment and the test environment was the cost of hardware.


I'm not sure I've ever heard of the JVM being used in trading level software before. I've gotten a broad but vague impression that custom applications are the norm.

No criticism, as I've only read others' accounts, but that sounds out of character.


Java or Clojure applications are "custom" too: custom code running in some kind of application server is more or less the same as custom code relying on libraries and frameworks in C or C++ or other leaner languages.

What's "out of character" (nice euphemism!) is adopting state of the art automatic garbage collection technology, writing in a language that makes actual garbage collection practically unavoidable, and then expecting that garbage collection doesn't happen or happens accordingly to arbitrary expectations. The JVM is clearly inappropriate technology if latency is important: it can perform well in common cases and with a reasonable level of tuning effort, but other options simply do not have the threat of GC pauses.


I gotta agree with HelloNurse here. When that kind of money is on the line you set up a production version that ingests real data at real speed, and produces the same output / response but doesn't actually spend / affect any money. You watch it for at least a week and see what happens.

I'm sorry to say it, but the loss of money here was not the JVMs fault. It was putting code with insufficient load testing in a production environment and crossing your fingers despite the fact that serious money was on the line.


A complete-fidelity production-like environment would be a couple orders of magnitude more expensive than the 1-day loss, so I disagree.


You don't need another complete set of servers: the new production servers, before beginning actual service, are available for brutal stress tests and all sorts of other experiments while the old servers running the old software are still in use and undisturbed. If the system is completely new and there is no old software on old servers, it's even easier.

Considering that software mishaps in securities trading can annihilate a company of any size, not only cost you as much as a bunch of servers, lack of testing appears very reckless.


> You don't need another complete set of servers: the new production servers, before beginning actual service, are available for brutal stress tests and all sorts of other experiments while the old servers running the old software are still in use and undisturbed. If the system is completely new and there is no old software on old servers, it's even easier.

We did not generally physically swap out servers each time we changed a line of code. Rather, we had two identical sets of servers, one for production and one for test. For test, we had some additional machines to run matching engines. If we wanted to make the test environment more closely match an actual venue, we would need many more machines that run matching engines. That still might not be sufficient to produce identical output timings in prod and test, though, because venues generally do not publish the sequence of all incoming ticks they receive during a day, the source code of their matching engine, what hardware they are using, their kernel version, and so on.

> Considering that software mishaps in securities trading can annihilate a company of any size, not only cost you as much as a bunch of servers, lack of testing appears very reckless.

It's strange that you replied to me suggesting we had no production-like test environment ~10 hours after I replied to you saying that this code did fine in our production-like test environment.


Do you know of a production ready VM with these properties that has a nice ecosystem?


I reckon part of the point is that a VM (in the JVM or CLR sense) might be a poor fit for the given use case, as might an automatic garbage collector, and thus that might dictate the use of languages which rely on neither (like C/C++ or Rust).


If you do a trivial amount of up-front work, C and Luajit have these properties, but Luajit often isn't a suitable replacement for the JVM and C isn't a VM. So no.


I did a JVM targeting language as a research project, and was involved into porting KVM to an obscure architecture. It's been a while of course (early 00s) so maybe I misremember something or there were huge strides of progress in JVM.

Can you jump over 64Kb in bytecode yet? Allocate a lexically scoped variable?

Of course, enormous amount of work has been put into making this particular pig fly at reasonable speed. Doesn't change the fact that most usability complaints about Clojure stem from reliance on JVM piggybacking.


so what's better and out there and reasonable stable?


Probably BeamVM, giving you better options for scaling, abstracting away machine borders and lightweight processes.


BeamVM is much worse than the JVM in the raw computing performances department.


As far as I know, people in the Erlang world tend to use NIFs for computation heavy tasks: http://erlang.org/doc/tutorial/nif.html However, I have not done that myself, so I cannot tell from experience how that works.


x86-64


market share is going to ARM


ARM is nice too, I concur


From the narrow scope of web development, JVM is fine. Kotlin is able to have every ergonomic feature you'd want from a language, and it gets closer to the performance of C in TechEmpower than many other languages (notably C#, Node, and Python, three of the most popular languages).

Tuning can be a pain, but scaling anything is hard.


It is clear that a lot of very important and practical work had been delivered with Java/JVM. But the original point was not about what is possible, but what is optimal.

Kotlin, from the little I've seen, appears to be largely a syntactic variation of Java, so there is certainly good impedance to JVM. Clojure is less so, and here lies the reason everyone curses about its backtraces.


Java is antiquated though, I don't see many new big libraries/frameworks being written in it. Java is too big and too entrenched in the enterprise to go away any time soon but in ten years the language will become legacy. For what's being used, languages like Go and to some extent Rust are eating Java's market share for new stuff being built. More and more universities are moving away from Java as teaching language too.

Clojure is in a bad spot here, it was a good idea at the time but not today. Clojure relies on Java for its libraries cause Clojure has almost no ecosystem. 95% of Clojure libs are Java wrappers.


Add the various JVM implementations, ART and the .NET variants into it as well.


HN has a very large bro/cowboy coder constituency. I suspect a large portion of the commenters here have not built anything non-trivial. You learn to filter it out but it's disappointing to learn that "engineers" are not much more rational and objective than the average Joe.


That "antiquated" VM is head and shoulders above any other extant runtime in compilation, GC and observability. Not only that, most of the major innovations in recent years in those areas seem to be happening there. Moving from the JVM to almost anything else -- be it Python, Go, Erlang or Node -- feels like being transported to medieval society. Don't get me wrong, some find it liberating as in, huh, I turns out I can survive without my smartphone, but calling the state-of-the-art technological leader "antiquated" is taking it way too far. If you care about performance, productivity and observability, there really aren't that many alternatives out there, and whatever adequate alternatives there are, they are even more "antiquated." And guess what? It turns out that a great many people care about those things.


It is state of the art in the same sense that America is the uncontested leader in WWE.


Is it? I've looked hard and couldn't find any runtime that isn't more than five years behind OpenJDK(+Graal) in those areas, but maybe I missed something. I'm not saying it's perfect, but there's nothing out there that's better in those areas.


Just to add to your point the only alternative that I would consider is .NET, and even then I do acknowledge that it is behind some JIT stuff, given that they have been favoring AOT / and single JIT scenarios.

Without Valhahla we will never get a Java variant of Unity, jMonkey Engine is a good as it gets and it has plenty of native code to overcome the areas where the JVM is found lacking.


You don't need Graal when you have Golang around the corner.


Golang's garbage collection system isn't just NOT state-of-the-art; it brings you back to the 1970s. A big pain point there.


We're not talking about the same thing. The innovative parts of Graal are not the AOT compiler.


What do you think about .NET?


It's probably the closest contender [1], but its non-Windows story is still a handicap, as is Microsoft's habit of making sweeping incompatible changes to their development platforms every 5-6 years.

[1]: A different philosophy that gives more control to the user in exchange for explicitness puts it ahead in some areas (structs) and behind in others (compilation quality, GC), but as a runtime engineer it's "technologically" behind (I work on OpenJDK so I'm biased, but I came to work on OpenJDK because that's where most interesting innovation in runtimes happens).


Microsoft doesn't make breaking changes. I have code that has survived three or four of your supposed cycles that still runs completely fine.


Microsoft did kill off or sunset some parts of the Framework. WCF, WWF, AppDomain, expression trees, dynamic, VB.

People who wrote WCF services in VB for a living are probably extremely disappointed.


Disappointed? Not at all! I worked with it many years ago.

Whilst WCF made some notable improvements over what existed within the ecosystem before it, it was still a sprawling, complex PITA and full of developer friction. Killing it off in this case was definitely the right thing to do :-)


Do you have anything to support that expression trees are being sunset? I haven't seen anything of the sort, unless you are talking about a deprecated library that has been replaced. But System.Linq.Expressions is still around.


I've also seen nothing about DLR being removed


.NET Core's cross-platform tools and ecosystem is wonderful. I'm working on a small team and the devs are using Windows, Mac, and Linux. CI builds are a mix of Windows and Linux, deploying to Linux.

I think their biggest weakness now is branding (.NET vs .NET Core). This will be fixed with .NET 5, which merges the two and will make all flavors of .NET cross-platform.


I guess this boils down the difference between what I call "l'art pour l'art" software engineering vs production-ready code providing a business functionality. The amount of hello world computing on HN is out of the roof, it seems nobody really cares about software lifetime or just never run into a situation when observability is a must. I think the reason for this that most businesses being ok with software being ridiculously expensive and shitty quality. The few places where software matters use Java/C++ because that works at a massive scale and these languages bring observability to the table that is required to maintain a service.


But that's besides the point, right? My question is about what Janet's niche is -- or are you arguing that we need a native Lisp because Clojure is only available on the JVM?


we'll see a renewed interest in VMs once diverse CPU architecture becomes more common. ARM and risc-V servers and laptops are coming.

right now if you release binary software for linux, windows and mac, you basically have 3 versions to distribute.

that will turn into 9 versions per release. that's a build and release nightmare.


Please don't use code blocks for quotes. It makes it very hard to read text on mobile, narrow viewports or via screen readers.


Sorry, I didn't even realize that it was a code block when I wrote the post.


It’s hard to read the bullet points on mobile — can you remove the indentation so it is formatted normally.

Other than that, good points.


I feel like a better description of how closure feels as a language would be something like “a functional language where everything is data and that data is flexible”. In particular the difference from a typical strongly typed functional language where basically everything is data you can dig into is that the data representation in closure is more extensible and uniform (because the types are implicit and not enforced)


In that respect, Janet doesn't really bring anything very new. Its just an alternative to Lua. Truth is, Lua had pretty few alternative as it stands. When you need something small, fast, to embedd. So Janet is a competitor there.


It looks like you may have inadvertently used the "block quote" ML in your whitespace. It renders terribly on mobile.


Yet Clojure supports a subset of CLOS, so not so much overrated after all.


Clojure was never against any of the ideas in OO, just that they should be accessible individually if or when they make sense. Traditional OO is very “all in”, while Clojure is very a-la-carte use the bits that make sense for the problem you are trying to solve. Clojure also encourages a data-over-objects and a functional-first approach, but when you need the OO features, they’re there and you should use them.


Even Smalltalk supports the other way around, although very few are aware of it, because they never bother to learn it.

You can do LINQ style coding in Smalltalk-80, and modern Smalltalks like Pharo do support traits as well.

So the point being that all major languages embrace both paradigms.

"Traditional OOP" basically boils down to Java until version 8 or C# until version 3, and very little else, but unfortunately most teachers fail at giving overviews of programming languages thorough computing history, and many just learn what they can reach out for.


Well, yes, I should have said “popular OO” instead of traditional. I did mean the Java’s and C#’s, Ruby’s and Python’s of the world, not the Smalltalks.

> So the point being that all major languages embrace both paradigms.

Sure, nowadays, most languages are very much multi-paradigm, but typically there is a dominant paradigm and the others are somewhat second-class. For example, in recent years, Java has gained a lot of functional programming feature too, but its not in the same ballpark as Haskell.


Just to clarify, CLOS is very a-la-carte too.


I'd love to hear more from the author (can someone interview him for a podcast please? :-). He also has another programming language called Fennel [1] which compiles a lisp like language to Lua.

Both Fennel and Janet seem to orbit around Clojure ideas.... interestingly one of Fennel's main contributors [2] is also the author of Clojure's most popular build system, Leiningen.

1: https://fennel-lang.org/

2: https://technomancy.us/186


I kept thinking that in the ‘small scripting Lisp’ category, a startup-time/performance comparison with Fennel would be apropos, as the latter is blazingly fast and thus well suited for scripting in interactive contexts.


    janet:1:> (cons 1 2)
    compile error: unknown symbol cons on line 1, column 1 while compiling repl
    janet:2:> (list 1 2)
    compile error: unknown symbol list on line 2, column 1 while compiling repl
    janet:3:> (vector 1 2)
    compile error: unknown symbol vector on line 3, column 1 while compiling repl
I certainly need to learn the basic idioms of this dialect; it looks nothing like Common Lisp under the hood.


I found this interesting so looked it up:

    Janet 1.9.1-4ae3722 Copyright (C) 2017-2019 Calvin Rose
    janet:1:> (array 1 2 3)
    @[1 2 3]
    janet:2:> (tuple 1 2 3)
    (1 2 3)
    janet:3:> (rest (tuple 1 2 3))
    compile error: unknown symbol rest on line 3, column 1 while compiling repl
    janet:5:> (tail (tuple 1 2 3))
    compile error: unknown symbol tail on line 5, column 1 while compiling repl
    janet:6:> (car (tuple 1 2 3))
    compile error: unknown symbol car on line 6, column 1 while compiling repl
    janet:7:> (cdr (tuple 1 2 3))
    compile error: unknown symbol cdr on line 7, column 1 while compiling repl
    janet:8:> (def t (tuple 1 2 3))
    (1 2 3)
    janet:9:> (def a (array 1 2 3))
    @[1 2 3]
    janet:11:> (drop 1 t)
    (2 3)
    janet:12:> (drop 1 a)
    (2 3)
    janet:13:> (first t)
    1
    janet:14:> (first a)
    1


Is it really Lisp without cons?


I can understand it's a traditional name, but maybe we can appreciate that it's a new/stand-alone language and that modern/expressive function names are an improvement and a breath of fresh air.


More precisely, Lisp

... without cons,

without arbitrary precision integers, fixed precision integers, rationals and complex numbers,

without image-based development,

without the conditions-restart system,

without CLOS,

etc.

It's interesting to note, since Janet is a bytecode interpreter, that we've have had a full Common Lisp implementation for years (CLISP) that provides all the features above (plus many many others) with a download size (1.8MB) just slightly bigge than to Janet's.


Yes, it seems so. Try the REPL available at the website.


I wonder if his question was not rhetorical, as in: It is really Lisp if it doesn't have cons?


That question has been a source of multiple flamewars in the past that have only made the world worse as a result. I prefer not to go down that hole again.


Is it really Lisp without 'lists'?

https://janet-lang.org/api/index.html

that does not mention list in meaningful ways. It seems to prefer to work with other data structures (like arrays) - which is okay, but then I won't call it a List Processor dialect.


I would say no: without list processing it's not really a lisp. It's something that looks like a lisp, but isn't.


I don't understand why somebody downvoted you, I've seen a number of languages that look like Lisp, but are based on vectors or arrays, and that changes the language drastically. So I'm wondering, too. I was trying to find out whether Janet programs are lists or something else like arrays, but didn't find it in the docs.


This does change the language drastically - source code is represented as tuples usually, although all of the core data structures have literal forms. This means writing macros is still easy, although certain idioms like consing are usually replaced by splicing, which is like unquote-splicing in Common Lisp but more general.


as someone who dosen't know the theory what is the difference between list and array? consing and splicing?

is there a pratical difference in the way you write programs or is more of theory/implemetation detail?


A list refers to a singly-linked list, while tuples are implemented as immutable arrays. The former is flexible in that multiple lists can hare structure, and prepending to a list is an O(1) operation that does not change the original list (consing). This property allows all sorts of interesting data structures which at their core are simply lists of atoms and other lists.

Janet on the other hand just uses tuples, which are easier to pack densely into memory so usually have better cache performance on reads (clever lisp implementations can sometimes can make lists fit densely in memory, but usually they take about twice as much memory as a tuple).

splicing is the like the spread operator in JavaScript or the splat operator in Ruby. This turns out to be very useful in a language without cons for manipulating tuples in macros, even though it is not as efficient as a cons.


thank you!


What are the most prominent examples? I tend to think that vectors are a better basis for a practical language.


Is it a lisp without car cdr and cadaaaadmycateatsmiceadaddddr?

Just in case -

))))))))))))))))))))))))))))))))))))))


1) How is this different from other Lisps or Schemes?

2) When promoting a new programming language, always provide a nontrivial example. Couple of pages of code. Mandelbrot generator, desk calculator, notekeeping app, that sort of thing.


And the motivation for designing the new language. What problems does it solve better than existing languages? How does it promote the creation of maintainable programs? Etc, etc.


> What problems does it solve better than existing languages? How does it promote the creation of maintainable programs?

I broadly agree that widespread adoption probably requires answering some sort question like these.

But sometimes people create languages just because they are fun to create, and they think other people might enjoy using them.


Just taking into consideration that most engineering degrees have of some of compilers design course, there are thousands of programming languages born every year across the globe.

So it needs more than just "grammar + semantics + basic library" to actually be relevant.

However that is how many nowadays mainstream languages have started, so luck also plays a big role.


1) the startup time is fantastic https://github.com/janet-lang/janet/issues/324#issuecomment-...

Function call throughput is just behind the JIT’ed lisps, which is understandable for a bytecode runtime.


> How is this different from other Lisps or Schemes?

Janet has almost nothing in common with Lisp or Scheme, besides the parentheses and the presence of some form of macro system.


Lisp languages should be great for embedding into games to implement game logic, instead of more straightforward solutions such as embedding Lua. I've always wanted to explore this for my iOS side-project game, which now uses an event-based system inspired by ReactiveX, implemented in Lua. This works pretty well but is very hard to debug.

The main thing holding me back to try to replace it with something Lisp-like is that I have zero experience in Lisp programming. I know some FP theory (in fact the Lua-based event system lends some concepts from it), but when I try to reason about common subproblems I really have no idea how to map them to something like Lisp. For example how to efficiently implement a message bus with observers that can register themselves, using state-based functions to execute when certain events are received, scheduling events, async/await to suspend processing until some event is received, etc.

Does anyone have pointers to books or websites to learn 'real-world' LISP programming? In other words not yet another introduction that shows the same old forced examples that just show you how to evaluate an expression or use higher-order functions to solve artificial toy problems, but resources that teach you how to be productive in a Lisp language?


> but when I try to reason about common subproblems I really have no idea how to map them to something like Lisp. For example how to efficiently implement a message bus with observers that can register for certain events.

I think that's a badly posed question. The main benefits of using Lisp are its introspection and interactivity. You can use the REPL to inspect your message bus at runtime, you can modify parts of it as it runs, you can interactively debug errors as they happen, but none of that is directly related to "how to implement a message bus".

I think that is more of an ADS question that it is a Lisp question; an efficient message bus, no matter whether you implement it in Lisp or in C or in Haskell, needs some synchronization primitives from the programming language core that are likely backed by OS primitives, as well as some data structures that need to be cleverly designed to be fast. Neither of these is language-specific - only means of implementing and using those might be language-specific.

In other words: Lisp makes it possible to be more productive by means of dramatically reducing the time of feedback loop and by means of providing in-depth language introspection, but it doesn't solve the problem of actually needing to figure out how to design programs and data structures. That's why e.g. SICP is not a Scheme tutorial, despite using Scheme throughout the whole book.


This is a very classic Lisp answer in that "productivity" is considered in the pure abstract, and that the specific question of how to do a specific thing - the actual productive result - is uninteresting. Who would expect the classic book on Scheme to teach you how to write Scheme programs?


SICP isn't a book on Scheme. It is not a Scheme tutorial; it uses Scheme to teach programming. One can learn Scheme by following the book and inferring everything that is done in there, but the topic of the book isn't teaching Scheme, it's teaching programming.


Regarding productivity, I don't consider it abstract. I feel a concrete efficiency boost from the two things I mentioned, which are interactive and incremental development with a very short feedback loop (no time wasted while waiting for the toolchain to do its work) and the ability to introspect and debug the live system (since I do not need to bother with external debugger and tooling).

What exactly do you mean by "uninteresting"?


The original post complains of not knowing how to solve their particular problem in Lisp, and gives an example:

> how to efficiently implement a message bus with observers

You say:

> I think that's a badly posed question

But it isn't! It's the specific thing that person is trying to do and is having trouble with, the act of translating structures and designs they understand from other languages into the Lisp language.

This is the barrier to Lisp adoption. You can cite iterative development and live debugging all you want, but they only benefit after you've actually written a program, which is the barrier the potential users are struggling at.


> but they only benefit after you've actually written a program, which is the barrier the potential users are struggling at

No, you miss the point. There is little benefit in interactive and incremental development once a program is written. The very benefit that this such incrementality provides is noticeable during the process of writing the program, not after it; when a program is written, it's written, and from some point it doesn't really matter if you've done it by mutating an image-based programming language or by linking together compiled C objects.


Programs are never finished. Most work is incremental modification and debugging of existing systems. The "getting started" bit of going from a blank editor to something that starts to be useful is a small part of the process. But it's the very beginning that is a surprisingly hard barrier to cross. The starting to write a program in the first place. Which is where the OP has got stuck. And is dismissed as irrelevant?


Maybe I phrased that incorrectly indeed. It's not so much that I want to find a 'better way to implement a message bus', but more 'if I want to re-implement the message bus I already have, how can I use the features of a Lisp-like languate to improve the implementation'.

For the logic in my iOS game I started out with a straighforward imperative system based on objects and explicit state, which quickly devolved in a big mess of if-then-else-but-only-if spaghetti code that was impossible to maintain. I refactored that to a reactive system based on what I call an 'event graph' where each node performs some kind of filtering or processing. For example nodes like 'filter on event type', 'take first 4 events', 'complete when event X received', etc. Game logic is implemented by branching off (subscribing) to nodes, synchronizing stuff by means of attaching a temporary subgraph that waits for a 'completion event' etc. Like I said it's quite similar to ReactiveX but simpler.

The basic concepts behind this are sound, but my feeling is that I need way too much code in to implement something like this in Lua, compared to what I would expect in something like Lisp, which seems more naturally suited for this kind of task. I just find it hard to get started trying to map the solution I already have to a Lisp language, because most of the 'introduction to Lisp'-like resources are so focused on artificial examples that are mostly interesting from a computation-theoretic point of view.


For your particular example, it seems you could use a dataflow approach, like the one in Cells (K. Tilton)

(see tutorial: http://stefano.dissegna.me/cells-tutorial.html, and documentation: https://gitlab.common-lisp.net/cells/cells/-/tree/master/doc)

    (ql:quickload :cells)
    (defpackage :robot (:use :cl :cells))
    (in-package :robot)
Define a robot model (class) where command in an input, and velocity is defined by an update rule:

    (defmodel robot ()
      ((command :accessor command :initform (c-in nil))
       (velocity :initform (c? (case (command self)
                                (:left -10)
                                (:right 10)
                                (t 0))))))
Whenever command is modified, velocity is updated accordingly. You can add observers for slot changes:

    (defun log-change (what from to)
      (print `(:change ,what :from ,from :to ,to)
             *debug-io*))

    (defobserver velocity ((model robot) new old boundp)
      (when boundp
        (log-change `(velocity ,model) old new)))
Then, if you instanciate the model, and mutate the command slot:

    (let ((w (make-instance 'robot)))
      (setf (command w) :right)
      (setf (command w) :left)
      (setf (command w) nil))
The following is logged:

    (:CHANGE (VELOCITY #<ROBOT {1015D66783}>) :FROM 0 :TO 10) 
    (:CHANGE (VELOCITY #<ROBOT {1015D66783}>) :FROM 10 :TO -10) 
    (:CHANGE (VELOCITY #<ROBOT {1015D66783}>) :FROM -10 :TO 0) 
Anyway, the book Common Lisp Recipes (E. Weitz) is good for solving actual, pratical problems with Lisp.


In terms of the basic tools available to you, Lisp and Lua are remarkably similar. In both you will rely heavily on closures to get the job you mentioned done. Lua offers the table as its primary data structure, though this can also be used as an array. Lisp offers the list, though this can also be used as a table (associative container, whatever).

Where Lisp and Lua differ is that Lisps are infinitely more malleable as languages. You can more-or-less invent new syntax to assist in your problem domain. For example, if you find that constructing your 'event nodes' requires boilerplate in Lua, you can write Lisp macros that make things clear again. Or you might write a little macro DSL to make constructing your graphs easier.

I enjoy Lua, but I constantly pine for the flexibility of a Lisp when Lua's clunky syntax annoys me. I find Fennel is a good compromise between the two - especially if you already have Lua code that you want to port over.


If he's currently using a message bus in Lua, it's likely everything is happening in a single-threaded interpreter.


I liked the book Practical Common Lisp (PCL) very much, you can read it online for free on the authors website: http://www.gigamonkeys.com/book/

I suggest using portacle (a preconfigured emacs) as development environment during that course as this is the easiest way to get cracking in CL imho.

Although Janet's core library is different from CL, I think once you grok LISP, it's not hard to switch to another dialect: After working through ~half of PCL I abandoned it in favour of a learning-by-doing approach in ClojureCLR as I work mostly in .NET or Mono atm.


Great, I will check it out!


If you’re interested in Common Lisp, Practical Common Lisp is a good overview of the language [1]. Since you’ve done game programming, here’s a simple snake game I wrote a while back; you might find it useful to peruse the code [2]. You can find a couple of bigger libraries I wrote on my github if you’re interested in seeing more substantial code.

[1] http://www.gigamonkeys.com/book/

[2] https://github.com/SahilKang/cl-snake


A great article on compile-time computing (with CL): https://medium.com/@MartinCracauer/a-gentle-introduction-to-...

There are good references and snippets on the Cookbook: https://lispcookbook.github.io/cl-cookbook/

for libraries, see https://github.com/CodyReichert/awesome-cl (and see the lparallel or lfarm libraries, channels, cl-gserver, the built-in SBCL threading functions, etc)

Also note that CL is not purely functional, you can throw in FP or OOP or iterative code.


The real power of Lisp becomes apparent when you develop programs top-down with very general data structures with their own accessors and a liberal use of higher-order functions.

I still think that Winston & Horn's classic Lisp (3rd ed.) conveys these ideas very well. SICP is also very good at it, but it's based on Scheme.


>Does anyone have pointers to books or websites to learn 'real-world' LISP programming?

https://lisp-lang.org/learn/getting-started/

You can start here to set up your IDE and then read the "Practical Common Lisp" book.


Learn GNU Guile, it's a dialect of Scheme. AFAIK, Scheme is specifically intended for use in teaching programming. I spent a late night reading the manual and hacking out some toy programs, and the rest is history.

TLDR: Guile is very easy to learn


There is Nu which runs on top of the Objective-C runtime: https://programming-nu.github.io/ Relative easy to read code and has actually been used with some iOS games.


Not mentioned here: Hy (hylang.org), a Lisp that manipulates/converts to the Python AST and therefore is fully interoperable with the whole vast Python ecosystem.

Although the language is still somewhat short of 1.0, it has stopped changing (after a fairly dramatic turn circa 18) and I’ve been writing more Hy than straight Python for a while now. When I started using Streamlit (and then you can’t have a main.hy function that it will recognize, although you can import arbitrary Hy modules) I started writing straight Python again for my weekend project and boy, do I regret it. I’m seriously considering pausing everything to port to Hy.


I certainly agree that if you need the Python ecosystem, then Hy makes development much more enjoyable, at least it does for me. I look at Hy in the same way as Armed Bear Common Lisp: as a great tool is I need the Java ecosystem.


+1 for Hy. I recently did a project where I needed to embed a lisp. I actually considered Janet (since Schemes are a little too threadbare), but there was no binding for a non-C language. I might consider creating Rust or Nim binding myself later, but Hy proved to be immensely easy and satisfying to work with.


2020: a programming language is named Janet; a child is named X Æ A-12. How did we come to this.


It's not really something new. Ada (https://en.wikipedia.org/wiki/Ada_(programming_language) ) was created 40 years ago. Kids with crazy names is a long standing tradition, going back millenia :-)


Also Amos, Haskell, Idris, Joy, Miranda or Yorick. Arguably Dylan or Pascal.

I'm more confused by a name starting with a capital "J" not being associated with Java, as once was custom.

As for kid's names, definitely[1].

[1]: https://www.youtube.com/watch?v=Jg9w0YSMLHY


And don't forget Swift, named after the biggest philosopher of our time - Taylor Swift.


"associated with Java, as once was the custom". Like JavaScript. Yeah, right.


Julia too


You are right, just the timing and example is so striking :)


I came here looking for "dammit, Janet" but only found that sentiment. Culture is dead.


You mean that the language should've been called ‘XÆA-12’ instead?


No, it should've been called X Æ A-12. At least one of the normal letters (X or A) should be Cyrillic and at least one of the spaces should be actually a tab.


Eh, let me introduce you to U+2060 zero-width word joiner.


Thank you, I've never hear of it. Unicode is so much fun.


sounds like a diabolical 'password rule'.


Names starting with a ‘j’ should be reserved for languages targeting JVM.


In the fine tradition of Jcala, Jotlin, Jroovy and Jlojure.


Little known fact: Rich Hickey initially planned to target .Net CLR and JVM, so he wanted something that has "C", "L", "R", and "J" in the name, that's how Clojure got its name.


Well, I guess had he put JVM first to reflect reality, the ‘Jocular’ language would evoke rather different feelings.


Damn, I almost spilled my coffee reading this :-)


I'm sure Oracle would love that, but fuck Oracle.


Many people seem to be comparing Janet and Clojure here as if one had to choose to use only one or the other.

Most of my daily work is done in Clojure or ClojureScript and that will continue to be true. However, I am excited by what I've seen in Janet and I'm already thinking that I'd love to call this from bash scripts to do system tasks (devops for example) that don't need the JVM cranked up.

I'm choosing both :-)


Please tell me this was somehow inspired by “The Good Place”...


Yep.

> Janet is named after the almost omniscient and friendly artificial being in The Good Place.[0]

[0]https://github.com/janet-lang/janet#why-janet


I'm not really clear why I would use this and not one of the small embedded Schemes that have srfi-18 support.

Chibi and S7 are both "drop some C into your project" levels of trivial to get running, and reasonably zippy.

And there's an upgrade path by using the standard: you can move to Chicken, Gambit, Guile, Racket or Chez and keep your code.


I'm beginning to create a lisp webserver using sbcl for a personal project.

I find this interesting but i'd like to know if a webserver library exist for janet yet, and how does it perform compared to sbcl performance-wise (x2 to x5 does not bother me, anything beyond that would not be great).

I'd like to know if a slime/swank can be used with janet (although this is really not a requirement)

[edit] i just thought that creating a webserver would be a nice introduction to the language.

Also can you compile janet into wasm?


I'd offer an alternative to the one linked on the main page in Joy[1]. Quick and easy to get started with.

1: https://github.com/joy-framework/joy


It’s right on the bottom of the page my dude, just scroll down or C-f circlet


wow i stopped at "try it". thank you.


I like it, and I am considering using it as a scripting language.

It was clear and easy how to build it for my Windows 10 box, and it is relatively small.

I have always like the small Lisps like PicoLisp, but PicoLisp is not Windows friendly; it's very POSIX-dependent. I haven't had great experiences with Cygwin. PicoLisp works on WSL/WSL2 though.


Have you seen https://janet-shell.org ?


I had, but it is for unixes, and I am primarily on Windows. I have a Linux box, WSL2, and an iMac, but all of my paying work is on Windows. I like them all, and I like Janet a lot!


Janet is quite nice, and I was playing with it only the other day:

https://taoofmac.com/space/blog/2020/05/10/1300

However, nobody here seems to have mentioned Joker, which is a Go interpreter/linter for Clojure:

https://joker-lang.org/

I'm rather torn between Janet and Joker, but am considering using Joker instead (even though it is single-threaded and lacks SQLite support) because it's closer to Clojure syntax.


This sounds awesome!

I was about to try and extend a software-product with graalvm to extend it live in clojure.

But Janet sounds like a couple less hoops to jump through.

Also, I won't have to deal with Oracle at all :D which is really the main reason I haven't begun work on this project yet.


There is already a JANET in the computing place, please rename your project:

https://en.wikipedia.org/wiki/JANET

Next time, actually Google your proposed name before you use it.


How does this language compare to, say Scheme?


I looked at the first two libraries listed on the landing page, and they seem to be mostly implemented in C. (The same goes for the 3rd lib, but it's SQLite bindings, which makes sense.) This is a bit discouraging.


I have been looking at Nim, Go, and D lately, as a follow-on to Python. Good SQLite support is something I need for HashBackup (author).

Nim has a direct interface to SQLite's C API and a higher-level db_sqlite interface. The higher-level interface doesn't support prepared queries so Nim's SQLite performance on a small test is slower than Python using the sqlite3 API which automatically compiles and caches queries.

The Nim binding replaces all parameters, specified with ?, with quoted values. That means:

- you can't have a null-valued parameter

- numbers get inserted as text (I think)

- blobs are string quoted and get changed to text instead of using SQLite's X'(hex)' blob notation

All returned values from db_sqlite are strings. If you want integers, you have to parseInt() the column.

I do like that Nim's db_sqlite interface is very simple so that you can still use the lower-level sqlite interface with the same db connection. So at least I could cache prepared queries if I wanted, but I have to do the work.

Checking out Go's SQLite interface, there are some issues posted on the repo (mattn/go-sqlite3) about performance problems because Go uses cgo for SQLite. Haven't test that yet.

Since the SQLite module was listed on the front page of Janet's site, I decided to check it out. It, like all the other C code I looked at in the project, is rather beautiful, and that's not a compliment you see very often about C code, my own included.

The SQLite interface handles multiple types, including nulls, blobs, and numbers (everything is a float, I'm guessing because that's how Janet implements all numbers).

It appears to not cache prepared statements (that would probably be a big performance boost), but does allow parameter substitution by name.

If you want to see some really nice C code, check out this project!


So, I do a lot of string manipulation using regular expressions, and Janet doesn't support that, in favor of PEGs, which I've never heard of. Does anyone know of a "PEGs for RegEx addicts" resource or something similar? :-)

What an interesting project! I'm sorely tempted to examine it more closely for one of my side projects I've been working on in Clojure.


While I'm asking for help.. does anyone know of any crypto bindings? I've been working with libsodium lately, and there is a Clojure library that binds to it.


For those who would prefer a more traditional albeit possibly less “lightweight” alternative there’s always Embeddable Common Lisp.


Or, since Janet is a bytecode interpreter with a download size of <2MB, there's CLISP which also fits that criteria, while providing the full power of Common Lisp and a nice FFI with C.


Is it compiled or interpreted? If interpreted does it use existing interpreter? The description mentions Lua, does it use the Lua engine? Does it have JIT? If it is compiled is it based on LLVM?


I don't know much about Janet (all I've done is complain that create-fiber did not work the way the docs say it did), but I'm pretty sure it uses its own bytecode interpreter.


It doesn't "depend" on Lua at all, but you can definitely feel inspiration from Lua in the language. Has coroutines, tables, small and easy to embed as a design goal, has a LPEG-like PEG built-in. Really lovely if you can appreciate those things in Lua-land.


For what it's worth, the win x64 installer died on me at "creating desktop icons", not exactly the most confidence-inspiring start, but lets see again.


Wow a lot of care was put into making a complete out of the box readable library but not including too much fat. Looking forward to my weekend.


Wow, this is exactly what I've been looking for. Also, I'm not a C-programmer, but I think the source code is good-looking.


How does this compare with Carp?


Any tutorial about Janet?


The Janet documentation is sufficient to get going. It reads pretty much like a tutorial. At least if you have any lisp/scheme experience. https://janet-lang.org/docs/index.html

I downloaded a couple weeks ago. I really wanted to build an executable, but I couldn’t. It seemed all the docs on this aspect were out of date or my build was broken in this respect.


oh okay, freecodecamp course would be great :-D


Can't find the tutorial or language manual.


I see no tutorial either, but did you try clicking the prominent link near the top labeled “Documentation”? The language manual seems to be all there. Once you're inside the documentation, there is a hamburger menu exposing a comprehensive table of contents.


I am an idiot. Somehow I went to the API documentation and examples instead on the github site. Thanks.


Call me when you modernize parenthesis out. ;-)


Touchy subject. As expected, downvoted by the Brotherhood of Defenders of a Holy Round Bracket.


I don't use lisp languages, but one basic difference this has from other lisps is that functions accept multiple expressions, e.g.:

    (defn greet [firstname lastname]
      (def fullname (string firstname " " lastname))
      (string "Hello, " fullname))
In other lisps, you'd have the last expression nested inside a `let` block:

    (defn greet [firstname lastname]
      (let [fullname (string firstname " " lastname)]
        (string "Hello, " fullname)))
which makes the code hard to read and edit IMO. Languages like Haskell and OCaml suffer from a similar problem too.


> which makes the code hard to read and edit IMO. Languages like Haskell and OCaml suffer from a similar problem too.

That's interesting, I've always really loved the "return last expression in a block" syntax. (Ruby and Rust can be added to your list as well.) That syntax just reads really naturally to me.


I like that too, but both Rust and Ruby allow multiple expressions/statements before the last expression. In Haskell and OCaml IIUC you need to use things like `let x = ... in <expr>` or `<expr> where x = ...`, so you still have a single expression in function bodies.


Common Lisp:

    (defun greet (firstname lastname &aux fullname)
      (setf fullname (format nil "~a ~a" firstname lastname))
      (format nil "Hello, ~a" fullname))


In common lisp `defun` does not require using `let`.


There are two things at play here (1) the implicit body and (2) being able to add new bindings in an existing scope with "def".

CL has an implicit body, but does not allow defining new bindings outside of some dedicated forms (let, etc.). I really prefer having to write "let" forms instead of having new variables added inside the body.


in Racket you can do it either way, and perhaps these sorts of issues should just be solved with an auto code formatter?




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: