Morloc Demo - CLI composition

by Zebulun Arendsee

In this post, I will describe how to use Morloc to transform simple functional libraries into composable CLI tools, HTTP/TCP APIs, and UNIX daemons. I’m focusing on one aspect of Morloc. Future posts will explore the underlying polyglot functional programming language, type system, and data marshalling systems.

I will start by defining one simple Morloc module that prints a calendar to the terminal:

module cal (cal, CalConfig)

import root-py

--' Calendar display options
--' unroll: true
--' arg: --cal-config
record CalConfig where

  --' Start week on Monday
  --' true: --monday
  --' false: --sunday
  --' default: true
  monday_first :: Bool

  --' Number of months to show
  --' arg: -n/--months
  --' default: 1
  num_months :: Int

  --' Highlight today
  --' true: -t/--show-today
  --' default: false
  highlight_today :: Bool

  --' Show week numbers
  --' true: -w/--week-numbers
  --' default: false
  show_week_numbers :: Bool

record Py => CalConfig = "dict"

--' Show a monthly calendar
--' name: cal
--' return: Formatted calendar text
cal :: CalConfig -> Str

source Py from "cal.py" ("show_calendar_wrapper" as cal)

This code exports a function that takes a record of parameters and returns a string showing the calendar. The Morloc program sources Python code from the cal.py file. This file contains idiomatic Python — with no Morloc-specific dependencies — that defines a function for generating a calender string given a config record. The record passed to this function is annotated in the Morloc script with descriptions for each field. These descriptions inform the generation of the CLI tool. For a full overview of the Morloc syntax, see the docs.

This program can be compiled with morloc make to generate the executable cal. You can install and build this tool with morloc install --build ./cal . This command both installs the Morloc module and creates the executable cal.

We can access the usage info for this executable:

$ cal -h
Usage: cal [OPTION...]
Show a monthly calendar

Nexus options:
  --print          Pretty-print output for human consumption
  --output-file    Print to this file instead of STDOUT
  --output-form    Output format [json|mpk|voidstar]

Daemon mode:
  --daemon         Run as a long-lived daemon
  --http-port PORT Listen on HTTP port
  --port PORT      Listen on TCP port
  --socket PATH    Listen on UNIX socket

Group arguments:
  CalConfig: Calendar display options
    --cal-config CalConfig
    --sunday
        default: true
        Start week on Monday
    -n, --months Int
        default: 1
        Number of months to show
    -t, --show-today
        default: false
        Highlight today
    -w, --week-numbers
        default: false
        Show week numbers

Return: Str
  Formatted calendar text

We can call this command like so:

$ cal --print
       February 2026
 Mon Tue Wed Thu Fri Sat Sun
----------------------------
                           1
   2   3   4   5   6   7   8
   9  10  11  12  13  14  15
  16  17  18  19  20  21  22
  23  24  25  26  27  28

The --print option tells the executable to pretty print the output rather than dumping raw JSON.

The Python file cal.py contains the following content (with the body of show_calendar elided for brevity):

import calendar
from datetime import date

def show_calendar_wrapper(kwargs):
    return show_calendar(**kwargs)

def show_calendar(
      num_months : int = 1,
      monday_first : bool = True,
      highlight_today : bool = True,
      show_week_numbers : bool = True
    ) -> str:
    ...

Note that this is pure idiomatic Python code – no Morloc-specific dependencies or idioms. The Morloc compiler generates the wiring needed to pass data to the Python function.

In addition to the CLI, we can also launch the program as an HTTP server:

$ cal --daemon --http-port 8080 &
[1] 129
$ curl -s localhost:8080/health
{"status":"ok","result":[true]}
$ curl -s localhost:8080/discover | jq .
{
  "status": "ok",
  "result": {
    "name": "cal",
    "version": 1,
    "commands": [
      {
        "name": "cal",
        "type": "remote",
        "return_type": "Str",
        "return_schema": "<str>s",
        "args": [
          {
            "kind": "grp",
            "metavar": "CalConfig",
            "desc": "Calendar display options"
          }
        ],
        "desc": "Show a monthly calendar"
      }
    ]
  }
}
$ curl -s -X POST localhost:8080/call/cal -d '[{"monday_first":true,"num_months":1,"highlight_today":true,"show_week_numbers":false}]' | jq -r '.result'
       February 2026
 Mon Tue Wed Thu Fri Sat Sun
----------------------------
                           1
   2   3   4   5   6   7   8
   9  10  11  12  13  14  15
  16  17  18  19  20  21  22
  23  24  25  26  27  28

We can similarly build TCP and UNIX domain socket daemons. Or build one server that listens over all three:

$ cal --daemon \
      --http-port 8080 \
      --port 9001 \
      --socket /tmp/cal.sock

Now suppose we have a second module that prints the current weather:

module weather (weather, WeatherConfig)

import root-py

--' Weather display options
--' unroll: true
--' arg: --weather-config
record WeatherConfig where
  --' City or location name
  --' arg: -l/--location
  --' default: "New York"
  --' literal: true
  location :: Str

  --' Use metric units
  --' true: --metric
  --' false: --imperial
  --' default: true
  metric :: Bool

  --' Show compact one-line output
  --' true: --compact
  --' false: --full
  --' default: false
  compact :: Bool

record Py => WeatherConfig = "dict"

--' Get current weather for a location
--' name: weather
--' return: Weather report
weather :: WeatherConfig -> Str

source Py from "weather.py" ("get_weather" as weather)

Both of these modules can be used independently as CLI tools or server/daemons. But they are also Morloc modules and can be imported into other Morloc programs. To demonstrate this, let’s define a third module, creatively named foo, that imports both and exports all their terms. This module also defines a new Morloc function that reuses the string output of the cal and weather functions:

module foo (cal, weather, briefing)

import root-py
import cal
import weather

source Py from "briefing.py" ("format_briefing")

format_briefing :: Str -> Str -> Str

--' Morning briefing with calendar and weather
--' return: Combined calendar and weather report
briefing :: CalConfig -> WeatherConfig -> Str
briefing c w = format_briefing (cal c) (weather w)

This new module can be compiled into a new executable (or server/daemon) that composes the original two modules. The three functions exported from this new module are translated into three subcommands.

$ foo -h
Usage: foo [OPTION...] COMMAND [ARG...]

Nexus options:
  -h, --help           Print this help message
  -p, --print          Pretty-print output for human consumption
  -o, --output-file    Print to this file instead of STDOUT
  -f, --output-format  Output format [json|mpk|voidstar]

Daemon mode:
  --daemon             Run as a long-lived daemon
  --http-port PORT     Listen on HTTP port
  --port PORT          Listen on TCP port
  --socket PATH        Listen on Unix socket

Commands (call with -h/--help for more info):
  cal       Show a monthly calendar
  weather   Get current weather for a location
  briefing  Morning briefing with calendar and weather

The usage statement for foo inherits documentation through its types, here is the usage output for foo briefing:

$ foo briefing -h
Usage: foo briefing [OPTION...]
Morning briefing with calendar and weather

Group arguments:
  CalConfig: Calendar display options
    --cal-config CalConfig
    --sunday
        default: true
        Start week on Monday
    -n, --months Int
        default: 1
        Number of months to show
    -t, --show-today
        default: false
        Highlight today
    -w, --week-numbers
        default: false
        Show week numbers

Group arguments:
  WeatherConfig: Weather display options
    --weather-config WeatherConfig
    -l, --location Str
        default: "New York"
        City or location name
    --imperial
        default: true
        Use metric units
    --compact
        default: false
        Show compact one-line output

Return: Str
  Combined calendar and weather report

Note that these examples were all written in Python, but Morloc allows mixing of supported languages and automatically handles interop.

built on 2026-04-20 01:32:15.730169201 UTC from file 2026-04-19-composable-cli