{"@attributes":{"version":"2.0"},"channel":{"title":"DEV Community: Vee Satayamas","description":"The latest articles on DEV Community by Vee Satayamas (@veer66).","link":"https:\/\/dev.to\/veer66","image":{"url":"https:\/\/media2.dev.to\/dynamic\/image\/width=90,height=90,fit=cover,gravity=auto,format=auto\/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F39553%2Fc5b3f02b-22ae-403a-8cb4-13d0b3b06774.jpg","title":"DEV Community: Vee Satayamas","link":"https:\/\/dev.to\/veer66"},"language":"en","item":[{"title":"Maybe not microservice: The Case for Pipes, Pipelines, and Functional Isolation","pubDate":"Sun, 19 Jul 2026 09:20:37 +0000","link":"https:\/\/dev.to\/veer66\/maybe-not-microservice-the-case-for-pipes-pipelines-and-functional-isolation-4hg5","guid":"https:\/\/dev.to\/veer66\/maybe-not-microservice-the-case-for-pipes-pipelines-and-functional-isolation-4hg5","description":"<h2>\n  \n  \n  1. Subsystem Decomposition\n<\/h2>\n\n<h3>\n  \n  \n  1.1 The Decomposition Problem\n<\/h3>\n\n<p>A subsystem decomposes a codebase into smaller, cohesive units. Two primary axes of decomposition exist:<\/p>\n\n<ul>\n<li>\n<strong>Technical axis<\/strong>: grouping by component type (controller, service, model, view)<\/li>\n<li>\n<strong>Functional axis<\/strong>: grouping by business capability (cataloguing, circulation, etc.)<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  1.2 Tension Between Framework Prescriptions and Decomposition Strategy\n<\/h3>\n\n<p>Organizing top-level subsystems functionally may create friction with frameworks that prescribe a technical-first structure. Concrete examples:<\/p>\n\n<ul>\n<li>\n<strong>Rails<\/strong> enforces model, view, and controller directories at the root level, making functional decomposition awkward without additional mechanisms like Rails Engines<\/li>\n<li>\n<strong>Sinatra<\/strong> (a microframework) imposes minimal structure, leaving architectural decisions entirely to the team<\/li>\n<\/ul>\n\n<p>Frameworks with rigid prescriptions constrain architectural choices. Frameworks with no structure shift the entire burden onto the team with no guidance. This second approach might be fine for teams that know what they are doing and how to shape the architecture properly. Not everyone needs guidance from the framework.<\/p>\n\n<h3>\n  \n  \n  1.3 Contexts as a Middle Ground\n<\/h3>\n\n<p>Phoenix provides contexts as a compromise:<\/p>\n\n<ul>\n<li>Explicit, guideline-oriented subsystems that enable functional decomposition without rigid enforcement<\/li>\n<li>Contexts define functional boundaries while allowing technical organization to remain nested within them<\/li>\n<li>Functional blocks may later evolve into microservices, but this is optional<\/li>\n<li>The same decomposition serves equally well in a modular monolith or a distributed architecture<\/li>\n<li>The choice depends on team needs, scaling requirements, and operational maturity, not on the decomposition strategy itself<\/li>\n<\/ul>\n\n<h2>\n  \n  \n  2. Pipeline Topology and Data Flow\n<\/h2>\n\n<h3>\n  \n  \n  2.1 The Unix Pipeline Model\n<\/h3>\n\n<p>Unix pipelines model data flow through a single stream connecting stdout to stdin. This forms a linear chain where each stage's output becomes the next stage's input. Key characteristics:<\/p>\n\n<ul>\n<li>Each stage has exactly one input and one output<\/li>\n<li>Cognitive overhead is minimized because the topology is trivial to trace<\/li>\n<li>The linear, single-stream characteristic is not mandatory for a pipeline, but it reduces complexity significantly<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  2.2 Arbitrary DAG Topologies\n<\/h3>\n\n<p>Orchestrators like Airflow allow arbitrary DAG topologies with fan-in and fan-out edges. Tradeoffs:<\/p>\n\n<ul>\n<li>Powerful for expressing complex dependencies<\/li>\n<li>DAGs with dense interconnections tend to become hard to read even with visual rendering<\/li>\n<li>Complex function-call topologies with many parameters outside Airflow and Unix pipelines also produce unreadable code<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  2.3 Byte Streams and Opaque Containers\n<\/h3>\n\n<p>Unix-like pipelines connect programs by passing data through unidirectional byte-streams:<\/p>\n\n<ul>\n<li>Programs at each end agree on a structure such as JSON, CSV, or tar archives<\/li>\n<li>The pipe mechanism itself transports only raw bytes<\/li>\n<li>This has a direct analogue in dynamically typed languages: in Lisp and Clojure, collections (lists, maps, vectors) serve as opaque containers that can hold almost arbitrary data<\/li>\n<li>The consumer interprets the contents rather than the container dictating them<\/li>\n<\/ul>\n\n<h2>\n  \n  \n  3. Typing Heterogeneous Pipeline Data\n<\/h2>\n\n<h3>\n  \n  \n  3.1 The Problem\n<\/h3>\n\n<p>Strict type systems introduce complications when handling heterogeneous data flowing through a pipeline where each stage transforms the shape slightly.<\/p>\n\n<h3>\n  \n  \n  3.2 Failed Approaches\n<\/h3>\n\n<p><strong>Single large type with many optional fields:<\/strong><\/p>\n\n<ul>\n<li>Creates dependencies between all pipeline steps<\/li>\n<li>Loses the ability to reject illegal data<\/li>\n<li>Makes reuse difficult<\/li>\n<li>Changes to one field propagate everywhere<\/li>\n<\/ul>\n\n<p><strong>Many separate types for each step:<\/strong><\/p>\n\n<ul>\n<li>Exhaustive and adds noise to the program<\/li>\n<li>Structures may not be mutually exclusive yet are treated as such<\/li>\n<li>Maintenance burden grows with every new stage<\/li>\n<\/ul>\n\n<p>Both approaches fail because each pipeline stage depends on more than it needs.<\/p>\n\n<h3>\n  \n  \n  3.3 Partial Fixes From Functional Programming\n<\/h3>\n\n<p>Two techniques alleviate but do not fully resolve the problem:<\/p>\n\n<ul>\n<li>\n<strong>Functional record update<\/strong>: enables creating modified copies without mutation, reducing coupling related to state changes<\/li>\n<li>\n<strong>Sum types<\/strong>: restore the ability to discriminate valid from invalid data and support exhaustiveness checking<\/li>\n<\/ul>\n\n<p>Remaining limitation: every step that pattern-matches on a sum type must know about all variants. Adding a new case still propagates changes through the pipeline.<\/p>\n\n<h3>\n  \n  \n  3.4 Structural Type Compatibility\n<\/h3>\n\n<p>Structural type compatibility offers a complementary solution:<\/p>\n\n<ul>\n<li>Independently defined types become compatible based on shape alone without requiring any inheritance relationship<\/li>\n<li>A consumer can specify only the subset of fields it needs via a structural interface<\/li>\n<li>Each step depends on a minimal projection of the data rather than the full type<\/li>\n<li>This decouples pipeline stages more effectively than either naive approach or sum types alone<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  3.5 Python Implementation\n<\/h3>\n\n<p>Python implements several of these patterns:<\/p>\n\n<ul>\n<li>\n<strong><code>dataclasses.replace()<\/code><\/strong>: supports immutable record updates<\/li>\n<li>\n<strong>The <code>|<\/code> union operator<\/strong>: simplifies union type expressions<\/li>\n<li>\n<strong>Protocol classes<\/strong>: enable structural subtyping, allowing independent types to satisfy contracts based on method and attribute signatures<\/li>\n<li>\n<strong>Tagged unions<\/strong>: modeled using <code>Literal<\/code> discriminator fields on dataclasses or TypedDicts<\/li>\n<li>\n<strong><code>typing.assert_never<\/code><\/strong> with <code>mypy<\/code>: enforces exhaustiveness checking on pattern matching or if chains, providing compile-time guarantees similar to sum types in functional languages<\/li>\n<\/ul>\n\n<p>Combined approach:<\/p>\n\n<ul>\n<li>Protocols decouple steps through structural conformance<\/li>\n<li>Tagged unions enable variant discrimination with exhaustiveness checking<\/li>\n<li>Functional record updates reduce mutation-related coupling<\/li>\n<\/ul>\n\n<h2>\n  \n  \n  4. Microservices, Processes, and Isolation Patterns\n<\/h2>\n\n<h3>\n  \n  \n  4.1 The Shared Principle: Isolated State by Default\n<\/h3>\n\n<p>A microservice and a Unix process share architectural similarities:<\/p>\n\n<ul>\n<li>\n<strong>Microservices<\/strong>: in well-designed architectures, a service does not share variables or databases with other services. Communication happens through well-defined interfaces. This is a best practice, not a hard technical constraint. <\/li>\n<li>\n<strong>Unix processes<\/strong>: each process has its own virtual address space and does not share memory directly with other processes. Explicit sharing is possible through mechanisms such as <code>shm_open<\/code> (POSIX shared memory) or <code>mmap<\/code>.<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  4.2 Historical Lineage\n<\/h3>\n\n<p>Microservices on GNU\/Linux are literally processes communicating via HTTP over TCP\/IP. The historical chain:<\/p>\n\n<ul>\n<li>\n<strong>TCP\/IP<\/strong>: first implemented in 4.2BSD Unix in 1983<\/li>\n<li>\n<strong>HTTP<\/strong>: developed at CERN in 1989 to 1990, building upon these networking foundations<\/li>\n<li>\n<strong>Tim Berners-Lee<\/strong> wrote the first HTTP server and web browser on a NeXT workstation running NeXTSTEP in fall 1990<\/li>\n<li>\n<strong>NeXTSTEP<\/strong> was heavily influenced by BSD Unix<\/li>\n<li>\n<strong>GNU\/Linux<\/strong> copied many initial ideas from Unix while remaining free and open<\/li>\n<\/ul>\n\n<p>The modern distributed system traces an unbroken lineage back to Unix.<\/p>\n\n<h3>\n  \n  \n  4.3 Pipes as an Alternative to Microservices\n<\/h3>\n\n<p>Piping via stdin-stdout chains is another mode of interprocess communication:<\/p>\n\n<ul>\n<li>Not as powerful or generic as TCP\/IP or HTTP<\/li>\n<li>Easy to use and reason about<\/li>\n<li>Naturally fits data pipelines<\/li>\n<li>A data pipeline can be built using command-line tools piped together, running as processes on GNU\/Linux instead of using microservices and a full orchestration system<\/li>\n<li>Scalability can be achieved by SSH and distribution through GNU Parallel, which launches jobs across multiple machines accessed over the network<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  4.4 Erlang and Clojure as Additional Isolation Models\n<\/h3>\n\n<p><strong>Erlang processes:<\/strong><\/p>\n\n<ul>\n<li>Lightweight alternative to Unix processes<\/li>\n<li>Rich high-level interprocess communication via mailbox message passing<\/li>\n<li>The Erlang VM enforces process isolation as a runtime guarantee<\/li>\n<\/ul>\n\n<p><strong>Clojure and other functional runtimes:<\/strong><\/p>\n\n<ul>\n<li>Do not have the same process isolation constraints as the Erlang VM<\/li>\n<li>Provide lightweight isolation via persistent data structures and Software Transactional Memory (STM)<\/li>\n<li>STM allows memory sharing while preventing conflicts even when multiple functions run in parallel or concurrently<\/li>\n<\/ul>\n\n<h2>\n  \n  \n  Bottom Line: Think Twice Before Going Micro\n<\/h2>\n\n<p>Here is the real talk. You might want to pause before spinning up your first microservice. Ask yourself these questions:<\/p>\n\n<ul>\n<li>Do I actually need physical isolation, or will logical separation suffice?<\/li>\n<li>Can a simple pipe between processes do the job just as well?<\/li>\n<li>Am I solving a scaling problem that does not exist yet?<\/li>\n<li>Do I have the ops maturity to handle distributed tracing, service meshes, and deployment pipelines?<\/li>\n<li>Will my team understand this architecture six months from now?<\/li>\n<\/ul>\n\n<p>The truth is, Unix pipes have been doing data transformation reliably since 1973. Erlang processes have handled millions of concurrent connections since the 1980s. Functional isolation with STM has been working since Clojure showed up in 2009. None of these require Kubernetes. None of them need a dedicated platform team. And none of them will haunt you with debugging nightmares at 3am.<\/p>\n\n<p>Microservices are not evil. They are just heavy. They are the nuclear option for isolation. Use them when the problem demands the weight. Otherwise, reach for the lighter tool. A pipe, a context boundary, a protocol type. Try the easy solution first. If it breaks, then scale up. Most teams never get to that point. And their systems stay simpler, cheaper, and easier to maintain because of it.<\/p>\n\n<p>So yeah, think twice. Maybe thrice. Then build the smallest thing that could possibly work.<\/p>\n\n","category":["architecture","subsystem","pipeline"]},{"title":"Building an MCP Server with Common Lisp","pubDate":"Sat, 11 Apr 2026 02:55:22 +0000","link":"https:\/\/dev.to\/veer66\/building-an-mcp-server-with-common-lisp-3lem","guid":"https:\/\/dev.to\/veer66\/building-an-mcp-server-with-common-lisp-3lem","description":"<p>I recently wanted to learn about MCP (Model Context Protocol). As someone whose default programming language is Common Lisp, I naturally decided to build an MCP server using Lisp.<\/p>\n\n<p>Thanks to the creators of <a href=\"https:\/\/github.com\/40ants\/mcp\" rel=\"noopener noreferrer\">40ants-mcp<\/a>, the library provides a nice pattern and code structure that I really like. However, I struggled significantly with installation and getting started. What should have taken minutes ended up taking days.<\/p>\n\n<p>I'm sharing my experience here so that others who want to build MCP servers in Common Lisp can get started in minutes, not days like I did.<\/p>\n\n<h2>\n  \n  \n  Prerequisites\n<\/h2>\n\n<p>Before you begin, make sure you have the following installed:<\/p>\n\n<ul>\n<li>\n<strong>SBCL<\/strong> - A high-performance Common Lisp compiler<\/li>\n<li>\n<strong>Roswell<\/strong> - A Common Lisp implementation manager and script runner<\/li>\n<li>\n<strong>Quicklisp<\/strong> - The de facto package manager for Common Lisp<\/li>\n<li>\n<strong>Ultralisp<\/strong> - A community-driven distribution of Common Lisp libraries<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  Installing Ultralisp\n<\/h3>\n\n<p>In SBCL with Quicklisp, you can enable Ultralisp by:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight common_lisp\"><code><span class=\"p\">(<\/span><span class=\"nv\">ql-dist:install-dist<\/span> <span class=\"s\">\"http:\/\/dist.ultralisp.org\/\"<\/span> <span class=\"ss\">:prompt<\/span> <span class=\"no\">nil<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>(How to install SBCL and Quicklisp is in the appendix.)<\/p>\n\n<h2>\n  \n  \n  The Gotcha: Loading 40ants-mcp\n<\/h2>\n\n<p>Here's the issue that cost me days: when you try to load <code>40ants-mcp<\/code> with:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight common_lisp\"><code><span class=\"p\">(<\/span><span class=\"nv\">ql:quickload<\/span> <span class=\"ss\">:40ants-mcp<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>You might encounter errors. The solution is simple but not obvious\u2014load <code>jsonrpc<\/code> first:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight common_lisp\"><code><span class=\"p\">(<\/span><span class=\"nv\">ql:quickload<\/span> <span class=\"ss\">:jsonrpc<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">(<\/span><span class=\"nv\">ql:quickload<\/span> <span class=\"ss\">:40ants-mcp<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>This dependency isn't automatically resolved, which was the source of my frustration.<\/p>\n\n<h2>\n  \n  \n  Creating Your MCP Server\n<\/h2>\n\n<p>Here's a minimal example from my <code>mcp-exper<\/code> package:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight common_lisp\"><code><span class=\"p\">(<\/span><span class=\"nb\">in-package<\/span> <span class=\"ss\">:mcp-exper<\/span><span class=\"p\">)<\/span>\n\n<span class=\"p\">(<\/span><span class=\"nv\">openrpc-server:define-api<\/span> <span class=\"p\">(<\/span><span class=\"nv\">mi-tools<\/span> <span class=\"ss\">:title<\/span> <span class=\"s\">\"mi-tools\"<\/span><span class=\"p\">))<\/span>\n\n<span class=\"p\">(<\/span><span class=\"nv\">40ants-mcp\/tools:define-tool<\/span> <span class=\"p\">(<\/span><span class=\"nv\">mi-tools<\/span> <span class=\"nv\">add<\/span><span class=\"p\">)<\/span> <span class=\"p\">(<\/span><span class=\"nv\">a<\/span> <span class=\"nv\">b<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">(<\/span><span class=\"ss\">:summary<\/span> <span class=\"s\">\"just add\"<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">(<\/span><span class=\"ss\">:param<\/span> <span class=\"nv\">a<\/span> <span class=\"nc\">integer<\/span> <span class=\"s\">\"a\"<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">(<\/span><span class=\"ss\">:param<\/span> <span class=\"nv\">b<\/span> <span class=\"nc\">integer<\/span> <span class=\"s\">\"b\"<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">(<\/span><span class=\"ss\">:result<\/span> <span class=\"nv\">text-content<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">(<\/span><span class=\"nb\">make-instance<\/span> <span class=\"ss\">'text-content<\/span> <span class=\"ss\">:text<\/span> <span class=\"p\">(<\/span><span class=\"nb\">format<\/span> <span class=\"no\">nil<\/span> <span class=\"s\">\"~a\"<\/span> <span class=\"p\">(<\/span><span class=\"nb\">+<\/span> <span class=\"nv\">a<\/span> <span class=\"nv\">b<\/span><span class=\"p\">))))<\/span>\n\n<span class=\"p\">(<\/span><span class=\"nb\">defun<\/span> <span class=\"nv\">start-server<\/span> <span class=\"p\">()<\/span>\n  <span class=\"p\">(<\/span><span class=\"nv\">40ants-mcp\/server\/definition:start-server<\/span> <span class=\"nv\">mi-tools<\/span><span class=\"p\">))<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Key points:<\/p>\n\n<ol>\n<li>Use <code>openrpc-server:define-api<\/code> to define your API<\/li>\n<li>Use <code>40ants-mcp\/tools:define-tool<\/code> to define tools<\/li>\n<li>Return <code>text-content<\/code> instances for text results (MCP requires specific content types)<\/li>\n<\/ol>\n\n<h2>\n  \n  \n  Running the Server\n<\/h2>\n\n<p>Create a Roswell script (<code>mi-mcp-server.ros<\/code>):<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight common_lisp\"><code><span class=\"err\">#<\/span><span class=\"nv\">!\/bin\/sh<\/span>\n<span class=\"cm\">#|-*- mode:lisp -*-|#<\/span>\n<span class=\"nv\">exec<\/span> <span class=\"nv\">ros<\/span> <span class=\"nv\">-Q<\/span> <span class=\"nv\">--<\/span> <span class=\"nv\">$0<\/span> <span class=\"s\">\"$@\"<\/span>\n<span class=\"err\">|#<\/span>\n<span class=\"p\">(<\/span><span class=\"k\">progn<\/span>\n  <span class=\"p\">(<\/span><span class=\"nv\">ros:ensure-asdf<\/span><span class=\"p\">)<\/span>\n  <span class=\"o\">#+<\/span><span class=\"nv\">quicklisp<\/span><span class=\"p\">(<\/span><span class=\"nv\">ql:quickload<\/span> <span class=\"o\">'<\/span><span class=\"p\">(<\/span><span class=\"ss\">:mcp-exper<\/span><span class=\"p\">)<\/span> <span class=\"ss\">:silent<\/span> <span class=\"no\">t<\/span><span class=\"p\">))<\/span>\n\n<span class=\"p\">(<\/span><span class=\"nb\">defun<\/span> <span class=\"nv\">main<\/span> <span class=\"p\">(<\/span><span class=\"k\">&amp;rest<\/span> <span class=\"nv\">argv<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">(<\/span><span class=\"k\">declare<\/span> <span class=\"p\">(<\/span><span class=\"k\">ignorable<\/span> <span class=\"nv\">argv<\/span><span class=\"p\">))<\/span>\n  <span class=\"p\">(<\/span><span class=\"nv\">mcp-exper:start-server<\/span><span class=\"p\">))<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  Quick Test\n<\/h3>\n\n<p>Run directly with Roswell:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>ros mi-mcp-server.ros\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  Production Installation\n<\/h3>\n\n<p>Build and install as an executable:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>ros build mi-mcp-server.ros\n<span class=\"nb\">install<\/span> <span class=\"nt\">-m<\/span> 0755 mi-mcp-server <span class=\"nv\">$HOME<\/span>\/.local\/bin\/\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Make sure <code>$HOME\/.local\/bin<\/code> is in your <code>PATH<\/code>.<\/p>\n\n<h2>\n  \n  \n  Integrating with Opencode\n<\/h2>\n\n<p>To enable your MCP server in opencode, add this to <code>~\/.config\/opencode\/opencode.json<\/code>:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight json\"><code><span class=\"p\">{<\/span><span class=\"w\">\n    <\/span><span class=\"nl\">\"mcp\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n        <\/span><span class=\"nl\">\"mi-tools\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n            <\/span><span class=\"nl\">\"type\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">\"local\"<\/span><span class=\"p\">,<\/span><span class=\"w\">\n            <\/span><span class=\"nl\">\"command\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">[<\/span><span class=\"s2\">\"mi-mcp-server\"<\/span><span class=\"p\">],<\/span><span class=\"w\">\n            <\/span><span class=\"nl\">\"enabled\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"kc\">true<\/span><span class=\"w\">\n        <\/span><span class=\"p\">}<\/span><span class=\"w\">\n    <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/code><\/pre>\n\n<\/div>\n\n\n\n<h2>\n  \n  \n  Conclusion\n<\/h2>\n\n<p>Building MCP servers with Common Lisp is straightforward once you know the tricks. The <code>40ants-mcp<\/code> library is well-designed, and the OpenRPC integration works smoothly.<\/p>\n\n<p>I hope this guide saves you the days of frustration I experienced. Happy hacking!<\/p>\n\n\n\n\n<p><em>The full source code for this example is available at <a href=\"https:\/\/codeberg.org\/veer66\/mcp-exper\" rel=\"noopener noreferrer\">mcp-exper<\/a>.<\/em><\/p>\n\n<h2>\n  \n  \n  Appendix: Installing SBCL\n<\/h2>\n\n<h3>\n  \n  \n  macOS\n<\/h3>\n\n\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>brew <span class=\"nb\">install <\/span>sbcl\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  Debian\/Ubuntu\n<\/h3>\n\n\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>apt <span class=\"nb\">install <\/span>sbcl\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  Arch Linux\n<\/h3>\n\n\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>pacman <span class=\"nt\">-S<\/span> sbcl\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h2>\n  \n  \n  Appendix: Installing Quicklisp\n<\/h2>\n\n<p>Download and install Quicklisp:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>wget https:\/\/beta.quicklisp.org\/quicklisp.lisp\nsbcl <span class=\"nt\">--load<\/span> quicklisp.lisp <span class=\"se\">\\<\/span>\n        <span class=\"nt\">--eval<\/span> <span class=\"s1\">'(quicklisp-quickstart:install)'<\/span> <span class=\"se\">\\<\/span>\n        <span class=\"nt\">--eval<\/span> <span class=\"s1\">'(ql-util:without-prompting (ql:add-to-init-file))'<\/span> <span class=\"se\">\\<\/span>\n        <span class=\"nt\">--quit<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n","category":["ai","llm","mcp","tutorial"]},{"title":"Validating Native Python: A Practical Approach with baredtype","pubDate":"Mon, 19 Jan 2026 00:35:05 +0000","link":"https:\/\/dev.to\/veer66\/validating-native-python-a-practical-approach-with-baredtype-44g1","guid":"https:\/\/dev.to\/veer66\/validating-native-python-a-practical-approach-with-baredtype-44g1","description":"<p>In Python development, we often default to using basic data structures like <code>dict<\/code> and <code>list<\/code> to move data around. They are flexible, JSON-compatible, and require no special setup. With the recent advancements in <code>TypedDict<\/code> and <code>Annotated<\/code>, the Python type system has become powerful enough to describe these structures with high precision.<\/p>\n\n<p><strong>baredtype<\/strong> is a library built to bridge the gap between these native structures and formal validation. It allows you to enforce strict rules on your data without ever forcing that data to change its shape or type.<\/p>\n\n\n\n\n<h2>\n  \n  \n  The Strength of Basic Data Structures\n<\/h2>\n\n<p>The main draw of <code>baredtype<\/code> is that it works directly with the data structures you already use. There is no custom class instantiation or \"wrapping\" of data into library-specific objects.<\/p>\n\n<h3>\n  \n  \n  Serialization and Deserialization\n<\/h3>\n\n<p>Because your data remains a standard <code>list<\/code> or <code>dict<\/code>, you don't need special methods to get your data in or out of a system. You use the standard tools you already know:<\/p>\n\n<ul>\n<li>\n<strong>To JSON:<\/strong> <code>json.dumps(my_data)<\/code>\n<\/li>\n<li>\n<strong>From JSON:<\/strong> <code>json.loads(input_string)<\/code>\n<\/li>\n<li>\n<strong>Validation:<\/strong> <code>validate(MySchema, my_data)<\/code>\n<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  Handling Keys and <code>None<\/code> Values\n<\/h3>\n\n<p>Working with native structures means using standard Python idioms. <code>baredtype<\/code> respects <code>TypedDict<\/code> definitions for required fields and <code>Annotated<\/code> for value constraints, allowing you to handle <code>None<\/code> values and missing keys naturally.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight python\"><code><span class=\"kn\">from<\/span> <span class=\"n\">typing_extensions<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">TypedDict<\/span><span class=\"p\">,<\/span> <span class=\"n\">NotRequired<\/span><span class=\"p\">,<\/span> <span class=\"n\">Annotated<\/span>\n<span class=\"kn\">from<\/span> <span class=\"n\">baredtype<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">validate<\/span>\n\n<span class=\"k\">class<\/span> <span class=\"nc\">UserData<\/span><span class=\"p\">(<\/span><span class=\"n\">TypedDict<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">username<\/span><span class=\"p\">:<\/span> <span class=\"nb\">str<\/span>\n    <span class=\"n\">email<\/span><span class=\"p\">:<\/span> <span class=\"n\">Annotated<\/span><span class=\"p\">[<\/span><span class=\"nb\">str<\/span> <span class=\"o\">|<\/span> <span class=\"bp\">None<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"sh\">\"<\/span><span class=\"s\">pattern<\/span><span class=\"sh\">\"<\/span><span class=\"p\">:<\/span> <span class=\"sa\">r<\/span><span class=\"sh\">\"<\/span><span class=\"s\">^[\\w.-]+@[\\w.-]+\\.\\w+$<\/span><span class=\"sh\">\"<\/span><span class=\"p\">}]<\/span>\n    <span class=\"n\">age<\/span><span class=\"p\">:<\/span> <span class=\"n\">NotRequired<\/span><span class=\"p\">[<\/span><span class=\"nb\">int<\/span><span class=\"p\">]<\/span>\n\n<span class=\"c1\"># Use standard Python to check your data:\n<\/span><span class=\"n\">data<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"sh\">\"<\/span><span class=\"s\">username<\/span><span class=\"sh\">\"<\/span><span class=\"p\">:<\/span> <span class=\"sh\">\"<\/span><span class=\"s\">tech_lead<\/span><span class=\"sh\">\"<\/span><span class=\"p\">,<\/span> <span class=\"sh\">\"<\/span><span class=\"s\">email<\/span><span class=\"sh\">\"<\/span><span class=\"p\">:<\/span> <span class=\"bp\">None<\/span><span class=\"p\">}<\/span>\n\n<span class=\"k\">if<\/span> <span class=\"n\">data<\/span><span class=\"p\">.<\/span><span class=\"nf\">get<\/span><span class=\"p\">(<\/span><span class=\"sh\">\"<\/span><span class=\"s\">email<\/span><span class=\"sh\">\"<\/span><span class=\"p\">)<\/span> <span class=\"ow\">is<\/span> <span class=\"bp\">None<\/span><span class=\"p\">:<\/span>\n    <span class=\"nf\">print<\/span><span class=\"p\">(<\/span><span class=\"sh\">\"<\/span><span class=\"s\">No email provided.<\/span><span class=\"sh\">\"<\/span><span class=\"p\">)<\/span>\n\n<span class=\"k\">if<\/span> <span class=\"sh\">\"<\/span><span class=\"s\">age<\/span><span class=\"sh\">\"<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">data<\/span><span class=\"p\">:<\/span>\n    <span class=\"nf\">print<\/span><span class=\"p\">(<\/span><span class=\"sh\">\"<\/span><span class=\"s\">Age is an optional key.<\/span><span class=\"sh\">\"<\/span><span class=\"p\">)<\/span>\n\n<span class=\"c1\"># Validation checks the types and the constraints\n<\/span><span class=\"n\">is_valid<\/span> <span class=\"o\">=<\/span> <span class=\"nf\">validate<\/span><span class=\"p\">(<\/span><span class=\"n\">UserData<\/span><span class=\"p\">,<\/span> <span class=\"n\">data<\/span><span class=\"p\">)<\/span>\n\n<\/code><\/pre>\n\n<\/div>\n\n\n\n\n\n\n<h2>\n  \n  \n  Mapping to JSON Schema and OpenAPI\n<\/h2>\n\n<p>A core objective of <code>baredtype<\/code> is making it easy to map Python types to external standards like JSON Schema and OpenAPI. It handles the \"quantifiers\" that define how different types can be combined.<\/p>\n\n<h3>\n  \n  \n  Quantifier Logic\n<\/h3>\n\n<ul>\n<li>\n<strong><code>oneOf<\/code><\/strong>: By using <code>Annotated<\/code> metadata, you can specify that data must match <strong>exactly one<\/strong> type in a Union. This is crucial for precise API definitions where ambiguity can lead to bugs.<\/li>\n<li>\n<strong><code>allOf<\/code><\/strong>: This is naturally supported through <code>TypedDict<\/code> inheritance. When one <code>TypedDict<\/code> inherits from others, <code>baredtype<\/code> generates a schema representing the union of those requirements, staying consistent with OpenAPI standards.<\/li>\n<\/ul>\n\n\n\n\n<h2>\n  \n  \n  Two Ways to Verify the Spec\n<\/h2>\n\n<p>When we define our data structures, we are writing a specification for our code. There are two primary ways to ensure our code follows this spec, both of which are supported by <code>baredtype<\/code>.<\/p>\n\n<h3>\n  \n  \n  1. Static Type Checking\n<\/h3>\n\n<p>Because the library is built on <code>TypedDict<\/code>, you get the full benefit of tools like <strong>Mypy<\/strong> or <strong>Pyright<\/strong>. Your IDE can catch missing keys or type mismatches while you are writing the code. This finds errors before the code ever runs.<\/p>\n\n<h3>\n  \n  \n  2. Property-Based Checking\n<\/h3>\n\n<p>While static types prove what <em>should<\/em> happen, property-based checking proves what <em>can<\/em> happen under stress. <code>baredtype<\/code> integrates with <strong>Hypothesis<\/strong> to automatically generate data that fits your <code>TypedDict<\/code> definitions, including constraints like <code>min_length<\/code> or regex patterns.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight python\"><code><span class=\"kn\">from<\/span> <span class=\"n\">baredtype<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">given_from_annotations<\/span>\n\n<span class=\"nd\">@given_from_annotations<\/span>\n<span class=\"k\">def<\/span> <span class=\"nf\">test_process_payload<\/span><span class=\"p\">(<\/span><span class=\"n\">payload<\/span><span class=\"p\">:<\/span> <span class=\"n\">UserData<\/span><span class=\"p\">):<\/span>\n    <span class=\"c1\"># Hypothesis generates a wide variety of valid UserData dicts\n<\/span>    <span class=\"c1\"># to find edge cases in your logic.\n<\/span>    <span class=\"n\">result<\/span> <span class=\"o\">=<\/span> <span class=\"nf\">my_function<\/span><span class=\"p\">(<\/span><span class=\"n\">payload<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">assert<\/span> <span class=\"n\">result<\/span> <span class=\"ow\">is<\/span> <span class=\"ow\">not<\/span> <span class=\"bp\">None<\/span>\n\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  Validation: The Constant Requirement\n<\/h3>\n\n<p>Regardless of whether you use static checking, property-based checking, or both, <strong>runtime validation is a must.<\/strong> External data is inherently untrusted. Static checking verifies your internal logic, and property-based testing verifies your code's resilience during development. At the moment data enters your system, <code>baredtype<\/code> provides the final, essential check to ensure the data actually matches your specification.<\/p>\n\n\n\n\n<h2>\n  \n  \n  Explore the Project\n<\/h2>\n\n<p><code>baredtype<\/code> offers a lightweight validation layer that stays out of your data's way, leveraging the emerging strengths of the Python type system.<\/p>\n\n<p><strong>Check out the project on Codeberg:<\/strong><br>\n\ud83d\udc49 <a href=\"https:\/\/codeberg.org\/veer66\/baredtype\" rel=\"noopener noreferrer\">https:\/\/codeberg.org\/veer66\/baredtype<\/a><\/p>\n\n","category":["python","baredtype","validation","openapi"]},{"title":"Revisiting codebase organization practices from 2004","pubDate":"Sat, 27 Dec 2025 02:06:57 +0000","link":"https:\/\/dev.to\/veer66\/revisiting-codebase-organization-practices-from-2004-5ej0","guid":"https:\/\/dev.to\/veer66\/revisiting-codebase-organization-practices-from-2004-5ej0","description":"<p>I reviewed the file and directory structure of several web-based free software projects released in 2003\u20132004: the Koha 1.2.0 library management system, the MRBS 1.2.1 meeting room booking system, and the DSpace 1.2 content repository system. These were written in Perl, PHP, and Java, respectively.<\/p>\n\n<p>Endpoint files are named using an action, optionally followed by an entity, following patterns such as search.pl, <code>search.php<\/code>, <code>SimpleSearchServlet.java<\/code>, <code>updatebibitem.pl<\/code>, <code>edit_entry.php<\/code>, and <code>EditProfileServlet.java<\/code>. These verbs or verb phrases may or may not align with user workflows. For example, Koha includes <code>opac_reserve.pl<\/code>, indicating it handles the reservation workflow via the Online Public Access Catalog (OPAC) API. However, there is no dedicated \"borrow\" endpoint file.<\/p>\n\n<p>Each system is designed in a modular style. Koha has a <code>Borrower.pm<\/code> module that provides a <code>reserveslist<\/code> function; MRBS uses <code>functions.inc<\/code> with functions such as <code>make_room_select_html<\/code>; and DSpace's <code>Item.java<\/code> includes methods like <code>findAll<\/code>. That said, they do not strictly follow a multi-tier architecture, for instance, database queries are not isolated into a dedicated data access layer. Still, DSpace, by using Servlets and JSPs, does establish a distinct presentation layer.<\/p>\n\n<p>Because of the verb-based naming convention, files emphasize what they do rather than which architectural layer they belong to. This makes their purpose easy to understand at a glance.<\/p>\n\n<p>However, this approach can make organization and testing more challenging. Logic, database queries, and presentation code are often mixed; sometimes not even separated into distinct functions.<\/p>\n\n","category":["softwareengineering","history","codebaseorganization"]},{"title":"Global Variables in Python Are Not That Global","pubDate":"Sat, 13 Dec 2025 05:51:25 +0000","link":"https:\/\/dev.to\/veer66\/global-variables-in-python-are-not-that-global-280j","guid":"https:\/\/dev.to\/veer66\/global-variables-in-python-are-not-that-global-280j","description":"<p>In the context of programming in the 1980s, \"global variables\" likely brings to mind languages like <a href=\"https:\/\/en.wikipedia.org\/wiki\/MBASIC\" rel=\"noopener noreferrer\">MBASIC<\/a>. However, using MBASIC as an example today would be challenging, as it is now rarely used or known. Instead, GNU Bash, which is the default shell scripting language for many systems\u2014will be used to illustrate what global variables were traditionally like. Anyway, some might think of Fortran II, but I'm not familiar with it.<\/p>\n\n<h2>\n  \n  \n  Bash Example\n<\/h2>\n\n<p>I wrote a Bash script consisting of four files:<\/p>\n\n<h3>\n  \n  \n  a.bash\n<\/h3>\n\n<p><code>a.bash<\/code> orchestrates everything. \"Orchestration\" might be too grand a word for these toy scripts, but I want to convey that it performs a role similar to Apache Airflow.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code><span class=\"c\">#!\/bin\/bash<\/span>\n\n<span class=\"nb\">.<\/span> .\/b.bash\n<span class=\"nb\">.<\/span> .\/c.bash\n<span class=\"nb\">.<\/span> .\/d.bash\n\ninit\nprint_count\ninc\nprint_count\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  b.bash\n<\/h3>\n\n<p><code>b.bash<\/code> contains only one function, <code>inc<\/code>, which increments the counter, which is the global variable in this example.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>inc<span class=\"o\">()<\/span> <span class=\"o\">{<\/span>\n    <span class=\"nv\">counter<\/span><span class=\"o\">=<\/span><span class=\"k\">$((<\/span>counter <span class=\"o\">+<\/span> <span class=\"m\">1<\/span><span class=\"k\">))<\/span>\n<span class=\"o\">}<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  c.bash\n<\/h3>\n\n<p><code>c.bash<\/code> contains <code>print_count<\/code>, which simply displays the value of the global variable <code>counter<\/code>.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>print_count<span class=\"o\">()<\/span> <span class=\"o\">{<\/span>\n    <span class=\"nb\">echo<\/span> <span class=\"nv\">$counter<\/span>\n<span class=\"o\">}<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  d.bash\n<\/h3>\n\n<p>In Bash, <code>counter<\/code> can be initialized globally from within a function by default.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>init<span class=\"o\">()<\/span> <span class=\"o\">{<\/span>\n    <span class=\"nv\">counter<\/span><span class=\"o\">=<\/span>1\n<span class=\"o\">}<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h2>\n  \n  \n  Python Example\n<\/h2>\n\n<p>The following section shows the result of porting the Bash script above to Python, highlighting the key differences.<\/p>\n\n<h2>\n  \n  \n  a.py\n<\/h2>\n\n<p>This is the orchestration part. Note the use of namespaces or module names.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight python\"><code><span class=\"kn\">import<\/span> <span class=\"n\">c<\/span><span class=\"p\">,<\/span> <span class=\"n\">b<\/span><span class=\"p\">,<\/span> <span class=\"n\">d<\/span>\n\n<span class=\"n\">d<\/span><span class=\"p\">.<\/span><span class=\"nf\">init<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">c<\/span><span class=\"p\">.<\/span><span class=\"nf\">print_count<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">b<\/span><span class=\"p\">.<\/span><span class=\"nf\">inc<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">c<\/span><span class=\"p\">.<\/span><span class=\"nf\">print_count<\/span><span class=\"p\">()<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h2>\n  \n  \n  b.py\n<\/h2>\n\n<p>In the Python version, <code>inc<\/code> must refer to the module <code>d<\/code> to access the variable. Alternatively, <code>counter<\/code> could be explicitly imported.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight python\"><code><span class=\"kn\">import<\/span> <span class=\"n\">d<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">inc<\/span><span class=\"p\">():<\/span>\n    <span class=\"n\">d<\/span><span class=\"p\">.<\/span><span class=\"n\">counter<\/span> <span class=\"o\">+=<\/span> <span class=\"mi\">1<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h2>\n  \n  \n  c.py\n<\/h2>\n\n<p>Similarly, <code>print_count<\/code> in Python must also refer to module <code>d<\/code>.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight python\"><code><span class=\"kn\">import<\/span> <span class=\"n\">d<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">print_count<\/span><span class=\"p\">():<\/span>\n    <span class=\"nf\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">d<\/span><span class=\"p\">.<\/span><span class=\"n\">counter<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h2>\n  \n  \n  d.py\n<\/h2>\n\n<p>Unlike in Bash, initializing a global variable from within a function\u2014even in the same module\u2014requires an explicit <code>global<\/code> declaration.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight python\"><code><span class=\"k\">def<\/span> <span class=\"nf\">init<\/span><span class=\"p\">():<\/span>\n    <span class=\"k\">global<\/span> <span class=\"n\">counter<\/span>\n    <span class=\"n\">counter<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">1<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h2>\n  \n  \n  Key Differences\n<\/h2>\n\n<p>As you can see, a global variable in Bash is truly global across files. In Python, however, a global variable is only global within its module, i.e., file. Furthermore, mutating a global variable in Bash requires no special syntax, whereas in Python a function must explicitly declare <code>global<\/code> to modify a module-level variable.<\/p>\n\n<p>Consequently, although both are called \"global variables,\" Python's are scoped to the module. This means they won\u2019t interfere with variables in other modules unless we deliberately make them do so. For developers who use one class per module, a Python global variable behaves much like a class variable. Additionally, variable assignment inside a Python function is local by default, preventing accidental modification of global state unless explicitly intended.<\/p>\n\n<p>In short, many traditional precautions about global variables in languages like Bash or MBASIC no longer apply in Python. Therefore, we might reconsider automatically rejecting global variables based on past advice and instead evaluate their use case thoughtfully.<\/p>\n\n","category":["python","globalvariable"]},{"title":"A lesson about abstraction from Arch Linux","pubDate":"Sun, 23 Nov 2025 09:23:50 +0000","link":"https:\/\/dev.to\/veer66\/a-lesson-about-abstraction-from-arch-linux-3e98","guid":"https:\/\/dev.to\/veer66\/a-lesson-about-abstraction-from-arch-linux-3e98","description":"<p>I was impressed by <a href=\"https:\/\/www.pkgsrc.org\/\" rel=\"noopener noreferrer\">pkgsrc<\/a> because it is a powerful package management system that defines packages using a common tool like a Makefile. I later discovered that the <a href=\"https:\/\/wiki.archlinux.org\/title\/PKGBUILD\" rel=\"noopener noreferrer\">PKGBUILD<\/a> file is even more impressive, as its purpose is immediately clear. I initially attributed this to my greater familiarity with Bash scripting compared to Makefiles, but I now believe the true reason is PKGBUILD's level of abstraction.<\/p>\n\n<p>It retains explicit calls to configure and make, preserving the transparency of a manual installation. This demonstrates that while increased abstraction can make code shorter, it can also hinder understanding.<\/p>\n\n<p>Another potential advantage of greater abstraction is the ability to change the configure command for every package by modifying just one location. However, since GNU Autotools has continued to use the configure command for decades, it may not be worth sacrificing clarity for this particular benefit.<\/p>\n\n","category":["abstraction","archlinux"]},{"title":"Reasons to use Emacs in 2025","pubDate":"Tue, 02 Sep 2025 15:52:03 +0000","link":"https:\/\/dev.to\/veer66\/reasons-to-use-emacs-in-2025-14bn","guid":"https:\/\/dev.to\/veer66\/reasons-to-use-emacs-in-2025-14bn","description":"<p>Expanding Emacs functionality is as simple as defining a new function instead of creating an entire extension package, as is often done in many other extensible editors. This function can then be re-evaluated, tested, and modified entirely within Emacs using just a few clicks or keyboard shortcuts, with no need to restart or reload Emacs. <\/p>\n\n","category":"emacs"},{"title":"Reasons to use Common Lisp in 2025","pubDate":"Sun, 17 Aug 2025 16:14:26 +0000","link":"https:\/\/dev.to\/veer66\/reasons-to-use-common-lisp-in-2025-523h","guid":"https:\/\/dev.to\/veer66\/reasons-to-use-common-lisp-in-2025-523h","description":"<h1>\n  \n  \n  Reasons to use Common Lisp\n<\/h1>\n\n<h2>\n  \n  \n  An actively-maintained-implementation, long-term-stable-specification programming language\n<\/h2>\n\n<p>There are many programming languages that don't change much, including<br>\nCommon Lisp, but Common Lisp implementations continue to be developed.<br>\nFor example, SBCL (Steel Bank Common Lisp) released its latest version<br>\njust last month.<\/p>\n\n<p>Common Lisp can be extended through libraries. For example, cl-interpol<br>\nenables Perl-style strings to Common Lisp without requiring a new<br>\nversion of Common Lisp. cl-arrows allows Common Lisp to create pipelines<br>\nusing Clojure-style syntax without needing to update the Common Lisp<br>\nspecification. This exceptional extensibility stems from macro and<br>\nparticularly reader macro support in Common Lisp.<\/p>\n<h2>\n  \n  \n  Feature-packed\n<\/h2>\n\n<p>Common Lisp includes many features found in modern programming<br>\nlanguages, such as:<\/p>\n\n<ul>\n<li>Garbage collection<\/li>\n<li>Built-in data structures (e.g., vectors, hash tables)<\/li>\n<li>Type hints<\/li>\n<li>Class definitions<\/li>\n<li>A syntactic structure similar to list comprehensions<\/li>\n<\/ul>\n<h2>\n  \n  \n  Multi-paradigm\n<\/h2>\n\n<p>While Lisp is commonly associated with functional programming, Common<br>\nLisp doesn't enforce this paradigm. It fully supports imperative<br>\nprogramming (like Pascal), and its object-oriented programming system<br>\neven includes advanced features. Best of all, you can freely mix all<br>\nthese styles. Common Lisp even embraces goto-like code via TAGBODY-GO.<\/p>\n<h2>\n  \n  \n  Performance\n<\/h2>\n\n<p>Common Lisp has many implementations, and some of them, such as SBCL,<br>\nare compilers that can generate efficient code.<\/p>\n\n<p>With some (of course, not all) implementations, many programs written in<br>\ndynamic programming languages run slower than those in static ones, such<br>\nas C and Modula-2.<\/p>\n\n<p>First, an example of the generated assembly will be shown, along with<br>\nmore explanation about why it might be slowed down by some dynamic<br>\nimplementations<\/p>\n\n<p>The code listing below is a part of a program written in Modula-2, which<br>\nmust be easy to read by programmers of languages in the extended ALGOL<br>\nfamily.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>    TYPE\n      Book = RECORD\n        title: ARRAY[1..64] OF CHAR;\n        price: REAL;\n      END;\n\n    PROCEDURE SumPrice(a, b: Book): REAL;\n    BEGIN\n      RETURN a.price + b.price;\n    END SumPrice;\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>The code is mainly for summing the price of books, and only the part<br>\n'a.price + b.price' will be focused on.<\/p>\n\n<p>'a.price + b.price' is translated into X86-64 assembly code list below<br>\nusing the GNU Modula-2 compiler.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>    movsd   80(%rbp), %xmm1\n    movsd   152(%rbp), %xmm0\n    addsd   %xmm1, %xmm0\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>\"movsd 80(%rbp), %xmm1' and 'movsd 152(%rbp), %xmm0' are for loading<br>\n'prices' to registers '%xmm1' and '%xmm0', respectively. Finally, 'addsd<br>\n%xmm1, %xmm0' is for adding prices together. As can be seen, the prices<br>\nare loaded from exact locations relative to the value of the '%rbp'<br>\nregister, which is one of the most efficient ways to load data from<br>\nmemory. The instruction 'addsd' is used because prices in this program<br>\nare REAL (floating point numbers), and '%xmm0', '%xmm1', and 'movsd' are<br>\nused for the same reason. This generated code should be reasonably<br>\nefficient. However, the compiler needs to know the type and location of<br>\nthe prices beforehand to choose the proper instructions and registers to<br>\nuse.<\/p>\n\n<p>In dynamic languages, 'SumPrice' can be applied to a price whose type is<br>\nan INTEGER instead of a REAL, or it can even be a string\/text. A<br>\nstraightforward implementation would check the type of 'a' and 'b' at<br>\nruntime, which makes the program much less efficient. The checking and<br>\nespecially branching can cost more time than adding the numbers<br>\nthemselves. Moreover, obtaining the value of the price attribute from<br>\n'a' and 'b' might be done by accessing a hash table instead of directly<br>\nloading the value from memory. Of course, while a hash-table has many<br>\nadvantages, it's less efficient because it requires many steps,<br>\nincluding comparing the attribute name and generating a hash value.<\/p>\n\n<p>However, compilers for dynamic languages can be much more advanced than<br>\nwhat's mentioned above, and SBCL is one such advanced compiler. SBCL can<br>\ninfer types from the code, especially from literals. Moreover, with<br>\ninformation from type hints and 'struct' usage, SBCL can generate code<br>\nthat's comparably as efficient as static language compilers.<\/p>\n\n<p>Given, the Common Lisp code listing below:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight common_lisp\"><code>    <span class=\"p\">(<\/span><span class=\"nb\">defstruct<\/span> <span class=\"nv\">book<\/span>\n      <span class=\"nv\">title<\/span>\n      <span class=\"p\">(<\/span><span class=\"nv\">price<\/span> <span class=\"mi\">0<\/span> <span class=\"ss\">:type<\/span> <span class=\"kt\">double-float<\/span><span class=\"p\">))<\/span>\n\n    <span class=\"p\">(<\/span><span class=\"nb\">declaim<\/span> <span class=\"p\">(<\/span><span class=\"k\">ftype<\/span> <span class=\"p\">(<\/span><span class=\"k\">function<\/span> <span class=\"p\">(<\/span><span class=\"nv\">book<\/span> <span class=\"nv\">book<\/span><span class=\"p\">)<\/span> <span class=\"kt\">double-float<\/span><span class=\"p\">)<\/span> <span class=\"nv\">add-price<\/span><span class=\"p\">)<\/span>\n         <span class=\"p\">(<\/span><span class=\"k\">optimize<\/span> <span class=\"p\">(<\/span><span class=\"nv\">speed<\/span> <span class=\"mi\">3<\/span><span class=\"p\">)<\/span> <span class=\"p\">(<\/span><span class=\"nv\">debug<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span> <span class=\"p\">(<\/span><span class=\"nv\">safety<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)))<\/span>\n    <span class=\"p\">(<\/span><span class=\"nb\">defun<\/span> <span class=\"nv\">add-price<\/span> <span class=\"p\">(<\/span><span class=\"nv\">a<\/span> <span class=\"nv\">b<\/span><span class=\"p\">)<\/span>\n      <span class=\"p\">(<\/span><span class=\"nb\">+<\/span> <span class=\"p\">(<\/span><span class=\"nv\">book-price<\/span> <span class=\"nv\">a<\/span><span class=\"p\">)<\/span>\n         <span class=\"p\">(<\/span><span class=\"nv\">book-price<\/span> <span class=\"nv\">b<\/span><span class=\"p\">)))<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>SBCL can generate assembly code for '(+ (book-price a) (book-price b))'<br>\nas shown below:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>    ; 86:       F20F104A0D       MOVSD XMM1, [RDX+13]\n    ; 8B:       F20F10570D       MOVSD XMM2, [RDI+13]\n    ; 90:       F20F58D1         ADDSD XMM2, XMM1\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>The assembly code format is slightly different from the one generated by<br>\nthe GNU Modula-2 compiler, but the main parts, the 'MOVSD' and 'ADDSD'<br>\ninstructions and the use of XMM registers\u2014are exactly the same. This<br>\nshows that we can write efficient code in Common Lisp at least for this<br>\ncase. This shows that we can write efficient code in Common Lisp, at<br>\nleast in this case, that is as efficient as, or nearly as efficient as,<br>\na static language.<\/p>\n\n<p>This implies that Common Lisp is good both for high-level rapid<br>\ndevelopment and optimized code, which has two advantages: (1) in many<br>\ncases, there is no need to switch between two languages, i.e., a<br>\nhigh-level one and a fast one; (2) the code can be started from<br>\nhigh-level and optimized in the same code after a profiler finds<br>\ncritical parts. This paradigm can prevent premature optimization.<\/p>\n<h2>\n  \n  \n  Interactive programming\n<\/h2>\n\n<p>Interactive programming may not sound familiar. However, it is a common<br>\ntechnique that has been used for decades. For example, a database engine<br>\nsuch as PostgreSQL doesn't need to be stopped and restarted just to run<br>\na new SQL statement. Similarly, it is akin to a spreadsheet like Lotus<br>\n1-2-3 or Microsoft Excel, which can run a new formula without needing to<br>\nreload existing sheets or restart the program.<\/p>\n\n<p>Common Lisp is exceptionally well-suited for interactive programming<br>\nbecause of (1) integrated editors with a REPL (Read Eval Print Loop),<br>\n(2) the language's syntax, and (3) the active community that has<br>\ndeveloped libraries specifically designed to support interactive<br>\nprogramming.<\/p>\n<h3>\n  \n  \n  Integrated editors with a REPL\n<\/h3>\n\n<p>With an integrated with a REPL, any part of the code can be evaluated<br>\nimmediately without copying and pasting from an editor into a REPL. This<br>\nworkflow provides feedback even faster than hot reloading because the<br>\ncode can be evaluated and its results seen instantaneously, even before<br>\nit is saved. There are many supported editors, such as Visual Studio<br>\nCode, Emacs, Neovim, and others.<\/p>\n<h3>\n  \n  \n  the language's syntax\n<\/h3>\n\n<p>Instead of marking region arbitrarily for evaluating, which is not very<br>\nconvenient when it is done every few seconds, in Common Lisp, we can<br>\nmark a form (which is similar to a block in ALGOL) by moving a cursor to<br>\none of the parentheses in the code, which is very easy with structural<br>\nediting, which will be discussed in the next section.<\/p>\n\n<p>Moreover, even a method definition can be evaluated immediately without<br>\nresetting the state of the object in Common Lisp. Since method<br>\ndefinitions are not nested in defclass, this allows mixing interactive<br>\nprogramming and object-oriented programming (OOP) smoothly.<\/p>\n\n<p>Here's the corrected code listing:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight common_lisp\"><code>    <span class=\"p\">(<\/span><span class=\"nb\">defclass<\/span> <span class=\"nv\">toto<\/span> <span class=\"p\">()<\/span>\n      <span class=\"p\">((<\/span><span class=\"nv\">i<\/span> <span class=\"ss\">:initarg<\/span> <span class=\"ss\">:i<\/span> <span class=\"ss\">:accesor<\/span> <span class=\"nv\">i<\/span><span class=\"p\">)))<\/span>\n\n    <span class=\"p\">(<\/span><span class=\"nb\">defmethod<\/span> <span class=\"nv\">update-i<\/span> <span class=\"p\">((<\/span><span class=\"nv\">obj<\/span> <span class=\"nv\">toto<\/span><span class=\"p\">))<\/span>\n      <span class=\"p\">(<\/span><span class=\"nb\">setf<\/span> <span class=\"p\">(<\/span><span class=\"nv\">i<\/span> <span class=\"nv\">obj<\/span><span class=\"p\">)<\/span> <span class=\"p\">(<\/span><span class=\"nb\">+<\/span> <span class=\"nv\">i<\/span> <span class=\"nv\">obj<\/span><span class=\"p\">)<\/span> <span class=\"mi\">1<\/span><span class=\"p\">))<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>According to the code listing above, the method 'update-i' can be<br>\nredefined without interfering with the pre-existing value of 'i'.<\/p>\n\n<h2>\n  \n  \n  Structural editing\n<\/h2>\n\n<p>Instead of editing Lisp code like normal text, tree-based operations can<br>\nbe used instead, such as paredit-join-sexps and<br>\nparedit-forward-slurp-sexp. Moving cursor operations, such as<br>\nparedit-forward, which moves the cursor to the end of the form (a<br>\nblock). These structural moving operations are also useful for selecting<br>\nregions to be evaluated in a REPL.<\/p>\n\n<h2>\n  \n  \n  Conclusion\n<\/h2>\n\n<p>In brief, Common Lisp has unparalleled combined advantages, which are<br>\nrelevant to software development especially now, not just an archaic<br>\ntechnology that just came earlier. For example, Forth has a<br>\nlong-term-stable specification, and works well with interactive<br>\nprogramming, but it is not designed for defining classes and adding type<br>\nhints. Julia has similar performance optimization and OOP is even<br>\nricher, but it doesn't have a long-term-stable specification. Moreover,<br>\nCommon Lisp's community is still active, as libraries, apps, and even<br>\nimplementations continue to receive updates.<\/p>\n\n","category":["commonlisp","lisp"]},{"title":"My programming environment journey","pubDate":"Sat, 19 Jul 2025 04:58:58 +0000","link":"https:\/\/dev.to\/veer66\/my-programming-environment-journey-5748","guid":"https:\/\/dev.to\/veer66\/my-programming-environment-journey-5748","description":"<p>No one actually cares about my programming environment journey, but I\u2019ve often been asked to share it, perhaps for the sake of social media algorithms. I post it here, so later, I can copy and paste this conveniently.<\/p>\n\n<p>My first computer, in the sense that I, not someone else, made the decision to buy it, ran Debian in 2002. It was a used Compaq desktop with a Pentium II processor, which I bought from Zeer Rangsit, a used computer market that may be the most famous in Thailand these days. When I got it home, I installed Debian right away. Before I bought my computer, I had used MBasic, mainly MS-DOS, Windows 3.1 (though rarely), and  Solaris (remotely).  For experimentation, I used Xenix, AIX, and one on DEC PDP-11 that I forgot.<\/p>\n\n<p>Since I started with MBasic, that was my first programming environment. I learned Logo at a summer camp, so that became my second. Later, my father bought me a copy of Turbo Basic, and at school, I switched to Turbo Pascal.<\/p>\n\n<p>After moving to GNU\/Linux, I used more editors instead IDEs. From 1995 to 2010, my editors were pico, nvi, vim, TextMate, and Emacs paired with GCC (mostly C, not C++), PHP, Perl, Ruby, Python, JavaScript, and SQL. I also used VisualAge to learn Java in the 90s. I tried Haskell, OCaml, Objective C, Lua, Julia, and Scala too, but it was strictly for learning only.<\/p>\n\n<p>After 2010, I used IntelliJ IDEA and Eclipse for Java and Kotlin. For Rust (instead of C), I used Emacs and Visual Studio Code. I explored Racket for learning purposes, then later started coding seriously in Clojure and Common Lisp. I tried using Vim 9.x and Neovim too, they were great, but not quite my cup of tea.<\/p>\n\n<p>In 2025, a few days ago, I learned Smalltalk with Pharo to deepen my understanding of OOP and exploratory programming.<\/p>\n\n<p>Update 2025\/07\/20: I forgot to mention xBase. In the '90s, I used it in a programming competition, but none of my programs in xBase reach production.<\/p>\n\n","category":"programmingenvironment"},{"title":"I have been told to avoid linked lists.","pubDate":"Sun, 23 Mar 2025 08:18:12 +0000","link":"https:\/\/dev.to\/veer66\/i-have-been-told-to-avoid-linked-lists-24ja","guid":"https:\/\/dev.to\/veer66\/i-have-been-told-to-avoid-linked-lists-24ja","description":"<p>I've been told to avoid linked lists because their elements are scattered everywhere, which can be true in some cases. However, I wonder what happens in loops, which I use frequently. I tried to inspect memory addresses of list elements of these two programs run on SBCL.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight common_lisp\"><code><span class=\"nv\">CL-USER&gt;<\/span> <span class=\"p\">(<\/span><span class=\"k\">setq<\/span> <span class=\"vg\">*a-list*<\/span> <span class=\"p\">(<\/span><span class=\"k\">let<\/span> <span class=\"p\">((<\/span><span class=\"nv\">a<\/span> <span class=\"no\">nil<\/span><span class=\"p\">))<\/span> <span class=\"p\">(<\/span><span class=\"nb\">push<\/span> <span class=\"mi\">10<\/span> <span class=\"nv\">a<\/span><span class=\"p\">)<\/span> <span class=\"p\">(<\/span><span class=\"nb\">push<\/span> <span class=\"mi\">20<\/span> <span class=\"nv\">a<\/span><span class=\"p\">)<\/span> <span class=\"p\">(<\/span><span class=\"nb\">push<\/span> <span class=\"mi\">30<\/span> <span class=\"nv\">a<\/span><span class=\"p\">)<\/span> <span class=\"nv\">a<\/span><span class=\"p\">))<\/span>\n<span class=\"p\">(<\/span><span class=\"mi\">30<\/span> <span class=\"mi\">20<\/span> <span class=\"mi\">10<\/span><span class=\"p\">)<\/span>\n<span class=\"nv\">CL-USER&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nv\">sb-kernel:get-lisp-obj-address<\/span> <span class=\"vg\">*a-list*<\/span><span class=\"p\">)<\/span>\n<span class=\"mi\">69517814583<\/span> <span class=\"p\">(<\/span><span class=\"mi\">37<\/span> <span class=\"nv\">bits,<\/span> <span class=\"m\">#x102F95AB37<\/span><span class=\"p\">)<\/span>\n<span class=\"nv\">CL-USER&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nv\">sb-kernel:get-lisp-obj-address<\/span> <span class=\"p\">(<\/span><span class=\"nb\">cdr<\/span> <span class=\"vg\">*a-list*<\/span><span class=\"p\">))<\/span>\n<span class=\"mi\">69517814567<\/span> <span class=\"p\">(<\/span><span class=\"mi\">37<\/span> <span class=\"nv\">bits,<\/span> <span class=\"m\">#x102F95AB27<\/span><span class=\"p\">)<\/span>\n<span class=\"nv\">CL-USER&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nv\">sb-kernel:get-lisp-obj-address<\/span> <span class=\"p\">(<\/span><span class=\"nb\">cddr<\/span> <span class=\"vg\">*a-list*<\/span><span class=\"p\">))<\/span>\n<span class=\"mi\">69517814551<\/span> <span class=\"p\">(<\/span><span class=\"mi\">37<\/span> <span class=\"nv\">bits,<\/span> <span class=\"m\">#x102F95AB17<\/span><span class=\"p\">)<\/span>\n<span class=\"nv\">CL-USER&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nv\">sb-kernel:get-lisp-obj-address<\/span> <span class=\"p\">(<\/span><span class=\"nb\">cddr<\/span> <span class=\"vg\">*a-list*<\/span><span class=\"p\">))<\/span>\n<span class=\"mi\">69517814551<\/span> <span class=\"p\">(<\/span><span class=\"mi\">37<\/span> <span class=\"nv\">bits,<\/span> <span class=\"m\">#x102F95AB17<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight common_lisp\"><code><span class=\"nv\">CL-USER&gt;<\/span> <span class=\"p\">(<\/span><span class=\"k\">setq<\/span> <span class=\"vg\">*a-list*<\/span> <span class=\"p\">(<\/span><span class=\"k\">let<\/span> <span class=\"p\">((<\/span><span class=\"nv\">a<\/span> <span class=\"no\">nil<\/span><span class=\"p\">))<\/span>\n              <span class=\"p\">(<\/span><span class=\"nb\">push<\/span> <span class=\"mi\">10<\/span> <span class=\"nv\">a<\/span><span class=\"p\">)<\/span>\n              <span class=\"p\">(<\/span><span class=\"nb\">push<\/span> <span class=\"mi\">20<\/span> <span class=\"nv\">a<\/span><span class=\"p\">)<\/span>\n              <span class=\"p\">(<\/span><span class=\"nb\">push<\/span> <span class=\"mi\">30<\/span> <span class=\"nv\">a<\/span><span class=\"p\">)<\/span> \n              <span class=\"nv\">a<\/span><span class=\"p\">))<\/span>\n<span class=\"p\">(<\/span><span class=\"mi\">30<\/span> <span class=\"mi\">20<\/span> <span class=\"mi\">10<\/span><span class=\"p\">)<\/span>\n<span class=\"nv\">CL-USER&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nv\">sb-kernel:get-lisp-obj-address<\/span> <span class=\"vg\">*a-list*<\/span><span class=\"p\">)<\/span>\n<span class=\"mi\">69518319943<\/span> <span class=\"p\">(<\/span><span class=\"mi\">37<\/span> <span class=\"nv\">bits,<\/span> <span class=\"m\">#x102F9D6147<\/span><span class=\"p\">)<\/span>\n<span class=\"nv\">CL-USER&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nv\">sb-kernel:get-lisp-obj-address<\/span> <span class=\"p\">(<\/span><span class=\"nb\">cdr<\/span> <span class=\"vg\">*a-list*<\/span><span class=\"p\">))<\/span>\n<span class=\"mi\">69518319927<\/span> <span class=\"p\">(<\/span><span class=\"mi\">37<\/span> <span class=\"nv\">bits,<\/span> <span class=\"m\">#x102F9D6137<\/span><span class=\"p\">)<\/span>\n<span class=\"nv\">CL-USER&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nv\">sb-kernel:get-lisp-obj-address<\/span> <span class=\"p\">(<\/span><span class=\"nb\">cddr<\/span> <span class=\"vg\">*a-list*<\/span><span class=\"p\">))<\/span>\n<span class=\"mi\">69518319911<\/span> <span class=\"p\">(<\/span><span class=\"mi\">37<\/span> <span class=\"nv\">bits,<\/span> <span class=\"m\">#x102F9D6127<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>In both programs, the list elements are not scattered. So, if scattered list elements were an issue for these simple cases, you probably used the wrong compiler or memory allocator.<\/p>\n\n","category":"linkedlist"},{"title":"JSON Manipulation","pubDate":"Sun, 19 Jan 2025 11:55:57 +0000","link":"https:\/\/dev.to\/veer66\/json-manipulation-1183","guid":"https:\/\/dev.to\/veer66\/json-manipulation-1183","description":"<p>There are many ways to manipulate JSON. I reviewed a few rapid ways today, which are using a command line tool called <code>jq<\/code> and libraries that support JSONPath query language.<\/p>\n\n<h1>\n  \n  \n  jq\n<\/h1>\n\n<p><code>Awk<\/code> is a powerful domain-specific language for text processing, but it lacks built-in support for manipulating hierarchical data structures like trees. <code>jq<\/code> fills this gap by providing a tool for transforming JSON data. However, one potential drawback is that <code>jq<\/code> requires a separate installation, which may add complexity to my workflow.<\/p>\n\n<p>So I write a shell script using <code>jq<\/code> to extract user's names and user's urls from a cURL response, and then output it in TSV format.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code>curl <span class=\"nt\">-s<\/span> <span class=\"s1\">'https:\/\/mstdn.in.th\/api\/v1\/timelines\/public?limit=10'<\/span> | jq <span class=\"s1\">'.[].account | [.username, .url] | @tsv'<\/span> <span class=\"nt\">-r<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>The result looks like this:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>kuketzblog      https:\/\/social.tchncs.de\/@kuketzblog\ncats    https:\/\/social.goose.rodeo\/@cats\nAlJazeera       https:\/\/flipboard.com\/@AlJazeera\nTheHindu        https:\/\/flipboard.com\/@TheHindu\nGossiTheDog     https:\/\/cyberplace.social\/@GossiTheDog\nkuketzblog      https:\/\/social.tchncs.de\/@kuketzblog\nweeklyOSM       https:\/\/en.osm.town\/@weeklyOSM\njuanbellas      https:\/\/masto.es\/@juanbellas\nnoborusudou     https:\/\/misskey.io\/@noborusudou\njerryd  https:\/\/mastodon.social\/@jerryd\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>With a TSV file, we can use Awk, sed, etc. to manipulate them as usual.<\/p>\n\n<h1>\n  \n  \n  JSONPath\n<\/h1>\n\n<p>JSONPath, which was explained in <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc9535\" rel=\"noopener noreferrer\">RFC 9535<\/a>, is supported by many libraries and applications, e.g. PostgreSQL. Still, I try to it in Python by the <code>jsonpath_nq<\/code> library.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight python\"><code><span class=\"kn\">from<\/span> <span class=\"n\">jsonpath_ng<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">parse<\/span>\n<span class=\"kn\">import<\/span> <span class=\"n\">requests<\/span>\n\n\n<span class=\"n\">res<\/span> <span class=\"o\">=<\/span> <span class=\"n\">requests<\/span><span class=\"p\">.<\/span><span class=\"nf\">get<\/span><span class=\"p\">(<\/span><span class=\"sh\">\"<\/span><span class=\"s\">https:\/\/mstdn.in.th\/api\/v1\/timelines\/public?limit=10<\/span><span class=\"sh\">\"<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">compiled_path<\/span> <span class=\"o\">=<\/span> <span class=\"nf\">parse<\/span><span class=\"p\">(<\/span><span class=\"sh\">\"<\/span><span class=\"s\">$[*].account<\/span><span class=\"sh\">\"<\/span><span class=\"p\">)<\/span>\n\n\n<span class=\"k\">for<\/span> <span class=\"n\">matched_node<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">compiled_path<\/span><span class=\"p\">.<\/span><span class=\"nf\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">res<\/span><span class=\"p\">.<\/span><span class=\"nf\">json<\/span><span class=\"p\">()):<\/span>\n    <span class=\"nf\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">matched_node<\/span><span class=\"p\">.<\/span><span class=\"n\">value<\/span><span class=\"p\">[<\/span><span class=\"sh\">\"<\/span><span class=\"s\">username<\/span><span class=\"sh\">\"<\/span><span class=\"p\">]<\/span> <span class=\"o\">+<\/span> <span class=\"sh\">\"<\/span><span class=\"se\">\\t<\/span><span class=\"sh\">\"<\/span> <span class=\"o\">+<\/span> <span class=\"n\">matched_node<\/span><span class=\"p\">.<\/span><span class=\"n\">value<\/span><span class=\"p\">[<\/span><span class=\"sh\">\"<\/span><span class=\"s\">url<\/span><span class=\"sh\">\"<\/span><span class=\"p\">])<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>It gave the same result to the shell script above. The code is a bit longer than the shell script. However, it can be integrated with many Python libraries.<\/p>\n\n","category":["json","dsl"]},{"title":"Four Reasons that I Sometimes Use Awk Instead of Python","pubDate":"Tue, 31 Dec 2024 17:44:34 +0000","link":"https:\/\/dev.to\/veer66\/four-reasons-that-i-sometimes-use-awk-instead-of-python-4nhb","guid":"https:\/\/dev.to\/veer66\/four-reasons-that-i-sometimes-use-awk-instead-of-python-4nhb","description":"<p>Python is a fantastic language, but in specific situations, Awk can offer significant advantages, particularly in terms of portability, longevity, conciseness, and interoperability.<\/p>\n\n<p>While Python scripts are generally portable, they may not always run seamlessly on popular Docker base images like Debian and Alpine. In contrast, Awk scripts are often readily available and executable within these environments.<\/p>\n\n<p>Although Python syntax is relatively stable, its lifespan is shorter compared to Awk. For example, the <code>print 10<\/code> syntax from the early 2000s is no longer valid in modern Python. However, Awk scripts from the 1980s can still be executed in current environments.<\/p>\n\n<p>Python is known for its conciseness, especially when compared to languages like Java. However, when it comes to text processing and working within shell pipelines, Awk often provides more concise solutions. For instance, extracting text blocks between \"REPORT\" and \"END\" can be achieved with a single line in Awk: <code>\/REPORT\/,\/END\/ { print }<\/code>. Achieving the same result in Python typically involves more lines of code, including handling file input and pattern matching.<\/p>\n\n<p>While Python can be embedded within shell scripts like Bash, aligning the indentation of multiline Python code with the surrounding shell script can often break the Python syntax. Awk, on the other hand, is less sensitive to indentation, making it easier to integrate into shell scripts.<\/p>\n\n<p>Although different Awk implementations (such as Busybox Awk and GNU Awk) may have minor variations, Awk generally offers advantages over Python in the situations mentioned above.<\/p>\n\n","category":["python","awk"]}]}}