#Writing Karaka॥
A tutorial that walks from your first program to pipeline-based error handling. Each section references an example in examples/ so you can run full programs as you read.
#First program
Create namaste.karaka:
प्रारम्भः
लिख("नमस्ते, लोक!")
नाम = "कारक"
लिख("भाषायाः नाम: {नाम}")
समाप्तRun it:
karaka run namaste.karakaOutput:
नमस्ते, लोक!
भाषायाः नाम: कारकEvery runnable program has exactly one प्रारम्भः block. Statements inside execute top to bottom. लिख prints one line; it takes exactly one argument.
See examples/01-namaste.karaka.
#The REPL
karaka replकारक> लिख("नमस्ते")
नमस्ते
कारक> चल क = ०; क = क + १; क
1The REPL keeps globals alive across inputs. Semicolons separate statements on a single line. Type :त्यज or :quit to exit. शून्यम् values are not echoed back.
#Typing Devanagari
Karaka source files are plain UTF-8 text. You can type Devanagari in any editor, terminal, or the REPL once you have an input method configured at the OS level. That is the recommended path — it works everywhere without any plugin and lets you use the full script correctly.
#OS input method setup
macOS
- Open System Settings > Keyboard > Input Sources, click +.
- Find Hindi and select Hindi — Transliteration.
- Switch to it with the menu bar flag or Ctrl+Space.
- Type phonetically:
namasteproduces नमस्ते,karmaproduces कर्म. The macOS scheme is close to ITRANS; most words work as you expect.
Windows
- Settings > Time & Language > Language > Add a language > Hindi.
- Choose the Hindi Phonetic keyboard (Microsoft Indic Language Input Tool), or install Microsoft Indic Input 3 for more coverage.
- Switch with Windows+Space.
Linux
Install ibus-m17n and enable the hi-itrans input method:
sudo apt install ibus-m17n # Debian / Ubuntu
ibus-setup # add hi (m17n) → hi-itransMobile (Android / iOS)
Install Gboard, add Hindi as a language, and use the transliteration keyboard. Type phonetically; Gboard offers candidate selection.
#karaka translit workflow
The fastest way to write a new Karaka program is to draft it in Roman, then convert it in one step:
karaka translit roman-draft.txt > program.karaka
karaka run program.karakakaraka translit reads a file (or stdin with -) and writes Devanagari to stdout. Conversion is word-wise: keyword dictionary first (case-insensitive), ITRANS engine for everything else. Words that are already Devanagari pass through unchanged.
String literals and comments are preserved by default. The content of "...", '...', line comments (# …), and block comments (#= … =#) is not transliterated unless you add --sarvam:
karaka translit roman-draft.txt --sarvam > program.karakaUse --sarvam when your string values should also be Devanagari. The default behaviour is intentional: a string like "admin" is usually an API value, not a Sanskrit word, and should stay Roman.
#VS Code extension flow
With the vscode-karaka extension installed, you do not need an OS input method for common keywords. As you type a Latin run in a .karaka file the completion popup shows:
- Keyword matches — curated spellings for all structure keywords, role labels, constants, and stdlib names. Press Tab or Enter to insert.
- अनुवर्णनम् candidate — a general ITRANS transliteration for any word not in the keyword table. Sorted below keyword matches.
The command Karaka: Transliterate word at cursor (Ctrl+Shift+P → Karaka: Transliterate) converts the Latin word under the cursor in place. Keyword table wins on an exact match; otherwise the ITRANS engine is used.
The extension also ships code snippets for the most common constructs. Type the prefix and press Tab to expand:
| Prefix | Expands to |
|---|---|
kriya | Role-declared क्रिया skeleton with अपेक्षते and फलम् |
niyama | नियमः skeleton with प्रमाणम् + अनुमतम्/निषिद्धम् branches |
jati | जातिः with two गुणः fields |
yadi | यदि / अन्यथा / समाप्त conditional block |
prarambha | प्रारम्भः entry block |
pratyekam | प्रत्येकम् loop over a range or list |
#Playground toggle
The browser playground at site/playground/ has an अनुवर्णनम् (type Latin) checkbox in the toolbar. When checked, pressing Space, Enter, or punctuation after a Latin word replaces it with Devanagari automatically. See the playground README for the limitation note.
#ITRANS cheat sheet
The scheme used by the VS Code extension and the playground toggle:
#Vowels
| Latin | Independent | Matra | Example |
|---|---|---|---|
a | अ | (inherent) | karma → कर्म |
aa or A | आ | ा | kartaa → कर्ता |
i | इ | ि | yadi → यदि |
ii or I | ई | ी | kriyaa → क्रिया |
u | उ | ु | — |
uu or U | ऊ | ू | shuunyam → शून्यम् |
R^i | ऋ | ृ | — |
e | ए | े | namaste → नमस्ते |
ai | ऐ | ै | — |
o | ओ | ो | — |
au | औ | ौ | — |
#Consonants
| Latin | Devanagari | Latin | Devanagari | Latin | Devanagari | ||
|---|---|---|---|---|---|---|---|
k | क | kh | ख | g | ग | ||
gh | घ | ~N | ङ | ch or c | च | ||
Ch or chh | छ | j | ज | jh | झ | ||
~n | ञ | T | ट | Th | ठ | ||
D | ड | Dh | ढ | N | ण | ||
t | त | th | थ | d | द | ||
dh | ध | n | न | p | प | ||
ph or f | फ | b | ब | bh | भ | ||
m | म | y | य | r | र | ||
l | ल | v or w | व | sh | श | ||
Sh or S | ष | s | स | h | ह | ||
L | ळ | kSh or x | क्ष | j~n or GY | ज्ञ |
#Marks and digits
| Latin | Output | Name |
|---|---|---|
M or .m | ं | anusvara |
H | ः | visarga |
.N | ँ | candrabindu |
.a | ऽ | avagraha |
0–9 | ०–९ | Devanagari digits |
- | - | samasa hyphen (passes through) |
#Worked examples
khaatam=kh+aa+t+a+m(end of word) → ख+ा+त+inherent+म+् = खातम्sheShaH=sh+e+Sh+a+H→ श+े+ष+inherent+ः = शेषः
#Values and bindings
#Bindings
A plain assignment creates an immutable binding:
अ = ५चल declares a mutable binding:
चल ब = ३
ब = ब + १नित्य explicitly marks immutability and accepts a type annotation:
नित्य गति: संख्या = ९.८Attempting to reassign a non-चल binding is a runtime error (K2006).
#Numeric rules
Integers are i64. Decimals are f64. The annotation संख्या accepts both.
Division / always returns a decimal (दशांशः), even with integer operands:
५ / ४ # → 1.25Modulo % is integer-only and errors on non-integer operands:
५ % ४ # → 1Both Devanagari digits (१२३) and ASCII digits (123) are legal and equivalent:
लिख(१२३ == 123) # → सत्यA literal may not mix Devanagari and ASCII digits in one token (K0008).
Integer overflow is a runtime error (K2018). Decimal overflow produces अनन्तम् (IEEE 754 infinity); अनन्तम् - अनन्तम् produces असंख्यम् (NaN).
#Strings and interpolation
String literals use double quotes. An expression inside {...} is interpolated:
नाम = "अर्जुनः"
लिख("नमस्ते, {नाम}!") # → नमस्ते, अर्जुनः!दीर्घता gives the grapheme-cluster length:
वाक्यम् = "क्षेत्रम्"
लिख(वाक्यम्.दीर्घता) # grapheme clusters, not bytes#Strict truthiness
Conditions require a सत्यता (Bool) value. There is no truthiness coercion. Writing यदि ५ तदा ... is a K2002 type error at runtime.
The boolean constants are सत्य and मिथ्या. Negation uses the keyword न:
यदि न (क == ०) तदा
लिख("nonzero")
समाप्त#Control flow
See examples/02-ganana.karaka.
#यदि / तदा / अन्यथा
यदि संख्या % ३ == ० च संख्या % ५ == ० तदा
लिख("त्रिपञ्चकम्")
अन्यथा यदि संख्या % ३ == ० तदा
लिख("त्रिकम्")
अन्यथा यदि संख्या % ५ == ० तदा
लिख("पञ्चकम्")
अन्यथा
लिख("{संख्या}")
समाप्ततदा is optional when the condition ends a line. The अन्यथा यदि chain must appear on the same line as the closing समाप्त of the previous arm. A newline before यदि nests instead of chains.
#यावत्
चल क = ३
यावत् क > ०
लिख(क)
क = क - १
समाप्तThe condition must be सत्यता; any other type is a K2002 error.
#प्रत्येकम्
Iterates over a range or a list:
प्रत्येकम् संख्या मध्ये १..१०
लिख(संख्या)
समाप्तRanges are inclusive at both ends. A reversed range (३..१) is empty. List iteration takes a snapshot of the list before the loop begins, so mutations inside the loop do not affect iteration.
#Collections
See examples/03-suchi-kosha.karaka.
#सूची (list)
चल स = ["अ", "आ", "इ"]
# append — method sugar or explicit role call
स.योजनम्(कर्म: "ई")
# length, first, last
लिख(स.दीर्घता)
लिख(स.प्रथमः)
लिख(स.अन्तिमः)
# 0-based index read
लिख(स[१])
# 0-based index write (requires चल binding)
स[०] = "ॐ"
# membership test
लिख("आ" मध्ये स) # → सत्य#कोशः (map)
शब्दकोशः = {अर्थः: "meaning", उदाहरणम्: "example"}
# member read by identifier key
लिख(शब्दकोशः.अर्थः)
# index read by string key
लिख(शब्दकोशः["उदाहरणम्"])
# index write adds or updates
चल क = {क: १}
क["ख"] = २#Container mutability
चल controls whether a binding can be rebound to a different container. The container's interior is always mutable — you can index-assign a list or map regardless of whether the binding is चल. The same rule applies to जातिः instances: चल-declared fields can be reassigned on the instance after construction.
#Actions (क्रिया)
See examples/04-kriya.karaka and examples/06-hastantaranam.karaka.
#Positional क्रिया
Arguments bind by position, no role labels required:
क्रिया योगः(अ, ब)
फलम् अ + ब
समाप्त
लिख(योगः(३, ४)) # → 7Use positional when the function is a pure computation over values of the same kind (math, string helpers, predicates).
#Role-declared क्रिया
Roles name the semantic function of each argument. Declare them in अपेक्षते:
क्रिया हस्तान्तरणम्
अपेक्षते
अपादानम्: खातम् # source (ablative: from)
सम्प्रदानम्: खातम् # recipient (dative: to)
कर्म: संख्या # amount (accusative)
यदि अपादानम्.शेषः < कर्म तदा
दोषः "अपर्याप्तः शेषः"
समाप्त
अपादानम्.शेषः = अपादानम्.शेषः - कर्म
सम्प्रदानम्.शेषः = सम्प्रदानम्.शेषः + कर्म
समाप्त
हस्तान्तरणम्(अपादानम्: खाता१, सम्प्रदानम्: खाता२, कर्म: ५००)Call sites must use the role labels. This makes direction errors impossible to write silently.
#The six कारक
| रोल | अर्थ |
|---|---|
| कर्ता | the agent — who acts |
| कर्म | the patient — what is acted upon |
| करणम् | the instrument — how the action is performed |
| सम्प्रदानम् | the recipient — to whom |
| अपादानम् | the source — from where |
| अधिकरणम् | the locus — where/on which (also the receiver in method calls) |
Use roles whenever the क्रिया involves more than one entity or any notion of authority or direction. Roles at every call site mean the intent is readable without looking at the definition.
#Optional arguments and defaults
Mark a role optional with ?. Provide a default expression after =:
क्रिया नमनम्
अपेक्षते
कर्ता: वाक्यम्
सम्प्रदानम्: वाक्यम् = "लोक"
फलम् "नमस्ते, {सम्प्रदानम्}! अहं {कर्ता} अस्मि।"
समाप्त
लिख(नमनम्(कर्ता: "अर्जुनः"))
# → नमस्ते, लोक! अहं अर्जुनः अस्मि।Default expressions evaluate against the global scope at call time.
#Default कर्म
A क्रिया that declares exactly one role labeled कर्म can be called with a single unlabeled argument:
क्रिया द्विगुणम्
अपेक्षते
कर्म: संख्या
फलम् कर्म * २
समाप्त
लिख(द्विगुणम्(५)) # unlabeled; fills कर्म
लिख(द्विगुणम्(कर्म: ५)) # explicit; identical result#Method sugar through अधिकरणम्
A क्रिया with an अधिकरणम् role can be called with dot syntax. The object to the left of the dot is bound as अधिकरणम्. Inside the body, अत्र is an alias for अधिकरणम्:
क्रिया निक्षेपः
अपेक्षते
अधिकरणम्: खातम्
कर्म: संख्या
अत्र.शेषः = अत्र.शेषः + कर्म
फलम् अत्र
समाप्त
क.निक्षेपः(कर्म: ५००)
# equivalent to:
निक्षेपः(अधिकरणम्: क, कर्म: ५००)#Errors as values
See examples/10-doshah.karaka and examples/06-hastantaranam.karaka.
#Creating a दोषः
दोषः "message" returns immediately from the enclosing function with a wrapped error value. It is not an exception; execution continues in the caller:
क्रिया भाजनम्
अपेक्षते
कर्म: संख्या
करणम्: संख्या
यदि करणम् == ० तदा
दोषः "शून्येन भागः"
समाप्त
फलम् कर्म / करणम्
समाप्त#Checking for a दोषः
Use the अस्ति type predicate:
फ = भाजनम्(कर्म: १०, करणम्: ०)
यदि फ दोषः अस्ति तदा
लिख(फ.विवरणम्) # extract the message string
समाप्त#Recovery pattern
फ = भाजनम्(कर्म: ७, करणम्: ०)
चल परिणामः = ०
यदि फ दोषः अस्ति तदा
परिणामः = -१
अन्यथा
परिणामः = फ
समाप्त#Uncaught दोषः
A दोषः statement at the top level — or one that propagates through all callers without being caught — triggers K2016 and exits with code 1. The REPL renders it as a value and stays alive.
#Pipelines short-circuit
Once a दोषः is in a pipeline, all remaining stages are skipped. See the next section.
#Rules and evidence
See examples/07-niyama.karaka.
नियमः defines a policy rule that returns one of three values:
| निर्णयः | अर्थ |
|---|---|
| अनुमतम् | allowed |
| निषिद्धम् | denied |
| अनिश्चितम् | undecided |
नियमः प्रवेश-अनुमतिः
अपेक्षते
कर्ता: उपयोगकर्ता
यदि कर्ता.सक्रियः == "नहि" तदा
प्रमाणम् "खाता-निष्क्रियः"
फलम् निषिद्धम्
समाप्त
यदि कर्ता.भूमिका == "व्यवस्थापकः" तदा
प्रमाणम् "व्यवस्थापक-भूमिका"
फलम् अनुमतम्
अन्यथा
प्रमाणम् "अपर्याप्त-अधिकारः"
फलम् निषिद्धम्
समाप्त
समाप्तप्रमाणम् "string" accumulates evidence on the active rule frame. Every branch should record a reason. प्रमाणम् outside a नियमः body is K2011.
Read accumulated evidence with .प्रमाणानि:
अनुज्ञा = प्रवेश-अनुमतिः(कर्ता: व१)
लिख(अनुज्ञा) # → अनुमतम्
लिख(अनुज्ञा.प्रमाणानि) # → [व्यवस्थापक-भूमिका]When a दोषः escapes from inside a नियमः, it propagates to the caller as a दोषः value.
#Pipelines
See examples/08-tatah.karaka.
ततः chains steps left to right. Each stage receives the previous result as its कर्म:
# text pipeline
परिणामः = "सूर्य-चन्द्र-तारा" ततः विभजनम्(करणम्: "-") ततः संयोजनम्(करणम्: " | ")
लिख(परिणामः) # → सूर्य | चन्द्र | तारा
# numeric pipeline
संख्या = ५ ततः द्विगुणम् ततः योजय-दशA stage may be a plain name (the value becomes the single argument) or a partial call with all roles except कर्म supplied.
If any stage returns a दोषः, all later stages are skipped. The दोषः is the pipeline result:
क्रिया विघ्नः
अपेक्षते
कर्म: संख्या
दोषः "रुकावट"
समाप्त
फ = १ ततः द्विगुणम् ततः विघ्नः ततः साक्षी
यदि फ दोषः अस्ति तदा
लिख(फ.विवरणम्) # साक्षी never ran
समाप्त#Standard library stages
| फलम् | कर्म | करणम् | विवरण |
|---|---|---|---|
विभजनम् | वाक्यम् | वाक्यम् separator | split string |
संयोजनम् | सूची | वाक्यम् separator | join list |
योजनम् | any | — | append to list (अधिकरणम् = the list) |
पठनम् | वाक्यम् path | — | read file to string (CLI only) |
लेखनम् | वाक्यम् content | — | write string to file (CLI only; अधिकरणम् = path) |
The गणितम् namespace holds: वर्गमूलम्, घातः, निरपेक्षम्, न्यूनतमम्, अधिकतमम्. See examples/09-ganitam.karaka.
#The trace
Run with --अभिलेख to print an audit log to stderr:
karaka run examples/07-niyama.karaka --अभिलेखEach traced invocation appears as:
प्रवेश-अनुमतिः ⟨कर्ता: व्यवस्थापकः⟩ → अनुमतम् [व्यवस्थापक-भूमिका]
लिख ⟨कर्म: अनुमतम्⟩ → फलम् शून्यम्Format: नाम ⟨roles⟩ → result. For rules: result [evidence...]. For actions: फलम् result.
Only role-declared क्रिया and all नियमः invocations are traced. Positional-only क्रिया and जाति construction are not.
Add --jsonl for machine-readable output, one JSON object per line:
karaka run examples/07-niyama.karaka --अभिलेख --jsonl{"seq":0,"depth":0,"kind":"niyama","name":"प्रवेश-अनुमतिः","roles":{"कर्ता":"व्यवस्थापकः"},"phala":"अनुमतम्","pramanani":["व्यवस्थापक-भूमिका"]}JSONL keys: seq (invocation order), depth (call depth, 0-based), kind (kriya/niyama/builtin), name, roles (object), phala or dosha (one or the other), pramanani (array, niyama records only).
#Style guide
#Names
Use समास compound names for multi-concept identifiers: धन-हस्तान्तरणम्, प्रवेश-अनुमतिः. A hyphen is legal inside an identifier when the character after it is a Devanagari letter. Do not use underscores — they are not part of the character set.
Prefer Devanagari digits in Devanagari code: write १..१०, not 1..10.
Name rules after the permission they grant, not what they check: प्रवेश-अनुमतिः rather than भूमिका-जाँचः.
#क्रिया size
Keep क्रिया small. If a body needs more than a few conditions, split it. Each role-declared क्रिया should do one thing that its roles describe completely.
#Authority in roles, not comments
Roles state authority at every call site. Do not write a comment explaining direction when अपादानम् and सम्प्रदानम् already express it. Reserve comments for the non-obvious.
#Reserved words as identifiers
Several Sanskrit words are reserved keywords in Karaka and cannot be used as identifiers. Two common traps:
नis the negation keyword. Useअंकः,गणना, or another name for a loop counter or number variable.प्रमाणम्is a statement keyword. It cannot name a binding.
The full list of keywords is: यदि, तदा, अन्यथा, यावत्, प्रत्येकम्, मध्ये, च, वा, न, फलम्, दोषः, प्रमाणम्, ततः, क्रिया, नियमः, जातिः, गुणः, चल, नित्य, अपेक्षते, अत्र, प्रारम्भः, समाप्त, सत्य, मिथ्या, शून्यम्, अनुमतम्, निषिद्धम्, अनिश्चितम्.
#Definitions at top level
Definitions (क्रिया, नियमः, जातिः) must appear at the top level, not inside प्रारम्भः. The definition keyword and its name must be on the same line.
# correct
क्रिया योगः(अ, ब)
फलम् अ + ब
समाप्त
# wrong — nested inside प्रारम्भः
प्रारम्भः
क्रिया योगः(अ, ब) # parse error
फलम् अ + ब
समाप्त
समाप्त#When to use roles vs positional
Use positional arguments for pure math or transformations where every argument has the same semantic weight:
क्रिया वर्गः(अ)
फलम् अ * अ
समाप्तUse roles when any of these apply:
- The function involves more than one entity of different kinds.
- There is a notion of source, destination, authority, or instrument.
- You want to be certain that swapping arguments produces a compile-time or call-site error.
- The function is part of policy or audit logic.
In almost every non-trivial business function, use roles. The extra labels pay for themselves the first time someone reads the call site six months later.