Core Changelog


Private type attribute

Keyword private is now available to protect type attributes from external mutations. The following example illustrates the semantics associated to the keyword.

Essentially, only local methods are allowed to mutate private attributes.

use util;

type Foo {
  private x: int;

  fn set(x: int) {
    this.x = x; // correct

fn main() {
  var foo = Foo { x: 5 };
  Assert::equals(foo.x, 100); // allowed to read private attribute

  foo.x = 42; // this will raise an error

New command: install

A new command install is available in the greycat CLI

This command will download the required libraries defined in the project.gcl:

@library("basic_c", "7.0.14-dev");

Before the first greycat run (or serve), user should run:

greycat install

It should output something like:

	Extracted: /Users/duke/.greycat/lib/basic_c/7.0.14-dev/basic.gcl
	Extracted: /Users/duke/.greycat/lib/basic_c/7.0.14-dev/basic_c.gclib

New command: print

A new command print is available in the greycat CLI

This command prints to stdout the content of the given GreyCat binary file (.gcb)

greycat print result.gcb

A --format=json flag can be specified to have it output JSON instead:

greycat print --format=json result.gcb

Removed module: math

Module math as been removed from the std library.

All math functions are now directly accessible from the core module.

The core module being always available in scope, it does not require any additional use statement now to use the old math symbols.

Updated types: Date, time

Date is no longer a native type, it is now designed for human presentation, while time is designed for calculations.
This means that most of the v6 Date API has been moved to time.

API Breaking changes

v6 v7
Date::new(): Date Date {year: , month: , ...} normal object creation
Date::fromEpoch(): Date Date::fromTime(time, TimeZone)
Date::toTime(): time Date::toTime(TimeZone): time
Date::add() removed, time::calendar_add(value, CalendarUnit, TimeZone)
Date::substract() removed, time::calendar_add(value, CalendarUnit, TimeZone) negative value
Date::floor() removed, time::calendar_floor(CalendarUnit, TimeZone): time
Date::ceiling() removed, time::calendar_ceiling(CalendarUnit, TimeZone): time
Date::startOfWeek() removed, time::startOfWeek(TimeZone): time
Date::dayOfYear() removed, time::dayOfYear(TimeZone)
Date::dayOfWeek() removed, time::dayOfWeek(TimeZone)
Date::weekOfYear() removed, time::weekOfYear()
Date::leapYear() removed
Date::isLeap() removed, time::isLeap(year: int)
Date::daysInYear() removed
Date::daysInMonth() removed
Date::totalDaysInYear() removed, use time::totalDaysInYear(year)
Date::totalDaysInMonth() removed, use time::totalDaysInMonth(month, year)
Date::startOfWeek() removed, use time::startOfWeek(Timezone):time
Date::endOfWeek() removed, use time::endOfWeek(Timezone):time
Date::set() removed, either set field, or use time methods (safer)
Date::setTimeZone() removed, timezone is not stored
Date::getTimeZone() removed, timezone is not stored
Date::shiftTimeZone() removed, timezone is not stored
Date::clone() removed
Date::toString() removed
Date::get(DatePart::year),… removed, use Date.year, Date.month, etc.
Date::year(), … removed, use Date.year, Date.month, etc.
Date::diff() removed, use an operation eg. date1.year - date2.year, or use times
Date::equals() removed, use an operation eg. date1.year == date2.year, or use times
Date::min(), max() removed, use time:min(), time::max()
time::toDate(): Date removed, Date::fromTime(time, TimeZone): Date
time::toDateUTC(): Date removed, Date::fromTime(time, TimeZone): Date

The new CalendarUnit replaces DatePart:

enum CalendarUnit {


5.x to 6.x

API breaking changes

v5 v6
Env::get System::getEnv()
Table::new(2, true) Table::new(2)
Table<T>::new(8, true) Table<T>::new(8)
CSVColumnString CsvColumnString
CSVColumnFloat CsvColumnFloat
CSVColumnInteger CsvColumnInteger
CSVColumnTime CsvColumnTime
CSVColumn CsvColumn
CSVFormat::new() CsvFormat {}
CSVFormat CsvFormat
BoxPlotF64 BoxPlotFloat
PCA::new() PCA {}
GaussianND::new() GaussianND {}
Tensor::new() Tensor {}
Gaussian::new() Gaussian {}
HistogramF64::new() HistogramFloat {}
HistogramI64::new() HistogramInt {}
gaussian.min() gaussian.min
ProgressTracker::new(); p.start(); ProgressTracker { start: time::now() };
p.steps() p.counter
p.throughput() * 1000000 p.speed (speed now is counter / s before it was counter / us)
p.duration() p.duration
.bin extension renamed to .gcb
@library("network"); no longer needed, lib network has been merged in std::io
Directory::new File::mkdir
Directory::open FileWalker::new
FileWriter::new("${filename}.json", false) JsonWriter::new("${filename}.json")
FileWriter::new("${filename}.bin", false) GcbWriter::new("${filename}.gcb")
fw.writeBin(metaTypeIndex) fw.write(metaTypeIndex);
File::open has different semantic now, it returns a file descriptor and not a reader. To read we need a specific type based on the format (eg. JsonReader, CsvReader, etc.)
File::open("${filename}.bin"); GcbReader::new("${filename}.gcb")
file.close() file = null;
File::open(csvPath) csvFile = CsvReader::new(csvPath, csvFormat) Ideally replace to csvFile.readTo(obj) doesn't exist anymore
csvFile.path() path
use http;, use smtp; use io;
JSON::parseFile(file) JsonReader::new(file.path).read()
gaussian.clear() gaussian = Gaussian{}
var clonedGaussian = gaussian.clone() var clonedGaussian = clone(gaussian)
Task::spawn("module.function", [params]); Task::spawn(module::function, [params]);
SmtpAuthMode SmtpAuth
SmtpConfig {...}; Smtp {...};
Smtp::send(...); var smtp = Smtp {...}; smtp.send(...);
Email {..., contentType: EmailContentType::html, ...} Email {..., body_is_html: true, ...}
@public @permission("public")
module.exports = (app) => {
        // ensures all non-GET requests are sent to GreyCat instead of Webpack-dev-server
        createProxyMiddleware((path, req) => req.method !== 'GET' || path.match('^/files'), {
            // your GreyCat endpoint URL
            target: greycatProxy,
const link = document.createElement('a'); = 'myFile.csv'; // the name of the downloaded file in the browser
link.href = '/files/path/to/myFile.csv'; // the actual path to download the file on GreyCat
document.body.appendChild(link);; // triggers the download
export const downloadResource = async (task: runtime.Task) => {
        .then((res) => {
            return res.text();
        .then((data) => {
            const fileName = data.trim();
            const link = document.createElement('a');
   = fileName;
            const url = new URL(`/files/${task.user_id}/tasks/${task.task_id}/${fileName}`, window.location.origin);
            link.href = url.toString();
const task = await myModule.myTask();
const handler = new TaskHandler(task);
const info = await handler.start(pollingDelayInMs, (info) => {
    // if you want to show progress somewhere
// getting files is using the standard "files" API
const result = await greycat.default.getFile(`${task.user_id}/tasks/${task.task_id}/result.gcb`);
const args = await greycat.default.getFile(`${task.user_id}/tasks/${task.task_id}/arguments.gcb`);

Get an Enum by accessor method:



const role = tenant.TenantRole.superadmin();

Get an Enum by string:

<module>.<enum>[<string> as <module>.<enum>.Field]()


const field = "superadmin"
const role = tenant.TenantRole[field as tenant.TenantRole.Field]();

Compare Enums Just compare them directly, no need to use value nor field (now called key).

where <user_id> and <task_id> are contextual.

@import '@greycat/web/css/greycat.base.css';


@import '@greycat/web/css/greycat.css';

Can not spawn Tasks from a spawned Task , only one level possible.

Progress tracker in Task is disabled

All usage of structuredClone need to be changed.