Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Joy: A maximalist web framework for Lisp aficionados (swlkr.com)
209 points by memorable on June 7, 2022 | hide | past | favorite | 68 comments


What some people seems to forget is that doing something simple is simple in every lang with any framework.

Can we have a sample with allowed hosts, cors, auth, input validation, open api docs, request logging with true IP addresses, XSRF/CSRF attack protection and so on?


> Can we have a sample with allowed hosts, cors, auth, input validation, open api docs, request logging with true IP addresses, XSRF/CSRF attack protection and so on?

The 2000s called and want their "Pet Store" back!


An API first "Pet store"? ;)

It is pretty much industry standard within the C# and Java community to provide Open API docs. It also displays the flaws of the API designs at an early stage. Stops one from going live with something embarrassingly bad as the Hashicorp API:s for instance.


jokes aside, this is all pretty much required functionality for mature applications


I had not pushed a project public in a while and recently had to kick something off and I hadn't been testing fully to get things into open space. CORS/CSRF and really getting my head back in the space of SSL being properly handled took me way longer than I'm comfortable to admit. These are things that I consider myself fairly comfortable with but chops had atrophied and browser enforcement leaves less wiggle room for failure. Also static file management really tapped me in my nuts.

It's really easy to think you've got it all figured out when it's just running on your local machine or a private network or to quote Mike Tyson "Everyone has a plan until they get punched in the mouth."

This experience has reinforced for me how critical getting CI/CD is to the process of taking something from a sketch pad to something that no only ships but other people can work on. Not saying that has to be a huge production, but it's just as crucial as how to post a form or connect to a data source. Looking at you JS bros that think they can cobble together an ORM that won't be a massive chore.


Most of what you're asking for is described in the docs, with examples. If you're looking for an example project using Joy, I think janetdocs[0] may be a good example. Some Janet experience may be good before diving into that repo, because it imports Joy with `:prefix ""`.

Also keep in mind that 99% of this seems[1] to have been made by 1 guy (Sean Walker), I think in his free time.

[0]: https://github.com/swlkr/janetdocs

[1]: https://github.com/joy-framework/joy/graphs/contributors


Fair. But when I click the docs link on the page I get 89 lines...


Hmm I see, that link may be a bit unfortunate, as it only shows the introduction to the docs.

More pages are here: https://github.com/joy-framework/joy/tree/master/docs#readme

PS: I did a very minor contribution to Joy once, so minor that I actually forgot about it :) I don't use Joy myself at the moment as I'm using my own framework, but the Joy docs + source code helped me out a lot in figuring out how to do authentication, csrf, etc!


Why would someone use Joy on Janet over Reitit & Ring on top of Clojure?

I see Janet is small and embeddable. Does that mean faster startup time than a full JVM? It's nice that most of Janes the literals match Clojure, so aside from `def` inside a function (as opposed to `let`), it's natural for a Clojure user to read.


Maybe they don't want to tie themselves with the JVM, or have no experience with the Java ecosystem? Janet seems like a much simpler ecosystem. Also, for fun would be a great reason :P


I don't really care about startup time personally but the linked page mentions "very low memory usage" which is quite a contrast with Clojure which seems to use insane amounts of memory for simple things.


Do you have a reference for Clojure's 'insane amounts of memory' use?

Our product (https://kpow.io) is written in vanilla Clojure/Clojurescript, it will quite happily run with a 64MB heap for smaller workloads.

64Mb for a non-trivial full-stack enterprise-grade application with authentication enabled providing a rich UI and multiple asynchronous and/or scheduled background processes that monitor and manage Kafka resources. Sure we don't recommend running multi-cluster production workloads on a 64MB heap, but requiring further memory for real workloads has nothing to do with Clojure and we're a long way from 'insane' memory use, or doing simple things.


RE: Clojure startup times, the full Kpow web app starts in ~7s.


I wrote janetdocs with joy and it's been running for quite a while on a pretty small VPS. htop shows RES memory usage as ~16MB and SHR as ~6MB, so it's anecdotal, but there's at least one data point.


I'm not saying it is using a low amount of memory, but I just fired up a new Clojure repl and it seems to end up using 90MiB memory once up and running. Is that what you'd call "insane amounts of memory"?

Maybe spending too much time in JavaScript-land has jaded my view of memory consumption.


Janet, for comparison to that 90MiB will use something more like 16MiB. Using less than 20% of the overall memory footprint does make Clojure seem insane by comparison.


How does that scale? I'm not worried about 60mb of ram in 2022. Is there a single like-like comparison of a complex application?


> It's nice that most of Janes the literals match Clojure

Two things:

* Janet doesn't have a built-in set data structure literal. Need to use a library for that.

* Janet has mutable and immutable versions of arrays and hashmaps (the literals for the mutable variety are prefixed with `@`)


I was surprised to discover that Janet tuples and records (the immutable array and hashmap you've mentioned) aren't the Clojure-style immutable which is designed for cheap updates, but the more usual kind of immutable where if you want a copy with changed data, you have to make a copy with changed data.


Janet starts incredibly quickly. Here was a microbenchmark someone produced comparing various Lisps[0].

[0] https://github.com/janet-lang/janet/issues/324#issuecomment-...


> so aside from `def` inside a function (as opposed to `let`)

It looks like the examples on the linked page use let, so it definitely seems like it's available.

Fwiw, you can technically use def anywhere in Clojure, it's just not good practice to do so.


On my machine, a "hello world" program runs 100x faster on Janet compared to Clojure.[1]

[1] https://gist.github.com/eigenhombre/ea1d2c6d2fcd5b5c9ca7e8df...


Makes a lot of sense. Babashka will be faster than Clojure as well.

Where Clojure shines, is long-running processes. Try the same benchmark, but instead printing "hello world" 1000000x times and compare it, and you'll have a benchmark showing Clojure's strength rather than weakness.

In the end, benchmarks usually show what the author(s) wanted to show with the benchmark.


What about Babashka vs Janet? In terms of utility, libraries and startup times...


Hiccup syntax for HTML is such a joy to use. It maps perfectly onto HTML, it is a breeze to write, and I haven't encountered anything yet that it can't express. It is one of my favorite parts of Lisp.


Me too! I wrote this little site years ago to help explain what hiccup is: https://escherize.com/works/hiccup.space/


I'm curious about and interested in janet, definitely keeping an eye on it. The author's last language, fennel, is very very impressive to me as a project.

Its strict adherence to lua's runtime semantics repulses lisp nerds like me at first. But after actually using it in some heavy lua projects, it's incredible how clearly the author understood the practical day-to-day problems with lua and air-dropped in a solution to only and exactly those. It doesn't introduce any extra complexity, perf, or debugging concerns; it runs on any version of lua with no compromises. There are endless lisp purity quagmires to get bogged down in and it completely avoids every single one of them. From a language design standpoint I really think it's an incredible achievement.

I've never used janet and don't particularly have a reason to right now. But who knows maybe I'll find an excuse soon.


> There are endless lisp purity quagmires to get bogged down in and it completely avoids every single one of them

Which is odd, because purity has never been a defining characteristic of the Lisp family of languages. It's somewhat analogous to the disputes over what is and isn't a rogue-like game. It's more a matter of spirit than any particular feature. Although there are features that the overwhelming majority of Lisps have, like a syntax that is pretty much directly is a tree representation of the AST (modulo reader macros if available). Of course the very first Lisp is an exception thanks to M-expressions!


I wouldn't say it's that odd - Purity is one of the largest defining differences between Scheme and Common Lisp. To the point where the Lisp family of languages can be defined by whether it takes after one or the other by its level of purity.


I didn’t mean purity in the functional programming sense. Rather, I was using the general English sense of strictly adhering to some ideal. I claim that there is no such thing as the Lispiest Lisp.


I too really enjoy Fennel. I never want to write plain Lua again.

> It doesn't introduce any extra complexity, perf, or debugging concerns;

Well, in my experience line numbers in error messages still don't always line up :(


Not to be confused with Joy, the stack-oriented programming language:

https://en.wikipedia.org/wiki/Joy_(programming_language)


This is great timing. I just started learning Janet yesterday. I look forward to working with this framework.

On a related note, I like the use of katakana in the name of the framework, but シoy would be pronounced "shi-oy." It should be ジoy, which would be closer (ji-oy).


I too initially saw that character as the katakana シ, but now I think it is just a smiley. :-)


I've spent a fair amount over the last few years experimenting with CL, Clojure, Gerbil, and Racket for web development. I really enjoy all of them but I always inevitably find problems that I can't really overlook and end up sticking to python or go.

I'll give this a try soon, it looks promising. Hopefully it hits a sweet spot!


Wll, with CL, Gerbil and Racket I understand coming across road blockers (for me this means, missing libraries)

But for Clojure, this doesn't add up, Clojure is either hosted on the JVM or Javascript, how can you not find the library you need on one or the other ?


Common Lisp is also on the JVM, so that's not really a good point for Clojure.


What problems are those?

Can you provide more details / examples?


the programming language is janet, which looks like a lisp at a cursory glance. why might I choose this stack with janet for a web app versus, say, Common Lisp? For instance https://lispcookbook.github.io/cl-cookbook/web.html


Honestly, the easiest to approach for me was Racket's out of the box web libraries. I just never took it further than that. I've dabbled with Clojure was well with lein and luminous web framework. I just am so undecided on what Lisp I want to use for a web project. I'm so spoiled by things like Django and ASP .NET Core. I really do want to do something with Lisp beyond just simple hello world web projects just because web development is something I do often enough in my career so it makes sense to jump straight into it, but I'm not sure. Of course if I went for Common Lisp which compiler do I even go for? I liked Racket a lot because it was easy to get into due to DrRacket. I liked Clojure because it had two simple editors that are now defunct, so I'm kind of left wondering what to use if I go that route.

https://docs.racket-lang.org/web-server/index.html


You mentioned Net Core, so I wanted to share this.

I started a project using Giraffe recently and love it. It’s an F# web framework that runs as a middleware in a AspNetCore app. I found F# to give me a lot of nice functional features, while also leveraging all the Dotnet ecosystem and CLR knowledge gained from my day job.


For Common Lisp, just stick to SBCL or CCL: https://common-lisp.net/implementations


I mean why use anything for anything, since another thing already exists.

Look I like common lisp just fine and have used it for serious professional work and value it for its practical strength and longevity.

It is gnarly though, and a pain in the ass sometimes, and really pretty hard to onboard people into. It's huge, and the intense focus on backwards compatibility means there are huge swathes of the language you won't use, some that possible no one should use. If you've already put the time in to learn all that then yeah sure why would you use this. But most people interested in lisps haven't spent hundreds of hours trudging through common lisp already, so maybe this is more for them.


Is there a "common lisp - the good parts" style thing that a decent percentage of users agree on?

(I've done scheme and a few others with great enjoyment and repeatedly bounced off common lisp, so I'm very much aware of the extent to which "lisp users agreeing on things" is a substantial ask, but it seemed worth a try even so ;)


> I mean why use anything for anything, since another thing already exists.

My question was to figure out WHY this might be better than the solutions that already exist, which I think is a fair question. If you're always going after the new thing without it having known / expected advantages, aren't you setting yourself up for pain?


I've always thought Lisp-ish would be great for building web apps, but I have so little experience in Lisp-ish I really have no idea if it works.


This website is a good example of a lisp web app, I think it works :)


Back in the day, I wrote a lot of Web applications in Chicken Scheme. Some are still running today, although they are harder to maintain now (bitrot, eroding skills on my part, etc.). I enjoyed the experience, though there was a lot of rolling-your-own to get to a finished solution. Maybe that has changed in the past 15-ish years...


Well, HN is written in a lisp.


Writing web applications with Lisp is a joy, so this is aptly named.

I wrote one app with Uncommon Web framework on Common Lisp around 2009, with it you could similarly render the HTML from Lisp functions, and use continuations for multi part form submissions.

I have also used Fennel recently, and if Janet is anything as good, I'll try Joy on my next suitable project!


> I have also used Fennel recently, and if Janet is anything as good,...

Fennel was created prior to Janet, by the same person. :)


My one fear with a lisp-based framework would be code-injection stuff, unless janet has some way of freezing the source in production. You could do a lot of damage if you suddenly realize you can inject code via a request payload on some server running a lisp-based framework.

Really cool though!


Unless the code you've written uses eval and passes user-provided data to the eval function, there's no way to inject code. JavaScript also has an eval function that and code injection is not a worry at run-time for Node projects unless they're using eval() as well.


But where's the eval? Unlike certain other languages (cough Java python), reading quoted s-exps has no side-effect.


I’ve never thought about this, that’s interesting, would escaping or coercing params help with this?


Joy is beautiful to work with in that it's _just_ enough. The scaffolding is great, the CLI is handy, and the API is very agreeable. Joy is (almost) enough incentive to start a project with Janet (like Rails for Ruby) even if I didn't have other plans to do so. Janet is also very nice as an aside, I just personally don't care for lisps syntactically.


Are there some aria examples?

on edit: I ask because wondering about the syntax, and if it does actually handle it. Often aria support is part not done.

Also inline svg?


I haven’t tried aria but there aren’t any restrictions on attributes, they should work


Looked at the source. It just turns `{:some-attr 42}` into `some-attr="42"`, so aria and any custom attrs would work.

Svg also tends to work wherever Html works for similar reasons.


Aria attributes can be difficult, because of bad parsers in accessibility software.

You need to set the attribute, and often do so without a corresponding value.

That is, how do you do: `<element aria-someattr>`.

Because the bad parser won't acknowledge something like: `<element aria-someattr='true'>`. That'll be ignored.


Looks nice but am curious about how you connect to and use a database. Is there some middleware or library to do this?


The tutorial talks about doing this with SQLite, in a way that suggests that this is the only option.

Which should be fine for a lot of applications.


It looks like janet supports great interop with c, so in theory it isn't much of a stretch to connect to other databases. I had the same question and came across this: https://github.com/newhook/janet-mysql

I know this is probably not for PROD but its a lot of fun to look at a nice clean database driver. I've been working with mysql off and on for ~13 years and never have really looked at the source of a client.


...or Tsuoy as the Katakana reads!


Fantastic ! Now we only need html-parser lib for janet/joy


Some C# code to create a minimal web application:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseHttpsRedirection();

var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };

app.MapGet("/weatherforecast", () =>

{

    var forecast = Enumerable.Range(1, 5).Select(index =>

       new WeatherForecast
       (
           DateTime.Now.AddDays(index),
           Random.Shared.Next(-20, 55),
           summaries[Random.Shared.Next(summaries.Length)]
       ))
        .ToArray();
    return forecast;
})

.WithName("GetWeatherForecast");

app.Run();

internal record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)

{

  public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}


If you’re going to post irrelevant code, at least format it.


I posted it to illustrate what in most languages you can do trivial apps with a few lines of code, so Lisp does not hold an advantage here.

It's hard to format code on phone, sorry about that.




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

Search: