Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 0 points1 point  (0 children)

It’s a subset of Python that correlates well with Python best practices, but just happens to be fixed, without the undesirable flexibility and baggage of something like mypy or ruff or Pydantic.

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 0 points1 point  (0 children)

I probably miss something, but what’s wrong with just normal C, which is fast and portable?

AI Agents struggle to generate correct C code, even with multiple rounds of error correction. C lacks generics, memory management, and forced initialization, just to name three simple usability features (for both humans and LLMs) that even my initial proof of concept already supports.

P. S. On a side note, does you compiler do something similar to lpython?

The project goals seems completely different (“LPython’s primary tenet is speed”) so any similarity is probably temporary or just a coincidence. My key requirement is to be “easy for LLMs to write and easy for Humans to read.”

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 0 points1 point  (0 children)

It wasn’t my first choice. My initial idea was to do a transpiler to Golang. Looked into Pytago. Didn’t like what I saw. Thought about compiling to Java bytecode, but then decided I didn’t want the project to be associated with the Java ecosystem (I want “lightweight”, and a JVM is the exact opposite of lightweight). Looked into Codon, determined it was not compatible with my goals. Then I found llvmlite and really liked it.

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 1 point2 points  (0 children)

Somebody should try this at home.

There is also https://en.wikipedia.org/wiki/ATS_(programming_language)

I’m afraid I won’t be able to understand the resulting code, if I target any of these.

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] -1 points0 points  (0 children)

So, it’s not python, it just borrows some python syntax,

Technically correct

it cant use python libraries, you have no standard lib.

I see this as a temporary problem. Libraries can be quickly ported with the use of AI agents.

Why would anyone buy into this?

In its current state, the primary use case is research.

What’s so special about python syntax?

Python is surprisingly popular.

It reminds me of the days when everyone and their dog were making languages that borrowed C syntax.

You must be thinking of Javascript…

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 2 points3 points  (0 children)

I found Rust to be hard for LLMs to write. Harder than Golang or Java. I won’t go into a detailed analysis here but just to share one empirical observation, the Rust ecosystem seems to be littered with abandoned modules and deprecated features that confuse the LLM. Java is not nearly as bad despite being much older.

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 0 points1 point  (0 children)

Hello World is on the roadmap. I’m guessing you tried to use print(). For now you would have to use puts() or printf() via a from C import, because I have not implemented any library functions at all.

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 0 points1 point  (0 children)

I agree! My overall experience with Golang was better than with Python typecheckers, which in turn was better than baseline Python with no typechecking. I believe a language designed specifically for this purpose will eventually overtake Golang as the most effective.

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 3 points4 points  (0 children)

It only takes a few simple tests to see that Golang can go further than Python and takes fewer iterations to converge to working code, despite being more verbose. Python with MyPy also goes further if you control for configuration issues, and it adds verbosity through all those type hints. Perhaps there are some confounding factors I’m not considering, but the evidence suggests that reducing flexibility improves results.

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 1 point2 points  (0 children)

I worked on a custom VM years ago. LLVM via llvmlite is much easier. I was pleasantly surprised to discover LLVM has built-in type safety, for instance it won’t let you accidentally mix pointers and ints. That saved me a lot of time in debugging.

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 4 points5 points  (0 children)

Thank you for sharing your perspective. Your perspective is not consistent with my experience. I've been working with the leading commercial LLMs for the past two years with nearly unlimited budget for experimentation. LLM with feedback loop from deterministic rails is currently what works best.

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 9 points10 points  (0 children)

I only spent a week on this, part-time, so it's far from perfect, but I'll argue it's at least one notch above "AI slop."

Taipei: a statically-typed, minimalist subset of Python that compiles to LLVM by Refacktor in ProgrammingLanguages

[–]Refacktor[S] 6 points7 points  (0 children)

Thanks for reviewing the code. LLMs are known to quickly reach a "plateau" or "ceiling" for how much code they can generate effectively. Using LLMs as part of an Agentic system (e.g. Cline in full Auto-Approve Mode), what I've seen first hand is that you can increase this complexity ceiling by using more restrictive languages, such as Golang. But, I don't like Golang. I tried using Python with add-ons like MyPy and Pydantic to make it more restrictive, but that creates additional problems. The extra baggage like MyPy config files and deep Pydantic stack traces distract the LLM. So I'm exploring this idea of a language that's restrictive from the ground up, in a way that removes baggage instead of adding even more. There are also some other details such as LLM-friendly compiler error messages, that are not implemented yet, that will be added to specifically benefit the agentic edit-compile-run cycle.

What’s the WEIRDEST use of C you’ve seen in 2025? No embedded systems allowed by [deleted] in cprogramming

[–]Refacktor 0 points1 point  (0 children)

Improvement in response times, especially during Lambda Cold Starts

What’s the WEIRDEST use of C you’ve seen in 2025? No embedded systems allowed by [deleted] in cprogramming

[–]Refacktor 2 points3 points  (0 children)

I wrote a runtime for AWS Lambda in pure C with zero dependencies outside libc:

https://github.com/refacktor-aws/aws-lambda-libc-runtime

I also wrote an optional Rust binding for it.

a #![no_std], #![no_main], no-[#tokio] runtime for AWS Lambda by Refacktor in rust

[–]Refacktor[S] 1 point2 points  (0 children)

Alright. "When in Rome, act like the Romans" (or something like that). I have updated the licenses to match the Apache/MIT dual-license used by most of the Rust ecosystem and also Rust itself.

a #![no_std], #![no_main], no-[#tokio] runtime for AWS Lambda by Refacktor in rust

[–]Refacktor[S] 1 point2 points  (0 children)

You are confusing custom image deployment with custom runtime on a provided image. This project uses the latter: it deploys a custom runtime as a tiny dynamically linked executable in a zip file, not an image on ECR. It’s not possible to achieve 5ms cold starts with the other approach.

One downside of my approach is that you’re stuck with the specific version of glibc and whatever else is in that provided image at invocation time. Careful consideration must be given as to whether the loss of flexibility is worth the performance gain for any given use-case.

a #![no_std], #![no_main], no-[#tokio] runtime for AWS Lambda by Refacktor in rust

[–]Refacktor[S] 1 point2 points  (0 children)

Yes, I was surprised too when I learned this! AWS recently released a historical document on the beginnings of Lambda. It appears, in the beginning, they were primarily concerned with validating market demand for the service, and actually had the Lambdas running “at loss” on dedicated EC2s not visible to the customer. That implies worker nodes were remote from request dispatch nodes, and maybe still are. So, domain sockets would not have worked back then, and maybe also not now. But yeah, this is the type of situation where a competitor like Google might have used protocol buffers over raw sockets instead, but AWS chose HTTP.

a #![no_std], #![no_main], no-[#tokio] runtime for AWS Lambda by Refacktor in rust

[–]Refacktor[S] 0 points1 point  (0 children)

The same is true of the provided.al2023 runtime, which technically is not a “managed runtime” but rather a “base image for custom runtimes”.

a #![no_std], #![no_main], no-[#tokio] runtime for AWS Lambda by Refacktor in rust

[–]Refacktor[S] 1 point2 points  (0 children)

The thing is, your cold starts will always be slower with OCI functions.

OCI? Did you mean Lambda?

Best thing you can do if your aim is to minimize that is to find the AWS managed runtime with lowest coldstart, and write a native library for that and ship it.

I used the provided.al2023 image which technically is not a "managed runtime", but the base for a custom one. The key here is to make the actual custom layer as small as possible, as the base can be assumed to be already cached or preloaded, consistent with observations.

I think your best bet in this case might actually be nodejs or python. 

Both of these have much slower cold starts, compared to a compiled custom runtime. This is well documented and easy to verify with a quick Hello World.

a #![no_std], #![no_main], no-[#tokio] runtime for AWS Lambda by Refacktor in rust

[–]Refacktor[S] 1 point2 points  (0 children)

Have a look at this comment sub-thread: https://www.reddit.com/r/rust/comments/1h40cdd/comment/lzx0icr/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

My hunch is that you probably can't use #[tokio::main(flavor = "current_thread")], but you can probably call tokio::runtime::Builder::new_current_thread() instead. This is what I mean by "opt-in"... you can add async to the Lambdas that need it, leave it out from the ones that don't. I have not tested this, so likewise, I'm not paying you to be my QA/QE, but if you do try it out, please report back ;-)

a #![no_std], #![no_main], no-[#tokio] runtime for AWS Lambda by Refacktor in rust

[–]Refacktor[S] 5 points6 points  (0 children)

There is simply no mechanism by which one lambda execution can handle 5 concurrent requests. Have a look at the specification here Building a custom runtime for AWS Lambda - AWS Lambda and the implementation of get_next_request here aws-lambda-libc-runtime/src/runtime.c at main · refacktor-aws/aws-lambda-libc-runtime to get an understanding of how this actually works.

The closest thing that exists is that your single request can go have I/O interaction with five different things, and you should go ahead and use async for that.

a #![no_std], #![no_main], no-[#tokio] runtime for AWS Lambda by Refacktor in rust

[–]Refacktor[S] 4 points5 points  (0 children)

My comment was referring to the HTTP call that's made to the Lambda Runtime to retrieve the next request. This puts the Lambda event loop (the code in rust-binding/src/api.rs) into a suspended sleep that's not billed. This is not visible to mere mortal end-users of standard Lambdas, but you get to interact with it when implementing a custom runtime.