<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Maple</title>
    <description>The latest articles on Forem by Maple (@maple).</description>
    <link>https://forem.com/maple</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F456950%2Ff15c6126-448c-4b4f-96ce-7e1383e5707d.jpg</url>
      <title>Forem: Maple</title>
      <link>https://forem.com/maple</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/maple"/>
    <language>en</language>
    <item>
      <title>Building a Python-requests-style HTTP Client in Haskell</title>
      <dc:creator>Maple</dc:creator>
      <pubDate>Sat, 14 Mar 2026 11:16:08 +0000</pubDate>
      <link>https://forem.com/maple/building-a-python-requests-style-http-client-in-haskell-3a0k</link>
      <guid>https://forem.com/maple/building-a-python-requests-style-http-client-in-haskell-3a0k</guid>
      <description>&lt;h2&gt;
  
  
  The Problem with Learning Haskell
&lt;/h2&gt;

&lt;p&gt;When I started learning Haskell, I always struggled to find a suitable scenario to apply it. Without a concrete problem to solve, it is difficult to make real progress.&lt;/p&gt;

&lt;p&gt;At some point I had some practical work to do: fetch data from HTTP APIs, process the results, and post them to other endpoints. My instinct was to reach for Python and the &lt;code&gt;requests&lt;/code&gt; library, because it is genuinely handy for this kind of task. You grab a URL, get back some JSON, transform it, and send it somewhere else. The whole thing can be done in a few lines.&lt;/p&gt;

&lt;p&gt;But I thought: this is exactly the kind of real problem I need. Why not try Haskell?&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring the Existing Ecosystem
&lt;/h2&gt;

&lt;p&gt;The first thing I reached for was &lt;code&gt;http-client&lt;/code&gt;. It is powerful and the foundation for most other Haskell HTTP libraries. But using it directly requires quite a bit of setup for simple tasks. You need to create a manager, parse a request, and work with a set of low-level types. It is a great library, but it is designed for cases where you need precise control.&lt;/p&gt;

&lt;p&gt;There are some lighter alternatives. &lt;code&gt;wreq&lt;/code&gt; is a popular one, built on a lens API. Lenses are a fascinating part of the Haskell ecosystem and many people find them worth learning. &lt;code&gt;req&lt;/code&gt; is another option with a clean, type-safe API. I used &lt;code&gt;req&lt;/code&gt; to finish the work I was doing, and it worked well. But I still missed the simplicity of Python's &lt;code&gt;requests&lt;/code&gt;, and I kept thinking about whether something like it was possible in Haskell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding http-dispatch and Deciding to Build Something
&lt;/h2&gt;

&lt;p&gt;Some time later, I came across &lt;code&gt;http-dispatch&lt;/code&gt;. It was much closer to what I had in mind. The API was straightforward and the concepts were familiar. Unfortunately the library had not been maintained for a long time. There were some bugs, and some fixes in the source code had never made it to Hackage.&lt;/p&gt;

&lt;p&gt;That is when I decided to build my own.&lt;/p&gt;

&lt;p&gt;The goal was simple: a library for scenarios where you do not need fine-grained control over the HTTP lifecycle. For those cases, &lt;code&gt;http-client&lt;/code&gt; is the right tool. But for the common case of making a request and getting back a response, I wanted something that felt closer to the Python experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Abstraction
&lt;/h2&gt;

&lt;p&gt;I started from the most basic mental model of an HTTP interaction: you have a request, you send it to a server, and you get back a response.&lt;/p&gt;

&lt;p&gt;This became the core API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;  &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Method&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;     &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Headers&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;    &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;  &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Headers&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;    &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;send&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ToRequestBody&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;FromResponseBody&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;send&lt;/code&gt; function takes a &lt;code&gt;Request&lt;/code&gt; and produces a &lt;code&gt;Response&lt;/code&gt; in &lt;code&gt;IO&lt;/code&gt;. The type parameters carry the body types, and the typeclass constraints handle serialization and deserialization automatically.&lt;/p&gt;

&lt;p&gt;For common HTTP methods, there are simple shortcuts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- A simple GET request&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s"&gt;"https://example.com"&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;print&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;  &lt;span class="c1"&gt;-- 200&lt;/span&gt;
&lt;span class="n"&gt;print&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;    &lt;span class="c1"&gt;-- HTML content as String&lt;/span&gt;

&lt;span class="c1"&gt;-- POST with a plain text body&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s"&gt;"https://httpbin.org/post"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;print&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;  &lt;span class="c1"&gt;-- 200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also construct a &lt;code&gt;Request&lt;/code&gt; manually when you need custom headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;GET&lt;/span&gt;
      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.example.com/data"&lt;/span&gt;
      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Bearer my-token"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library supports modern Haskell record dot syntax (&lt;code&gt;resp.status&lt;/code&gt;, &lt;code&gt;resp.body&lt;/code&gt;) as well as traditional accessor functions (&lt;code&gt;responseStatus resp&lt;/code&gt;, &lt;code&gt;responseBody resp&lt;/code&gt;) for those who prefer not to enable language extensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON Integration
&lt;/h2&gt;

&lt;p&gt;The feature I most wanted to replicate from Python's &lt;code&gt;requests&lt;/code&gt; is its JSON handling. In Python, you pass &lt;code&gt;json=&lt;/code&gt; to your request and call &lt;code&gt;.json()&lt;/code&gt; on the response. It handles the content type header, the serialization, and the deserialization for you. Most API work is exactly this: send JSON, receive JSON.&lt;/p&gt;

&lt;p&gt;Haskell's type system makes this possible in a way that is, I think, even nicer than Python. Because we declare our data types upfront, we get static guarantees about the shape of the data.&lt;/p&gt;

&lt;p&gt;Here is an example of parsing a JSON response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="cp"&gt;{-# LANGUAGE DeriveGeneric #-}&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Aeson&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FromJSON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;GHC.Generics&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Generic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Network.HTTP.Request&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;__type&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iso&lt;/span&gt;     &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Generic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;FromJSON&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;

&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s"&gt;"https://api.leancloud.cn/1.1/date"&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;  &lt;span class="c1"&gt;-- 200&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;    &lt;span class="c1"&gt;-- Date { __type = "Date", iso = "2026-03-14T..." }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no explicit parsing step. The type annotation &lt;code&gt;:: IO (Response Date)&lt;/code&gt; is enough to tell the library to decode the JSON body into a &lt;code&gt;Date&lt;/code&gt; value. If decoding fails, an &lt;code&gt;AesonException&lt;/code&gt; is thrown.&lt;/p&gt;

&lt;p&gt;Sending JSON works the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Generic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;ToJSON&lt;/span&gt; &lt;span class="kt"&gt;Message&lt;/span&gt;

&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s"&gt;"https://api.example.com/messages"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Message&lt;/span&gt; &lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any type with a &lt;code&gt;ToJSON&lt;/code&gt; instance is automatically serialized, and &lt;code&gt;Content-Type: application/json&lt;/code&gt; is set on the request. You do not need to think about it.&lt;/p&gt;

&lt;p&gt;This gives you the simplicity of Python's &lt;code&gt;requests&lt;/code&gt; combined with the compile-time guarantees of a static type system. You define your API shapes as types, and the compiler helps you use them correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using It for an LLM Agent
&lt;/h2&gt;

&lt;p&gt;With the library in a working state, I wanted to build something more substantial with it. I decided to implement a small LLM agent. That became &lt;code&gt;hasuke&lt;/code&gt;, a CLI tool for interacting with Claude.&lt;/p&gt;

&lt;p&gt;Calling an Anthropic-style API is exactly the use case this library was built for. You construct a JSON request body, send it to the endpoint, and get back a JSON response. The library handled all of this without any friction.&lt;/p&gt;

&lt;p&gt;But after building the first version, I noticed that it would sit silently until the full response was generated, then display everything at once. Modern LLM providers support streaming responses to address this: they send partial results incrementally using Server-Sent Events, so you start seeing output right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Streaming and SSE Support
&lt;/h2&gt;

&lt;p&gt;I extended the library to support streaming. The key was to express this within the existing type system without changing the core API. I introduced a &lt;code&gt;StreamBody&lt;/code&gt; type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;StreamBody&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;StreamBody&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;readNext&lt;/span&gt;    &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;closeStream&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To receive a streaming response, you just change the type annotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="kt"&gt;GET&lt;/span&gt; &lt;span class="s"&gt;"https://example.com/stream"&lt;/span&gt; &lt;span class="kt"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;StreamBody&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;mChunk&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readNext&lt;/span&gt;
      &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;mChunk&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Nothing&lt;/span&gt;    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt;
        &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;putStr&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
&lt;span class="n"&gt;loop&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;closeStream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For SSE, the library parses the event stream protocol automatically. Each &lt;code&gt;SseEvent&lt;/code&gt; has fields for the data, event type, and event id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;SseEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SseEvent&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sseData&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sseType&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sseId&lt;/span&gt;   &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="kt"&gt;POST&lt;/span&gt; &lt;span class="s"&gt;"https://api.anthropic.com/v1/messages"&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;StreamBody&lt;/span&gt; &lt;span class="kt"&gt;SseEvent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;mEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readNext&lt;/span&gt;
      &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;mEvent&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Nothing&lt;/span&gt;    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt;
        &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;putStr&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sseData&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
&lt;span class="n"&gt;loop&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;closeStream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;send&lt;/code&gt; function signature did not change at all. When the target type is &lt;code&gt;StreamBody SseEvent&lt;/code&gt;, the library keeps the connection open and streams events through an internal buffer. From the caller's side, you are just getting a different kind of response body.&lt;/p&gt;

&lt;p&gt;This is where Haskell's type system earns its keep. Adding a completely different data transfer mode required almost no changes to the existing API. The same &lt;code&gt;send&lt;/code&gt; function, the same &lt;code&gt;Request&lt;/code&gt; type, the same conventions. It is now working well in &lt;code&gt;hasuke&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Things Stand
&lt;/h2&gt;

&lt;p&gt;The library is published on Hackage under the name &lt;code&gt;request&lt;/code&gt; and can be installed with cabal or stack in the usual way. The source code is on GitHub at &lt;a href="https://github.com/aisk/request" rel="noopener noreferrer"&gt;https://github.com/aisk/request&lt;/a&gt;. It powers the streaming output in &lt;code&gt;hasuke&lt;/code&gt;, which you can find at &lt;a href="https://github.com/aisk/hasuke" rel="noopener noreferrer"&gt;https://github.com/aisk/hasuke&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are features still missing, such as support for HTML form encoding and some other less common use cases. I plan to add these gradually over time. If you run into something you need, feel free to open an issue on GitHub. Feedback and contributions are very welcome.&lt;/p&gt;

&lt;p&gt;For the common case of calling JSON APIs, whether in a single response or as a stream, the library does what it was built to do. If you are learning Haskell and looking for a practical project, or you just need a lightweight HTTP client, it might be worth a try.&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>http</category>
    </item>
    <item>
      <title>Making Python Modules Callable: Introducing Cadule</title>
      <dc:creator>Maple</dc:creator>
      <pubDate>Fri, 26 Dec 2025 15:02:37 +0000</pubDate>
      <link>https://forem.com/maple/making-python-modules-callable-introducing-cadule-mba</link>
      <guid>https://forem.com/maple/making-python-modules-callable-introducing-cadule-mba</guid>
      <description>&lt;p&gt;When writing Python code, I often find myself missing a feature from Node.js: the ability to directly &lt;code&gt;require&lt;/code&gt; a module and have it become callable. In Node.js, you can export a function from a module and call it immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messUpThings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./mess-up-things&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;messUpThings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Works!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know some developers aren't fond of this pattern, but there are legitimate use cases for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Consider a scenario where you need to write a helper function with a self-descriptive name, like &lt;code&gt;mess_up_things&lt;/code&gt;. The function name itself tells you what it does. You have a few options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a util or helper module&lt;/strong&gt;: Put it in a &lt;code&gt;util.py&lt;/code&gt; or &lt;code&gt;helper.py&lt;/code&gt; module. But many developers dislike these generic names, especially Go developers who prefer more descriptive package names.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a dedicated file&lt;/strong&gt;: Put it in &lt;code&gt;mess_up_things.py&lt;/code&gt;. But then you need to write verbose import statements like:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mess_up_things&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mess_up_things&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This repetition feels unnecessary. Wouldn't it be nice if you could just do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mess_up_things&lt;/span&gt;
&lt;span class="nf"&gt;mess_up_things&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exploring Solutions
&lt;/h2&gt;

&lt;p&gt;If you're familiar with Python's magic methods, you might know that any object with a &lt;code&gt;__call__&lt;/code&gt; method becomes callable. Since modules are instances of &lt;code&gt;types.ModuleType&lt;/code&gt;, and they support magic methods like &lt;code&gt;__getattr__&lt;/code&gt;, couldn't we just define a &lt;code&gt;__call__&lt;/code&gt; method directly on a module?&lt;/p&gt;

&lt;p&gt;Unfortunately, Python doesn't support this out of the box. There was actually &lt;a href="https://peps.python.org/pep-0713/" rel="noopener noreferrer"&gt;PEP 713&lt;/a&gt; that proposed adding module-level &lt;code&gt;__call__&lt;/code&gt; support, but it was rejected.&lt;/p&gt;

&lt;p&gt;However, if you're familiar with Python's runtime, you'll find there's a trick to achieve this behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Messing up things...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyModule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works by dynamically replacing the module's &lt;code&gt;__class__&lt;/code&gt; at runtime. But who wants to copy-paste this boilerplate every time?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Cadule
&lt;/h2&gt;

&lt;p&gt;To avoid repeating this boilerplate, I've encapsulated this simple mechanism into a package called &lt;strong&gt;Cadule&lt;/strong&gt; (short for &lt;strong&gt;Ca&lt;/strong&gt;llable &lt;strong&gt;[Mo]dule&lt;/strong&gt; &lt;strong&gt;Le&lt;/strong&gt;ss).&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;cadule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;Create a file called &lt;code&gt;mess_up_things.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cadule&lt;/span&gt;

&lt;span class="nd"&gt;@cadule&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Messing up things...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in your Python REPL or another script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mess_up_things&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;mess_up_things&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Messing&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mess_up_things&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! The entire &lt;code&gt;mess_up_things&lt;/code&gt; module is now a callable object. When you call it, it executes the decorated &lt;code&gt;__call__&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;You can also pass arguments and return values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cadule&lt;/span&gt;

&lt;span class="nd"&gt;@cadule&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Messing up &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mess_up_things&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;mess_up_things&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the database&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Messing up the database!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When to Use It
&lt;/h2&gt;

&lt;p&gt;Cadule is particularly useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single-purpose modules&lt;/strong&gt;: When a module's main purpose is to expose one function&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DSL and fluent interfaces&lt;/strong&gt;: Creating more natural-looking APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripts and utilities&lt;/strong&gt;: Making command-line tools more intuitive&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Github: &lt;a href="https://github.com/aisk/cadule" rel="noopener noreferrer"&gt;https://github.com/aisk/cadule&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Bringing timeout to macOS Without Installing All of GNU Coreutils</title>
      <dc:creator>Maple</dc:creator>
      <pubDate>Sun, 21 Sep 2025 07:21:11 +0000</pubDate>
      <link>https://forem.com/maple/bringing-timeout-to-macos-without-installing-all-of-gnu-coreutils-22lj</link>
      <guid>https://forem.com/maple/bringing-timeout-to-macos-without-installing-all-of-gnu-coreutils-22lj</guid>
      <description>&lt;p&gt;Not long ago, I was writing a shell script on macOS and ran into a small but surprising issue: there was no built-in &lt;code&gt;timeout&lt;/code&gt; command. If you’ve spent time on Linux, you’ve probably taken &lt;code&gt;timeout&lt;/code&gt; for granted, it’s part of GNU coreutils and is incredibly handy when you want to limit how long a command can run.&lt;/p&gt;

&lt;p&gt;On macOS, though, the situation is different. macOS ships with a BSD-flavored set of core utilities, and GNU’s version of &lt;code&gt;timeout&lt;/code&gt; simply isn’t there. My first instinct was to install GNU coreutils through Homebrew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;coreutils
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does work—but it comes with a catch. To avoid conflicts with the BSD commands that ship with macOS (like &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;cat&lt;/code&gt;), Homebrew installs GNU coreutils under names prefixed with a &lt;code&gt;g&lt;/code&gt;. So instead of &lt;code&gt;ls&lt;/code&gt;, you get &lt;code&gt;gls&lt;/code&gt;; instead of &lt;code&gt;timeout&lt;/code&gt;, you get &lt;code&gt;gtimeout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’s fine if you’re working in a personal environment, but it creates headaches for writing portable shell scripts. If I write a script that calls &lt;code&gt;gtimeout&lt;/code&gt; on macOS, it won’t run out-of-the-box on Linux, where the command is just &lt;code&gt;timeout&lt;/code&gt;. Homebrew does provide a way to remove the &lt;code&gt;g&lt;/code&gt; prefix, but that’s a pretty heavy-handed solution—suddenly your system’s default &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt;, and other tools are replaced by GNU versions, which can break expectations in subtle ways.&lt;/p&gt;

&lt;p&gt;So, instead of going down that path, I decided to build a standalone &lt;code&gt;timeout&lt;/code&gt; command that behaves like GNU’s version but doesn’t bring along the entire coreutils package. This way, you can install just what you need without worrying about side effects.&lt;/p&gt;

&lt;p&gt;The project is open source and lives here:&lt;br&gt;
👉 &lt;a href="https://github.com/aisk/timeout" rel="noopener noreferrer"&gt;https://github.com/aisk/timeout&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And to make installation easier, I also set up a Homebrew tap. You can install it directly with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;aisk/homebrew-tap/timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it—you’ll get a drop-in replacement for &lt;code&gt;timeout&lt;/code&gt;, without the &lt;code&gt;g&lt;/code&gt; prefix, and without swapping out your other system tools.&lt;/p&gt;

&lt;p&gt;I built this mainly to solve my own problem, but if it helps you too, I’d really appreciate a star on the GitHub repo. Every bit of support means a lot and helps me keep the project alive.&lt;/p&gt;

</description>
      <category>homebrew</category>
      <category>cli</category>
      <category>linux</category>
      <category>bash</category>
    </item>
    <item>
      <title>Build and Modify Linux System Image for C-sky based Gx6605s Board</title>
      <dc:creator>Maple</dc:creator>
      <pubDate>Sun, 11 Aug 2024 12:02:03 +0000</pubDate>
      <link>https://forem.com/maple/build-and-modify-linux-system-image-for-c-sky-based-gx6605s-board-3i7j</link>
      <guid>https://forem.com/maple/build-and-modify-linux-system-image-for-c-sky-based-gx6605s-board-3i7j</guid>
      <description>&lt;p&gt;The Gx6605s is a very cheap development board (39 Chinese Yuan with free shipping), which has a C-sky instruction set based CPU. The support for Linux kernel, GCC, and even Greenlet, is upstreamed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0woalkicrtw0z6q0fbi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0woalkicrtw0z6q0fbi.jpg" alt="Gx6605s development board" width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have a Gx6605s board which I bought years ago, and I only tried to run it with C-sky's official system image. It's a Buildroot-based Linux system, and there is no package manager for it. I can't install more packages / software on it, thus it is not useful for me, so I quickly lost interest in it.&lt;/p&gt;

&lt;p&gt;When I found it in a box after moving to another city, I thought maybe I could do something with it, so I decided to give it a try.&lt;/p&gt;

&lt;p&gt;But sadly, the company behind the development of the dev board and instruction set has abandoned the development for the dev board and C-sky instructions, and has continued their work on RiscV.&lt;/p&gt;

&lt;p&gt;They have a Buildroot fork on GitLab which can produce the root file system for the image, but the support for this board and C-sky instruction set was dropped some time ago. Now this repo can't produce a system image for Gx6605s.&lt;/p&gt;

&lt;p&gt;I made a dig into the repo and found that there is a branch called &lt;a href="https://gitlab.com/c-sky/buildroot/-/tree/master_bakup" rel="noopener noreferrer"&gt;&lt;code&gt;master_backup&lt;/code&gt;&lt;/a&gt;, which was once their development branch capable of building C-sky CPU based system images. The build processes were run on GitLab Pipeline. So, you can fork this repo, and then run the GitLab Pipeline on the web page to get the generated system image.&lt;/p&gt;

&lt;p&gt;But unfortunately, there is a file that Gx6605s Buildroot depended on, hosted at &lt;a href="https://github.com/c-sky/tools" rel="noopener noreferrer"&gt;https://github.com/c-sky/tools&lt;/a&gt;, which has been deleted and changed to a new unrelated repo. Luckily, I found a mirror site that has this repo, so you can just apply this patch to get the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/package/csky-debug/csky-debug.mk b/package/csky-debug/csky-debug.mk
index 241755019e..a318247ebf 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/package/csky-debug/csky-debug.mk
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/package/csky-debug/csky-debug.mk
&lt;/span&gt;&lt;span class="p"&gt;@@ -6,7 +6,7 @@&lt;/span&gt;
&lt;span class="err"&gt;
&lt;/span&gt; CSKY_DEBUG_VERSION = V4.2.0-tmp-20170411
 CSKY_DEBUG_SOURCE = DebugServerConsole-linux-x86_64-$(CSKY_DEBUG_VERSION).tar.gz
&lt;span class="gd"&gt;-CSKY_DEBUG_SITE = https://github.com/c-sky/tools/raw/master
&lt;/span&gt;&lt;span class="gi"&gt;+CSKY_DEBUG_SITE = https://isrc.iscas.ac.cn/gitlab/mirrors/github.com/c-sky_tools/-/raw/master
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; define HOST_CSKY_DEBUG_INSTALL_CMDS
        mkdir -p $(HOST_DIR)/csky-debug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, you can build a system image for the Gx6605s board, and it's a little newer than the one provided on the C-sky official site. However, I want to modify the system image by adding more packages, such as MicroPython and even a GCC compiler. Therefore, I need to build it locally.&lt;/p&gt;

&lt;p&gt;Take a look at the &lt;a href="https://gitlab.com/c-sky/buildroot/-/blob/master_bakup/.gitlab-ci.yml" rel="noopener noreferrer"&gt;&lt;code&gt;.gitlab-ci.yml&lt;/code&gt;&lt;/a&gt;. I believe it's using a Docker image called &lt;code&gt;maohan001/ubuntu-buildroot&lt;/code&gt; as the building environment. Please note that since they dropped support for the C-sky instruction set, if you want to build the image in the future, please pull and backup this image. It's not Dockerfile-based, so we don't know how to reproduce it.&lt;/p&gt;

&lt;p&gt;I've never used Buildroot before, but from the GitLab Pipeline's log and the &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, I assume one should run &lt;code&gt;make {config-name}&lt;/code&gt; inside the Docker image with the &lt;code&gt;buildroot&lt;/code&gt; root path to create a configuration for the specified development board. Then, run &lt;code&gt;make&lt;/code&gt; to download all necessary files and build the compiler, kernel, libc, and userland.&lt;/p&gt;

&lt;p&gt;So, for the Gx6605s, we should use &lt;code&gt;$ make csky_610_gx6605s_4.9_uclibc_br_defconfig&lt;/code&gt;, and then &lt;code&gt;$ make&lt;/code&gt;. You will get a &lt;code&gt;usb.img.xz&lt;/code&gt; in the &lt;code&gt;output/images&lt;/code&gt; folder. This should be exactly what you got in the GitLab Pipeline.&lt;/p&gt;

&lt;p&gt;And as the &lt;code&gt;Buildroot&lt;/code&gt;'s official website says, now we can run &lt;code&gt;$ make menuconfig&lt;/code&gt; to customize the build process, such as adding MicroPython to the system.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
