MLC Chat is a great iPhone app for running models (it's on Android too) and currently ships with Llama 3.2 3B Instruct - not the version Meta released today, its a quantized version of their previous release.
I wouldn't be surprised to see it add the new ones shortly, it's quite actively maintained.
I write Clojure for food, and Common Lisp for fun. One reason for the latter is CL's speed -- awhile back I compared a bit of (non-optimized) Clojure code I wrote for a blog post with a rough equivalent in CL, and was stunned that the Common Lisp code ran about 10x faster. This made me curious as to how fast it could be made if I really tried, and was able to get nearly 30x more[1] by optimizing it.
Clojure is definitely fast enough for everything I've done professionally for six years. But Common Lisp, while having plenty of rough edges, intrigues on the basis of performance alone. (This is on SBCL -- I have yet to play with a commercial implementation.)
This is racket's [rhombus], which might be an interesting second link here; it's a scheme, underneath, with the full power of Racket libs available.
[`shrubbery`], the replacement for s-exprs, is pretty interesting, expanding s-expr simply with grouping, and then a separate infix-pass on top. I've been playing with using it as the basis for a separate language; it's in an interesting place in the AST space, especially given the forethought put into macros.
Note that DuckDB can ingest SQLite tables. If you need OLAP on your SQLite system today instead of whenever this is stable, consider pairing up DuckDB. In the simplest case if you don’t need amazing latency and have small data (<1gb), I would copy the whole table from SQLite into an in-memory or temp file DuckDB, do your OLAP queries, then throw it away. For larger datasets, you can incrementally replicate the SQLite table into DuckDB by adding a logical clock column or something to the SQLite table, and then copy rows where logical_clock>last_duckdb_change before running your next DuckDB query.
We’re currently doing a bake-off between a few databases for small datasets (<10m rows) with dynamic schemas, and have pretty comparable read latencies between DuckDB and SQLite for our workflow, so you might be able to get away with switching wholesale from SQLite to DuckDB if you don’t do a lot of update.
"A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system."
– John Gall (1975) Systemantics: How Systems Really Work and How They Fail
You could theoretically use Datalog anywhere you're currently using a SELECT SQL statement - it's a declarative way to produce a set of tuples given some existing sets of tuples.
But, Datalog is much better at deductive reasoning compared to SQL. There's a great interactive tutorial here: https://percival.ink/. To see how Datalog and SQL can be equivalent, I have a build of percival.ink that transforms simpler queries to SQLite: https://percival.jake.tl/
That post describes building a recipe engine that can tell you, given a bunch of recipes + the current contents of your bar cart, what drinks you can make, as well as giving you top ingredients you could buy that would allow you to make new drinks. It's quite a readable walkthrough of a very practical application of Datalog/Souffle in a place where SQL would certainly struggle.
Luckily there are faster and smaller alternatives in Rust for the ElasticSearch - Toshi[1], Meili[2] and Sonic[3]. In the age of Rust there is no need to use JVMs overhead.
I get the same gut reaction as you, but I tried reformatting the code a little bit and it's not _that_ terrible.
From the very little APL I've learnt, I know that operators always have either one parameter (named ω) or two (named α and ω), and their inputs and outputs are always arrays.
If you need to write a lot of such functions, it makes sense to define some macros to help you:
#define V1(f) A f(w)A w; // create one-arg function
#define V2(f) A f(a,w)A a,w; // create two-arg function
#define DO(n,x) {I i=0,_n=(n);for(;i<_n;++i){x;}}
iota is the APL equivalent of Python's range function:
V1(iota) { // Define one-arg function iota.
I n = *w->p; // Get the value of the ω argument.
A z = ga(0, 1, &n); // Allocate output array.
DO(n, z->p[i] = i); // Assign increasing integers.
R z; // Return the result.
}
The plus function adds two arrays:
V2(plus) { // Define two-arg function plus.
I r = w->r, // Get the rank of ω.
*d = w->d, // Get a pointer to ω's data.
n = tr(r,d); // Get tne size of ω's data.
A z = ga(0, r, d); // Allocate output array.
DO(n, z->p[i] = a->p[i] + w->p[i]);
// Add corresponding values of α and ω
R z; // Return the result.
}
Personally I cannot tolerate the lack of whitespace, but the APL guys are known to like to see their entire programs in one screenful. I can understand that some people like to write code like this.
I dig logic programming, but I wish Racklog had better (or any?) constructs for storage. Racket Datalog can serialize a theory[0], but that still means you'll have to write it in its entirety to the disk after any accumulation of facts.
In my opinion the SQL language is inferior to Prolog, but in most SQL systems incremental update isn't a problem.
Here's a comparison of SQLite with xml-files-in-a-zip-file[1] (and the zip-based file format has similar problems as serializing a Datalog theory):
> It is difficult to update individual entries in a ZIP archive. It is especially difficult to update individual entries in a ZIP archive in a way that does not destroy the entire document if the computer loses power and/or crashes in the middle of the update. It is not impossible to do this, but it is sufficiently difficult that nobody actually does it. Instead, whenever the user selects "File/Save", the entire ZIP archive is rewritten. Hence, "File/Save" takes longer than it ought, especially on older hardware. Newer machines are faster, but it is still bothersome that changing a single character in a 50 megabyte presentation causes one to burn through 50 megabytes of the finite write life on the SSD.
I wouldn't be surprised to see it add the new ones shortly, it's quite actively maintained.
https://apps.apple.com/us/app/mlc-chat/id6448482937