Monk is a Haskell project that tries to translate Bash scripts into fish. It started as a fun excuse to learn more about shell parsing, typed ASTs, and all the weird corners where Bash and fish do not line up cleanly.
Monk is deliberately conservative: it
translates what it understands, emits warnings for the parts that need a human to look again, and can fail fast in --strict mode when it would rather stop than try its best.
Monk parses Bash with ShellCheck, hands translation output through a typed fish DSL boundary, and renders fish source from the existing pretty-printer backend.
Today it handles a lot of ordinary shell code:
- control flow such as
if,while,for, andcase - functions, arrays, variable assignments, and common special variables
- pipelines, background jobs, and command substitution
- redirections, here-strings, and a chunk of process substitution
- recursive
sourcetranslation for literal source paths
It also has a long tail of best-effort behavior.
If you run Monk on a script, the happy path is:
- it produces fish output
- it tells you where translation got lossy or approximate
- you review the result like generated migration code, not handwritten code
The current source of truth for exact vs best-effort behavior is
docs/design/translator-audit.md.
Constructs that still deserve extra attention include:
- subshell-heavy scripts
- residual
readedge cases outside the exact helper-backed surface set -e/pipefailinteractions in compound shell logic- non-literal
source - option-heavy
trap, uncatchable trap signals,shopt, andcoproc - argument-position or broader
>(...)forms outside the covered Linux redirect-target fixtures
Build it from source:
git clone https://github.com/eessmann/monk.git
cd monk
cabal buildTranslate a script:
monk script.sh > script.fish
monk script.sh --output script.fish
monk script.sh --strict
monk script.sh --recursive --sources separateUseful flags:
--output FILEwrites to a file instead of stdout--strictturns best-effort warnings into failures where supported--quiet-warningssuppresses warning output--recursivefollows literalsource/.--sources inline|separatecontrols how recursive source translation is emitted
Warnings and notes go to stderr.
The public modules are intentionally small:
Monk.Translationfor parse + translate entry pointsMonk.Translation.Typesfor the stable translation/diagnostics contractMonk.AST/Language.Fish.DSLfor the public type-safe Fish construction DSLLanguage.Fish.DSL.Lowerfor explicit DSL-to-backend loweringMonk.AST.Rawfor the explicit raw renderer-backend AST escape hatchMonk.Sourcefor recursive source-graph helpersMonk.Diagnosticsfor warning rendering and confidence summariesMonkas a thin convenience re-export
Monk.AST now exposes smart constructors such as script, stmt,
command, arg, redirect, begin, pipeline, if_, while, for,
switch, and function. The DSL keeps block and pipeline bodies non-empty at
the type level and lowers through the existing pretty-printer backend. Code
that intentionally needs raw constructors should import Monk.AST.Raw; this is
a breaking change for callers that previously used Monk.AST as the raw
constructor surface.
Successful translations now retain the typed Script in TranslationResult.
Call translationStatements when compatibility code needs the lowered raw
renderer-backend statements.
Example:
import Monk.Translation
main :: IO ()
main = do
result <- translateBashFile defaultConfig "script.sh"
case result of
Left err -> print err
Right translation -> do
putStrLn (toString (renderTranslation translation))
print (translationWarnings translation)The normal local loop is:
cabal build
cabal test
MONK_INTEGRATION=1 cabal test
hlint .There is also a bake-off runner for comparing Monk and Babelfish:
cabal run monk-bakeoff -- --compatible --no-benchmark --out-dir /tmp/monk-bakeoffBake-off prerequisites:
babelfishandfishare requiredhyperfineis optional and only needed for benchmark runs- the runner now validates tool paths up front and reports actionable preflight errors or benchmark-skip notes
docs/design/translator-audit.md: fidelity matrix and evidence backlogdocs/design/translator-todo.md: active translator backlogdocs/design/architecture.md: module layout and subsystem boundariesdocs/migration-guide.md: manual cleanup patterns after translationdocs/babelfish-comparison.md: current bake-off workflow and comparison notes