Below is a quick survey of the Transd programming language along with code examples. In the example code the screen output of a code line is shown after the //<=
symbols. All λ
symbols can be replaced with lambda
word. All the examples can be run in the Transd command line interpreter. To run an example:
.td
extension: <ANY_NAME>.td
.tree3 <YOUR_FILE_NAME>
Transd provides built-in tools for working with various types of data in a uniform
database-like way. These tools include data types for working with data (Table
,
DataBase
), support of tabular data formats (CSV and the like) and built-in data query
language (TQL).
#lang transd
MainModule: {
tabdata:
"Name,Salary:Int,Department
Alice,10000,IT
Susan,25000,HR
Bob,30000,Marketing",
_start: (λ
(with tabl Table()
(load-table tabl tabdata)
(build-index tabl "Salary")
(with rows (tsd tabl
select: ["Name", "Department"]
as: [[String(),String()]]
where: "Salary > 20000"
sortby: "Name")
(for row in rows do (lout row)))
))
}
// The output:
// [Bob, Marketing]
// [Susan, HR]
Data importing (or object deserializing) allows quick creation and initialization of program objects of custom classes using external textual data. An example of this feature is deserializing data in JSON format into custom class objects in some languages.
Data importing in Transd is similar to JSON object deserializng, but enhanced with some essential features, so that whole hierarchies of custom objects with complex structure can be loaded into the program from data files with minimal coding.
Transd is statically typed: the types of all expressions and variables in a Transd program are known before the run-time phase begins. This increases the robustness of programs, helps in the early detection of bugs, and improves the language performance.
#lang transd
MainModule: {
int1: 5,
str1: "abc",
printStr: (λ par String() (lout par)),
printInt: (λ par Int() (lout par)),
_start: (λ
(printStr str1) // OK
(printInt int1) // OK
(printStr int1) // Type mismatch at compilation stage
)
}
The type system of Transd includes such types as: Bool, Int, Double, Long, ULong, Byte, Char, Object, Position, DateTime, Vector, Tuple, Index, HashIndex, ByteArray, Table, TSDBase, StringStream, ByteStream, FileStream, Lambda, String
.
Strings in Transd are implemented as mutable wide character sequences. Characters are encoded in UTF-16 encoding on Windows and in UTF-32 on Linux. Which means that both ASCII and non-ASCII characters are provided with the same level of full support in Transd.
#lang transd
MainModule: {
_start: (λ
(with s1 "和平"
s2 "和平5"
(lout "size of " s1 " is: " (size s1))
(lout "second character in " s2 " is: " (subn s1 1))
(lout s1 " only contains letters: "
:boolalpha (match s1 "[[:alpha:]]+"))
(lout s2 " only contains letters: "
(match s2 "[[:alpha:]]+")))
)
}
OUTPUT:
size of 和平 is: 2
second character in 和平5 is: 平
和平 only contains letters: true
和平5 only contains letters: false
Transd offers features that help strengthen various aspects of language security both for programmers and for users of Transd programs:
All code in Transd programs is organized into modules. Modules serve for logical grouping of the code and isolation of program parts. A source file can contain several modules, and a module can be defined in several source files. A module can be private or public. The contents of public modules are accessible to other modules. The contents of private modules are inaccessible and invisible by default to other modules, but the access to some or all module members can be explicitly granted to some or all other modules.
#lang transd
public Module1: {
i: 5,
func1: (λ (textout "In Module1::func1."))
}
MainModule: {
import: "Module1",
_start: (λ (textout i " ") (func1)) //<= 5 In Module1::func1.
}
Transd supports object-oriented programming style and offers the mechanism of classes as templates for new objects.
#lang transd
class Point: {
x : Double(),
y : Double(),
@init: (λ x_ Double() y_ Double() (set x x_) (set y y_)),
dist: (λ pt Point()
(sqrt (+ (pow (- x pt.x) 2 ) (pow (- y pt.y) 2 ))))
}
MainModule: {
import: "Point",
pt1: Point( 5.0 6.0 ),
_start: (λ
(with pt2 Point(2.5 4.5)
pt3 Point(15.8 24.3)
(lout (dist pt1 pt2)) //<= 5.147815
(lout (dist pt1 pt3)) //<= 28.984996
)
)
}
A built-in class Lambda
plays the role of a function that can be passed to a function as an argument, assigned to a variable, etc.
#lang transd
MainModule: {
lambd1: Lambda<String Null>(λ s String() (textout "Hello, " s "! ") ),
func:(λ lam Lambda<Int Int>()
(textout "Square of 8 is" (exec lam 8))),
_start: (λ
(exec lambd1 "Lambda") //<= "Hello, Lambda!"
(func (λ i Int() -> Int() (* i i))) //<= "Square of 8 is 64"
)
}
Transd type system includes parameterized (or compound) types, sometimes called "generics". Container types in Transd (vectors, associative arrays, etc.) are only allowed to contain elements of one type, which is specified at container's creation: for example, Vector<Int>
. This limitation for a container to hold values only of a certain type increases safety and speed of programs, and comprehensibility of program texts.
#lang transd
MainModule: {
_start: (λ
(with v Vector<Int>([1,2,3])
(for i in v do (textout (* i 2) " ")) //<= 2 4 6
)
(with v Vector<String>()
(append v "abc") (append v "def") (append v "ghi")
(lout v) //<= ["abc", "def", "ghi"]
)
)
}
Transd is statically and strongly typed. This means that the type of every variable and expression in a Transd program is known at the compilation stage. But in majority of cases there is no need in explicit type annotations since Transd usually can deduce types automatically.
#lang transd
MainModule: {
_start: (λ
(with v1 [1,2,3,4,4,5]
v2 ["a","b","c","d","d","e"]
(with pos1 (find-adjacent v1)
pos2 (find-adjacent v2)
(lout (+ 5 (get-el pos1)))
(lout (+ 5 (get-el pos2))) //<= compile time error
)))
}
While compiling this fragment, Transd deduces the type of v2
as Vector<String>
,
pos2
receives the type Position<String>
, which is the return type of the polymorphic method (find-adjacent <container>)
(this method finds the first two adjacent equal elements in a container and returns an iterator to the first one). Then another polymorphic method (get-el <Position>)
returns the value of the element at the position. The type of this value in the first call of get-el
is Int
, in the second - String
. The addition operation of Int
and String
is not defined, and compilator throws an error:
Error:
...
while linking '_start' function
while compiling function '(lout (+ 5 (get-el pos2)))'
: method is not resolved: Int::sum( String() )
The mechanism of exceptions is a powerful means for flow control and communicating between program parts. Transd implements exceptions in a well-known "try-throw-catch" form.
#lang transd
MainModule: {
_start: (λ
(with vec [2,5,7,1,3]
vec1 Vector<Int>() // vec1 is empty vector
(with pos (max-element vec)
pos1 (max-element vec1)
(try
(textout "element at pos is: ")
(lout (get-el pos))
(textout "element at pos1 is: ")
(lout (get-el pos1))
(catch (textout "exception catched \n"))) // catch the exception
(textout "pos1 is vec1.end: " :boolalpha (eq pos1 (end vec1)) " ") // flow continues
)))
}
OUTPUT:
element at pos is: 7
element at pos1 is: exception catched
pos1 is vec1.end: true
Pipeline evaluation operator makes it possible to compose functions by concatenating function calls instead of nesting. For example, suppose, we have some string as data and we want to perform a sequence of operations on this data:
In usual way, this data processing flow can be arranged as follows:
(textout
(sort
(split someStr " ")))
The pipeline operator -|
allows us to avoid deep nesting and to write function calls in the same order as operations follow logically:
(-| (split someStr " ")
(sort)
(textout))
The necessary condition for combining function calls in this way is that the return value of a function must be admissible as an argument to the next function.
A more extended example of using the pipeline operator can be found here
Among other features, Transd has list comprehensions, ranges, dynamic module loading/unloading, etc.
One of the featured properties of Transd is its implementation size. The size of the executable file with language interpreter on Windows and Linux - less than 3Mb (about 5 Mb if statically compiled).
The C++ source code library of Transd, which can be directly included in other C++ projects as an embedded language, is contained in two files and consists of less than 20,000 lines of code. (All figures are from December, 2021).
The speed of Transd ranges from fast to very fast. See a performance test.