From ocaml-dev
Enforces OCaml coding style including naming conventions, modularity, refactoring patterns like Option/Result combinators and monadic syntax, plus error handling to improve code quality and reduce complexity.
npx claudepluginhub avsm/ocaml-claude-marketplace --plugin ocaml-devThis skill uses the workspace's default tool permissions.
1. **Interface-First**: Design `.mli` first. Clean interface > 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 first. Clean interface > clever implementation.| Element | Convention | Example |
|---|---|---|
| Files | lowercase_underscores | user_profile.ml |
| Modules | Snake_case | User_profile |
| Types | snake_case, primary type is t | type user_profile, type t |
| Values | snake_case | find_user, create_channel |
| Variants | Snake_case | Waiting_for_input, Processing_data |
Function naming:
find_* returns option (may not exist)get_* returns value directly (must exist)Avoid: Long names with many underscores (get_user_profile_data_from_database_by_id).
(* Before *)
match get_value () with Some x -> Some (x + 1) | None -> None
(* After *)
Option.map (fun x -> x + 1) (get_value ())
Prefer: Option.map, Option.bind, Option.value, Result.map, Result.bind
(* Before - nested matches *)
match fetch_user id with
| Ok user -> (match fetch_perms user with Ok p -> Ok (user, p) | Error e -> Error e)
| Error e -> Error e
(* After *)
let open Result.Syntax in
let* user = fetch_user id in
let+ perms = fetch_perms user in
(user, perms)
(* Before *)
if x > 0 then if x < 10 then "small" else "large" else "negative"
(* After *)
match x with
| x when x < 0 -> "negative"
| x when x < 10 -> "small"
| _ -> "large"
Keep functions small: Under 50 lines. One purpose per function.
Avoid deep nesting: Max 4 levels of match/if. Extract helpers.
High complexity signal: Many branches = split into focused helpers.
(* Bad - high complexity *)
let check x y z =
if x > 0 then if y > 0 then if z > 0 then ... else ... else ... else ...
(* Good - factored *)
let all_positive x y z = x > 0 && y > 0 && z > 0
let check x y z = if not (all_positive x y z) then "invalid" else ...
Use result for recoverable errors. Exceptions only for programming errors.
Never catch-all:
(* Bad *)
try f () with _ -> default
(* Good *)
try f () with Failure _ -> default
Don't silence warnings: Fix the issue, don't use [@warning "-nn"].
| Instead of | Use | Why |
|---|---|---|
Str | Re | Better API, no global state |
Printf | Fmt | Composable, type-safe |
yojson (manual) | jsont | Type-safe codecs |
Abstract types: Keep type t abstract. Expose smart constructors.
(* Good - .mli *)
type t
val create : name:string -> t
val name : t -> string
val pp : t Fmt.t
Avoid generic names: Not Util, Helpers. Use String_ext, Json_codec.
Avoid boolean blindness:
(* Bad *)
let create_widget visible bordered = ...
let w = create_widget true false (* What does this mean? *)
(* Good *)
type visibility = Visible | Hidden
let create_widget ~visibility ~border = ...
Some v -> Some (f v) | None -> Nonepp function on typesObj.magic anywhere