Thank you for your interest in contributing to T! This guide will help you get started.
This project follows a Code of Ethics adapted from the SQLite project. Please review it to understand the detailed ethical guidelines and expected behavior.
Before submitting, check if the issue already exists in GitHub Issues.
When submitting: 1. Use a clear, descriptive title 2. Describe the exact steps to reproduce 3. Provide sample code and data (if applicable) 4. Include your environment (OS, Nix version, OCaml version) 5. Attach error messages and stack traces
Example:
**Bug**: `mean()` returns incorrect result for large floats
**Steps**:
1. Start REPL
2. Run: `mean([1e10, 1e10, 1e10])`
3. Expected: `1e10`, Got: `9.99999e9`
**Environment**:
- OS: Ubuntu 22.04
- Nix: 2.13.3
- OCaml: 4.14.1Before suggesting, consider:
When suggesting: 1. Clearly describe the problem it solves 2. Provide concrete examples 3. Discuss alternatives
We welcome:
Good first issues are tagged with
good-first-issue in GitHub.
Documentation contributions are highly valued:
The repository’s flake.nix provides the complete
development toolchain — OCaml compiler, Menhir parser generator,
development tools, and all library dependencies. There is nothing to
install separately.
git clone https://github.com/b-rodrigues/tlang.git
cd tlang
nix develop
dune build
dune runtestFor coverage builds and deeper environment details, see the Development Guide.
tlang/
├── src/
│ ├── ast.ml # AST definition
│ ├── lexer.mll # Lexer (ocamllex)
│ ├── parser.mly # Parser (Menhir)
│ ├── eval.ml # Evaluator
│ ├── repl.ml # REPL implementation
│ ├── arrow/ # Arrow FFI bindings
│ │ ├── arrow_ffi.ml
│ │ └── arrow_stubs.c
│ ├── ffi/ # Other FFI utilities
│ └── packages/ # Standard library
│ ├── base/ # Errors, NA, assertions
│ ├── core/ # Functional utilities
│ ├── math/ # Math functions
│ ├── stats/ # Statistics
│ ├── to_dataframe/ # DataFrame operations
│ ├── colcraft/ # Data verbs
│ ├── pipeline/ # Pipeline introspection
│ └── explain/ # Debugging tools
├── tests/ # Test suite
│ ├── unit/ # Unit tests
│ ├── golden/ # Golden tests (T vs R)
│ └── examples/ # Example programs
├── docs/ # Documentation
├── examples/ # Example T programs
├── scripts/ # Development scripts
├── flake.nix # Nix flake configuration
├── dune-project # Dune configuration
└── Makefile # Convenience targets
Follow standard OCaml conventions:
Naming:
snake_case for functions and variablesPascalCase for modules and typesSCREAMING_CASE for constantsFormatting:
(* Use ocamlformat for automatic formatting *)
let eval env expr =
match expr with
| Int n -> VInt n
| Float f -> VFloat f
| Ident name -> Environment.lookup env name
| BinOp (op, left, right) ->
let v_left = eval env left in
let v_right = eval env right in
eval_binop op v_left v_right
| _ -> failwith "Not implemented"Comments:
(* OCaml comments *) for implementation notesPattern Matching:
_ for catch-all only when intentionalExample programs should demonstrate best practices:
-- Good: Clear variable names, explicit NA handling
customers = read_csv("data.csv", clean_colnames = true)
avg_age = mean(customers.age, na_rm = true)
-- Bad: Cryptic names, implicit NA behavior
c = read_csv("data.csv")
a = mean(c.age) -- Errors if NA present
Documentation:
Located in tests/unit/.
Example (tests/unit/test_mean.ml):
let test_mean_basic () =
let result = Stats.mean [VInt 1; VInt 2; VInt 3] false in
assert (result = VFloat 2.0)
let test_mean_with_na () =
let result = Stats.mean [VInt 1; VNA NAInt; VInt 3] true in
assert (result = VFloat 2.0)
let tests = [
("mean_basic", test_mean_basic);
("mean_with_na", test_mean_with_na);
]Located in tests/golden/.
Compare T output against R:
T Program (tests/golden/mean.t):
print(mean([1, 2, 3, 4, 5]))
R Script (tests/golden/mean.R):
cat(mean(c(1, 2, 3, 4, 5)), "\n")Run:
dune exec src/repl.exe < tests/golden/mean.t > t_output.txt
Rscript tests/golden/mean.R > r_output.txt
diff t_output.txt r_output.txtRequired:
Recommended:
# All tests
dune runtest
# Specific test
dune runtest tests/unit/test_mean.ml
# Verbose output
dune runtest --verbose
# Watch mode (re-run on changes)
dune runtest --watchFork the repository
Clone your fork:
git clone https://github.com/YOUR_USERNAME/tlang.gitCreate a branch:
git checkout -b feature/my-featureMake changes:
Test:
dune build
dune runtestCommit:
git add .
git commit -m "Add feature: description"Push:
git push origin feature/my-featureOpen a Pull Request on GitHub
Follow conventional commit format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation onlystyle: Formatting, no code changerefactor: Code refactortest: Add or fix testschore: Build, CI, toolingExamples:
feat(stats): Add median function
Implements median via quantile(0.5). Supports na_rm parameter.
Closes #42
fix(eval): Fix closure environment capture
Closures were capturing global env instead of local env.
This caused incorrect behavior in nested functions.
Title: Clear and descriptive
Add median function to stats package
Description: Include:
Fixes #42,
Closes #17)Example:
## Summary
Adds `median()` function to stats package.
## Changes
- Implement median as `quantile(data, 0.5)`
- Add unit tests
- Update API reference documentation
## Testing
- Unit tests pass
- Golden test against R's `median()`
- Tested with NA values (na_rm parameter)
Fixes #42Checklist:
Choose a package: base,
core, math, stats,
to_dataframe, colcraft, pipeline,
or explain
Create function file:
src/packages/<package>/<function_name>.ml or
.t
Implement function:
(* src/packages/stats/median.ml *)
let median values na_rm =
Stats.quantile values 0.5 na_rmRegister in package loader (if needed)
Add tests:
tests/unit/test_median.ml
Update docs:
docs/api-reference.md
Add example:
examples/median_example.t
Parameters:
na_rm) lastReturn values:
VError for errors, not OCaml exceptionsError handling:
(* Good: Return VError *)
if List.length values = 0 then
VError { code = "ValueError"; message = "Empty list"; ... }
else
(* Compute result *)
(* Bad: Raise exception *)
if List.length values = 0 then
failwith "Empty list"Include:
Example:
I'm trying to add a new window function `row_min()` but getting a type error:
Error: This expression has type 'a list but an expression was expected of type Vector.t
Code:
```ocaml
let row_min values =
(* ... *)I’ve looked at row_number.ml but can’t figure out where
the conversion happens.
Environment: Ubuntu 22.04, OCaml 4.14.1 ```
Contributors are recognized in:
CONTRIBUTORS.md file (coming soon)By contributing, you agree that your contributions will be licensed under the EUPL v1.2.
Ready to contribute? Check out good first issues or dive into the Development Guide!