开发者
⚡️ Leverage Go superpowers with PureScript! Native speed w/ absolute type safety
When you combine the absolute safety and elegance of a purely functional language with the raw execution speed of a modern low-level language, it's like discovering that space and time are inextricably linked: a whole new dimension opens up to you. In my previous article , I introduced phpurs , a compiler backend that brings the absolute mathematical safety of PureScript to the 70% of the web that still runs on PHP. The goal was to prove that we do not have to sacrifice modern safety and ergonomics just because we target a runtime that is, at first glance, not the most natural. Today, we are looking at the exact opposite end of the spectrum. If PHP was about ultimate portability and legacy compatibility, what happens when we want pure, raw metal speed ? What happens when we want true multi-core concurrency, static native binaries, and a garbage collector designed for extreme high-throughput? Say hello to one of my recent projects: gopurs , a super-optimized PureScript-to-Go compiler. And let me spoil the end of the story right away: by combining the high-level semantic purity of PureScript with the raw execution force of modern Go, the final compiled code matches Chez Scheme speed on pure computational benchmarks. Yes, Chez Scheme, one of the absolute historical champions of functional AOT compilation (i.e., decades of compiler optimizations). This will be a shorter article in the series, but here is a quick version of the story... Breaking the AOT ceiling: how TAST changed everything Historically, compiling highly polymorphic functional languages (like PureScript or Haskell) to statically typed languages like Go has often been a nightmare. Older attempts often relied on mapping everything to Go's interface{} (or any ). It works, but it's a performance sacrifice. Every primitive value you assign to an interface{} is boxed and escapes to the heap , generating massive pressure on Go's Garbage Collector. To solve this, gopurs initially used a flat Value struct (a tagge
AI 资讯
Understanding Monads in TypeScript: A Practical Guide
You have read five blog posts about monads. Each one started with category theory, mentioned burritos, and left you more confused than before. Let's skip all of that. Here is the short version: a monad is a container that supports three operations. You already use two monads every day in TypeScript — Promise and Array . Once you see the pattern, every monad in @oofp/core will feel familiar. The Three Operations Every monad has three things: of(a) — Put a value into the container. map(f) — Transform the value inside, keep the container shape. chain(f) — Transform the value into a new container, then flatten. That's it. If a type supports these three operations and follows a few simple laws, it's a monad. Let's see this with types you already know. Promise — a monad you use daily // of — wrap a value const p = Promise . resolve ( 42 ); // map — transform the inside (then with a plain return) const doubled = p . then (( x ) => x * 2 ); // Promise<number> // chain — transform into a new Promise (then with an async return) const fetched = p . then (( id ) => fetch ( `/api/users/ ${ id } ` )); // Promise<Response> Promise.resolve is of . .then acts as both map and chain — JavaScript conflates them. When your callback returns a plain value, it's map . When it returns a Promise , it's chain . The runtime flattens the nested Promise<Promise<T>> into Promise<T> automatically. Array — another monad hiding in plain sight // of — wrap a value const arr = [ 42 ]; // map — transform each element const doubled = arr . map (( x ) => x * 2 ); // [84] // chain — transform each element into an array, then flatten const expanded = arr . flatMap (( x ) => [ x , x * 10 ]); // [42, 420] Array.of (or just [value] ) is of . .map is map . .flatMap is chain . Same pattern, different container. The insight is: chain is map followed by flatten . That's why it's also called flatMap or bind . You apply a function that returns a new container, then flatten the nested result. Maybe: Eliminating null
AI 资讯
Functional programming primitives in Javascript
Category theory often sounds like impenetrable academic jargon, but at its core, it is simply the mathematics of composition. In functional programming, we use its concepts to build modular, predictable, and bug-resistant code. JavaScript isn't a pure functional language like Haskell, but it has first-class functions and treats functions as values. This makes it entirely possible to implement category theory primitives. Here is how the theoretical concepts map to practical JavaScript. The Vocabulary of Category Theory Before jumping into code, let's define the fundamental pieces: Categories: A collection of objects and arrows (morphisms) between them. Objects: In programming, these are our types or data (e.g., String, Number, Boolean, Array). Morphisms: These are our pure functions that transform one type into another (e.g., a function that takes a String and returns a Number). The goal of functional programming primitives is to create safe wrappers (containers) around our data so we can compose these functions predictably, without side effects or unhandled errors. 1. Functors: The Mappables A Functor is any type that implements a map method. It is a container holding a value, and map allows you to apply a function to that value without pulling it out of the container. When you map over a Functor, it returns a new Functor of the same type, preserving the container's structure. The native JS Functor: You already use Functors every day. JavaScript Arrays are Functors. const numbers = [ 1 , 2 , 3 ]; // We apply a function to the values inside, and get a new Array back const doubled = numbers . map ( x => x * 2 ); // [2, 4, 6] Building a custom Functor: Let's build a simple Box container to see how this works for single values. const Box = x => ({ map : f => Box ( f ( x )), fold : f => f ( x ), // An escape hatch to get the value out inspect : () => `Box( ${ x } )` }); // Usage: const result = Box ( ' Functional Programming ' ) . map ( str => str . trim ()) . map ( str
AI 资讯
Dhall-to-Effect: Provably Safe Task Orchestration via Total Functional Configuration and Purely Functional Runtimes
ᓯᐅᓇᕐᑕᖅ — Inuktitut for "that which lies ahead; a purpose" 🗣️ On the Name Full disclosure: I named this repository at 2 AM, which is probably when most repository names are decided. Siunertaq comes from Kalaallisut (West Greenlandic), a polysynthetic language — the kind where a single word can encode an entire clause's worth of meaning through agglutination and incorporation. I'm a bit of a grammar nerd, and polysynthetic languages have always fascinated me precisely because of how much structure they pack into a single morphological unit. One word carries subject, object, tense, evidentiality, and mood all at once, with none of it ambiguous if you know the grammar. That felt like the right metaphor for what this project is trying to do: pack a build graph's topology, its norm constraints, and its effect ordering into a single type-checkable unit — where the structure does the work, not the runtime. The word itself means something like "that which lies ahead; a purpose" — which seemed fitting for a tool that reasons about what needs to happen before anything actually runs. 🧵 TL;DR What if your task orchestration system couldn't even represent an ill-ordered build? Not "it would fail at runtime" — but "the type system refuses to construct the value in the first place." That's the idea behind Siunertaq : a Scala 3 project that combines Dhall (a total, non-Turing-complete configuration language), Cats Effect (purely functional async runtime), and a BSD Quiver model (directed Banach space graph) to make inconsistent build topologies structurally non-representable . This post walks through the design — with analogies aimed squarely at the Typelevel community — and closes with some thoughts on what modern AI-assisted development actually looks like when you refuse to let the LLM take the easy path. 🤔 Why Yet Another Build/Orchestration Abstraction? Most task orchestrators model their dependency graph as a mutable Map[Task, List[Task]] or similar at runtime, then check for