Contributing to T

Thank you for your interest in contributing to T! This guide will help you get started.

Table of Contents


Code of Ethics

This project follows a Code of Ethics adapted from the SQLite project. Please review it to understand the detailed ethical guidelines and expected behavior.


How Can I Contribute?

Reporting Bugs

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.1

Suggesting Features

Before suggesting, consider:

When suggesting: 1. Clearly describe the problem it solves 2. Provide concrete examples 3. Discuss alternatives

Contributing Code

We welcome:

Good first issues are tagged with good-first-issue in GitHub.

Improving Documentation

Documentation contributions are highly valued:


Development Setup

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 runtest

For coverage builds and deeper environment details, see the Development Guide.


Project Structure

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

Coding Standards

OCaml Style

Follow standard OCaml conventions:

Naming:

Formatting:

(* 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:

Pattern Matching:

T Language Style

Example 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:


Testing Requirements

Unit Tests

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);
]

Golden Tests

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.txt

Test Coverage

Required:

Recommended:

Running Tests

# 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 --watch

Submitting Changes

Workflow

  1. Fork the repository

  2. Clone your fork:

    git clone https://github.com/YOUR_USERNAME/tlang.git
  3. Create a branch:

    git checkout -b feature/my-feature
  4. Make changes:

    • Write code
    • Add tests
    • Update documentation
  5. Test:

    dune build
    dune runtest
  6. Commit:

    git add .
    git commit -m "Add feature: description"
  7. Push:

    git push origin feature/my-feature
  8. Open a Pull Request on GitHub

Commit Messages

Follow conventional commit format:

<type>(<scope>): <subject>

<body>

<footer>

Types:

Examples:

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.

Pull Request Guidelines

Title: Clear and descriptive

Add median function to stats package

Description: Include:

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 #42

Checklist:


Review Process

What Reviewers Look For

  1. Correctness: Does it work as intended?
  2. Testing: Are changes adequately tested?
  3. Style: Does it follow project conventions?
  4. Documentation: Are changes documented?
  5. Scope: Is the change focused and minimal?
  6. Backward Compatibility: Does it break existing code?

Addressing Feedback

Approval and Merge


Adding New Standard Library Functions

Process

  1. Choose a package: base, core, math, stats, to_dataframe, colcraft, pipeline, or explain

  2. Create function file: src/packages/<package>/<function_name>.ml or .t

  3. Implement function:

    (* src/packages/stats/median.ml *)
    let median values na_rm =
      Stats.quantile values 0.5 na_rm
  4. Register in package loader (if needed)

  5. Add tests: tests/unit/test_median.ml

  6. Update docs: docs/api-reference.md

  7. Add example: examples/median_example.t

Function Signature Guidelines

Parameters:

Return values:

Error 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"

Getting Help

Communication Channels

Asking Good Questions

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 ```


Recognition

Contributors are recognized in:


License

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!