Quick survey of Transd


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:

  1. Open an empty text file.
  2. Copy the text of an example from this page and paste it into the empty file.
  3. Save the file under any name ending with .td extension: <ANY_NAME>.td.
  4. Run the file in the Transd interpreter:
tree3 <YOUR_FILE_NAME>

Contents:

Data processing

Transd has powerful built-in capabilities for data processing. It has an integrated SQL-like data query language, as well as built-in functionality for working with tabular data.

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

Type safety

Transd is statically typed: the types of all expressions and variables in a Transd program are known before the run-time phase begins. This greatly 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.

Full Unicode support

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

Security

Transd has different features that help strengthen various aspects of language security both for programmers and for users of Transd programs:

Modules

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

Classes

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
    )
  )
}

Functions as data

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"
  )
}

Generic collections

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"]
        )
    )
}

Type inference

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() )

Exceptions

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 order execution

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:

  1. Split it to words;
  2. Sort words in alphabetical order;
  3. Print sorted words to the screen.

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.

Size

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

Speed

The speed of Transd ranges from fast to very fast. See a performance test.