Joy: A maximalist web framework for Lisp aficionados
joy.swlkr.com
joy.swlkr.com
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!
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.
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.
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
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!
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.
[1] https://gist.github.com/eigenhombre/ea1d2c6d2fcd5b5c9ca7e8df...
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.
Maybe spending too much time in JavaScript-land has jaded my view of memory consumption.
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.
[0] https://github.com/janet-lang/janet/issues/324#issuecomment-...
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.
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 `@`)
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.
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!
> 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 :(
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'll give this a try soon, it looks promising. Hopefully it hits a sweet spot!
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 ?
Can you provide more details / examples?
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.
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 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 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.
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!
Fennel was created prior to Janet, by the same person. :)
Really cool though!
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?
Svg also tends to work wherever Html works for similar reasons.
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.
Which should be fine for a lot of applications.
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.
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);
}It's hard to format code on phone, sorry about that.