In this page
Core Changelog
7.0
Precision Decorator
Float values can now be decorated by an annotation @precision(0.01)
.
The annotation value defined the expected precision kept by GreyCat by the dev.
This simple annotation can compress by an order of magnitude the size of GreyCat store.
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 };
foo.set(100);
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:
Download: https://get.greycat.io/files/lib/basic_c/dev/7.0/arm64-apple/7.0.14-dev.zip
Success
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 additionaluse
statement now to use the oldmath
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 time s |
Date::equals() |
removed, use an operation eg. date1.year == date2.year , or use time s |
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 {
year(0);
month(1);
day(2);
hour(3);
minute(4);
second(5);
microsecond(6);
}
Updated type: Quantizer
Quantizer
is no longer a native type, its configuration is set at object creation.
The configuration per dimension is set with the appropriate type: QuantizerSparseDim
and QuantizerDenseDim
.
Their use is unchanged from v6.
API Breaking changes
v6 | v7 |
---|---|
Quantizer::configure(dim: Array<any>): Quantizer |
see below |
Quantizer::new(): Quantizer |
Quantizer {dimensions: Array<QuantizerDim>} normal object creation |
Quantizer::dimensions(): int |
removed, available from dimensions field |
6.10
- migrate mbedtls to 3.6.0
- add support for Google OAuth2 SSO
- add protection and raise error for modification of nodeX while iterating over it
- add support for Key rotation of SSO such as Azure AD
- add support for Linux AArch64 platform and glibc binaries
- add executable signature for Apple related binaries
- add executable signature for Windows related binaries
- add skeleton for Apple .app
- cleanup STD GCL files according to newly introduced LSP server with better nullable checkes
- enable support for weakly formed CSV with nullable empty fields
- fix http get support for server that do not add binary len property
- fix ABi protocol for broken Array message
- fix codegen for SDK Java to allows unaligned version of ABI server and generated code
- fix support for breaking statement within for-in loops
- fix rangeSize in nodeTime and nodeList
- fix CSV support for trailing null values
5.x to 6.x
- Installation Install GreyCat v6 by visiting here, then:
- Rename:
~/.greycat/licence
to~/.greycat/license
(notice that ‘c’ becomes ‘s’)
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.readBin() |
file.read() |
file.close() |
file = null; |
File::open(csvPath) |
csvFile = CsvReader::new(csvPath, csvFormat) |
csvFile.read(csvFormat) |
csvFile.read() Ideally replace to csvFile.readTo(obj) |
csvFile.name() |
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") |
-
File iterator
- Old v5 way:
for (i, entry in dir) { if (entry is Directory) { // entry is dir } else if (entry is File) { // entry is file } }
- New v6 way:
var fw = FileWalker::new(path); if (fw != null) { while (!fw.isEmpty()) { var entry = fw.next(); if (entry != null) { if (entry.isDir()) { // entry is dir } else { // entry is file } } } }
-
Update webpack proxy middleware
module.exports = (app) => {
app.use(
// 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,
}),
);
};
-
Get files (JavaScript) See source
-
Upload files (JavaScript) See source
-
Download files in the browser (JavaScript)
const link = document.createElement('a');
link.download = '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);
link.click(); // triggers the download
document.body.removeChild(link);
- Download file from task result
export const downloadResource = async (task: runtime.Task) => {
fetch(`/files/${task.user_id}/tasks/${task.task_id}/result.gcb?json`)
.then((res) => {
return res.text();
})
.then((data) => {
const fileName = data.trim();
const link = document.createElement('a');
link.download = fileName;
const url = new URL(`/files/${task.user_id}/tasks/${task.task_id}/${fileName}`, window.location.origin);
link.href = url.toString();
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
};
- Task API
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`);
- New Enums
Get an Enum by accessor method:
<module>.<enum>.<enumAccessor>()
e.g.:
const role = tenant.TenantRole.superadmin();
Get an Enum by string:
<module>.<enum>[<string> as <module>.<enum>.Field]()
e.g.:
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
).
- New concepts
- File now is just the file descriptor (name, path, size etc)
- For every specific format, there is dedicated class for example: JsonReader, CsvReader etc.
- You can get specific file paths by calling these static methods:
File::baseDir()
translates to"./gcdata/files/"
File::userDir()
translates to"./gcdata/files/<user_id>/"
File::taskDir()
translates to"./gcdata/files/<user_id>/tasks/<task_id>"
where
<user_id>
and<task_id>
are contextual.
-
Code generation
v5 v6 greycat-lang gen -o ./src/greycat
greycat codegen project.gcl ./src/greycat
-
Styling if using MUI
@import '@greycat/web/css/greycat.base.css';
else
@import '@greycat/web/css/greycat.css';
- Breaking changes
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.