From ocaml-dev
Guides OCaml development with best practices for writing code, designing .mli interfaces, result-based error handling, dune builds, and libraries like eio, fmt, logs, cmdliner, yojson, cohttp-eio.
npx claudepluginhub avsm/ocaml-claude-marketplace --plugin ocaml-devThis skill uses the workspace's default tool permissions.
1. **Interface-First Design**: Design the `.mli` file first. A clean interface matters more than clever implementation.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
.mli file first. A clean interface matters more than clever implementation.dune exclusivelydune fmt before committing (uses ocamlformat)eiofmtlogscmdlineryojsoncohttp-eioEvery .mli file starts with a top-level doc comment:
(** User API
This module provides types and functions for interacting with users. *)
Use [function_name arg1 arg2] is ... pattern:
val is_bot : t -> bool
(** [is_bot u] is [true] if [u] is a bot user. *)
For values, describe what they represent:
type id = string
(** A user identifier. *)
For modules with a central type t, provide these functions where applicable:
| Function | Purpose |
|---|---|
val v : ... -> t | Pure smart constructor (no I/O) |
val create : ... -> (t, Error.t) result | Constructor with side-effects |
val pp : t Fmt.t | Pretty-printer for logging/debugging |
val equal : t -> t -> bool | Structural equality |
val compare : t -> t -> int | Comparison for sorting |
val of_json : Yojson.Safe.t -> (t, string) result | Parse from JSON |
val to_json : t -> Yojson.Safe.t | Serialize to JSON |
val validate : t -> (t, string) result | Validate data integrity |
Keep types abstract (type t) when possible. Expose smart constructors and accessors instead of record fields to maintain invariants.
Use result type for recoverable errors. Reserve exceptions for programming errors (e.g., Invalid_argument).
Define a comprehensive error type in lib/error.ml:
(* In lib/error.mli *)
type t = [
| `Api of string * Yojson.Safe.t
| `Json_parse of string
| `Network of string
| `Msg of string
]
val pp : t Fmt.t
let err_api code fields = Error (`Api (code, fields))
let err_parse msg = Error (`Json_parse msg)
let find_user_id json =
match Yojson.Safe.Util.find_opt "id" json with
| Some (`String id) -> Ok id
| Some _ -> err_parse "Expected string for user ID"
| None -> err_parse "Missing user ID"
try ... with _ -> .... Match specific exceptions.Fmt.failwith:let tls_config =
match Tls.Config.client ~authenticator () with
| Ok config -> config
| Error (`Msg msg) -> Fmt.failwith "Failed to create TLS config: %s" msg
match/if signals need for refactoring.bin/, lib/ui/).Use the logs library with per-module log sources:
let log_src = Logs.Src.create "project_name.module_name"
module Log = (val Logs.src_log log_src : Logs.LOG)
| Level | Use Case |
|---|---|
Log.app | Messages always shown to user (startup) |
Log.err | Handled but critical errors |
Log.warn | Potential issues, operation continues |
Log.info | Informational state messages |
Log.debug | Verbose debugging details |
Log.info (fun m ->
m "Received event: %s" event_type
~tags:(Logs.Tag.add "channel_id" channel_id Logs.Tag.empty))
| Element | Convention | Example |
|---|---|---|
| Files | lowercase_underscores | user_profile.ml |
| Modules | lowercase_underscores | user_profile |
| Primary type | t | type t |
| Identifiers | id | type id = string |
| Values | short_descriptive | find_user, create_channel |
Use labels only when they clarify meaning. Avoid ~f and ~x.
For bin/ applications using cmdliner:
bin/common.mlrun function that initializes the main loop and environment (e.g., Eio loop)Follow Conventional Commits:
Format: type(scope): subject
| Type | Purpose |
|---|---|
feat | New feature |
fix | Bug fix |
docs | Documentation |
style | Formatting |
refactor | Code restructuring |
test | Tests |
chore | Maintenance |
Examples:
feat(api): add support for file uploadsfix(ui): correct channel list rendering bugtest(user): add tests for user profile updates