Jina

1, Jina

Jina is a programming language with sane memory management, actors, and a coherent syntax

Mahsa Jina Amini was a 22 years old girl, murdered by the evil regime in Iran
at her funeral, these words were written on a stone above her grave:
beloved Jina, you will not die, your name will become a code

memory management is done in a sane way, by distinguishing 2 kinds of variables:
, immutable/mutable borrow variables
, immutable/mutable own variables:

note that in Jina, mutability and ownership are features of variables (not types)
so they only appear in two situations:
, variable types
, function parameters types

immutable borrow var can only be put into immutable borrow var
mutable borrow var can only be put into immutable/mutable borrow var
note that here "put into" means:
, variable assignment
, field assignment
, variable capture in functions

putting immutable own var into immutable/mutable borrow var is/isn't possible
putting mutable own var into immutable/mutable borrow var is possible
putting immutable/mutable own var into immutable/mutable own var:
, if on stack: copy
, if on heap: if reference count is zero and it isn't used in the following code, move,
otherwise increase reference count
putting immutable/mutable own var into mutable/immutable own var is not possible

numbers are stored on the stack (thus don't need memory management)
lists and dictionaries are stored on the heap
small records will be kept on stack, big ones on the heap

https://www.toptal.com/software/eliminating-garbage-collector
http://blog.skylight.io/rust-means-never-having-to-close-a-socket/

actors are used to achieve lock'free concurrency
sharing mutable data in concurrent parts of a program is problematic
a data race happens when these three behaviors occur:
, two or more pointers access the same data at the same time
, at least one of the pointers is being used to write to the data
, there's no mechanism being used to synchronize access to the data

to deal with it, programming languages choose different approaches:
, abandon concurrency altogether; make single threaded programs
, implement complicated and error prone lock mechanisms
, abandon mutability like in pure functional programming; but since at the end, mutability is necessary,
a complicated mechanism like monads, linear types, effects, or Clojure reference types, must be used
https://stackoverflow.com/questions/9132346/clojure-differences-between-ref-var-agent-atom-with-examples
https://clojure-doc.org/articles/language/concurrency_and_parallelism/
any how, the lack of direct mutability, and the need for aggressive garbage collection,
means that functional programming is not an efficient method
, use the approach taken by Pony https://www.ponylang.io/
it controls aliasing (sharing) and mutability, using reference capabilities
Pony's approach introduces many complexities, especially when dealing with generics
i think the main reason for its complexities is that even class fields have reference capabilities

in Jina, as shown above, mutable variables can't be captured in immutable own functions
messages are sent to actors via immutable own functions
so in Jina, all mutations will be synchronous

immutable functions with null return can be called asynchronously, since they can't affect their environment

types show us what we can do with the data, ie which operations are valid
inheritance is problematic:
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
https://www.tedinski.com/2018/06/26/variance.html
i feel that this problem was the motivation behind dynamic typing (another bad design)
to avoid this problem, some languages (eg Rust) have two kinds of types:
, concrete types can be instantiated, but cannot have subtypes
, abstract types cannot be instantiated, but can have subtypes
Jina does the same, without actually spliting types into two kinds

Jina compiles to C, and can easily use existing C libraries
the compiled binary will at least need "glib2" and "flint" dynamic libraries to be installed on the system

to install Jina in SPM Linux:
; spm install jina <jina-repo-url> [<public-key>]
in other Unix'like systems, install "gcc gnunet git", download SPM Linux source, then:
; cp spm.sh ~/.local/bin/spm
; chmod +x ~/.local/bin/spm
; spm install jina <jina-repo-url> [<public-key>]

2, syntax

comments:
;; comment line
;; comment
block
;;; comment
block
;

identifiers (variable names) start with an alphabetic character, and can include numbers and apostrophe:
abc'efg0123
template identifiers ("x" and "y" will be used in the definition of the variable):
abc[x, y]

immutable borrow variable definition:
v :T = ...
v = ...
mutable borrow variable:
v !T = ...
v! = ...
immutable own variable:
v &T = ...
v& = ...
mutable own variable:
v $T = ...
v$ = ...
mutating a mutable variable:
v .= ...

numeric types:
, Num: floating point with mid-rad interval arithmetics
, Num'z: multi'precision integer
, Num'u: compiled to "unsigned int" type in C
, Num'c: compiled to "char" type in C
Num'u follows modular arithmetic (ie it wraps around on overflow)
literal numbers:
1'234'567'890 ;; Num (by default), can be Num'z, or Num'u too
1.23e4 ;; Num
123z ;; Num'z
0x1234'5678'9abc'def0 ;; Num'u (on 32 bits systems, this number will be stored as 0x9abc'def0)
0'ab ;; Num'c (the character with hexadecimal ASCII code 0xab)
0x ;; Num'c (ASCII code for "a" character)

lists:
l :List[Num] = [1, 2, 3]
indexing:
l_1
negative indices count from the end of the list
mutating lists:
l! = [1, 2, 3]
l.put 0 index: 0 ;; [0, 2, 3]
l.put 4 index: 3 ;; [0, 2, 3, 4]
initial value of lists will be kept on stack
heap allocation will be used only when the lists needs to grow beyond its initial size

strings are UTF8 encoded, and implemented using lists:
s :String = "abc def"
one word string:
'abc
string interpolation: "abc{x}"
it's better to split long strings into a list of single'line strings:
""
first line
second line
""
which is equivalent to:
["first line", "\tsecond line"]

dictionaries are indexed using strings (instead of integers as in lists):
d :Dict[Num] = ["a": 1, "b": 2, "c": 3]
when keys are one word strings, this can also be written as:
[a: 1, b: 2, c: 3]
indexing:
d_'a

records:
r :(a: Num, b: Num, c: Num) = a: 1, b: 2, c: 3
r :(Num, Num, c: Num) = 1, 2, c: 3
to access an element:
r.a
r.0
records can be matched on smaller records:
a, b = 1, 2, 3
tagged fields can be matched on untagged ones, if the name of variables match:
a, b = a: 1, b: 2
and vice versa:
a: x, b: y, c: z = a, b, c: d
mutating a record (if "r" is a mutable variable):
r.a = 10

function:
f = {a :A, b !B -> C | ...}
note that the type of the above function is:
f :{a :A, b !B -> C}
function call:
f x y
which is equivalent to these forms:
f(x, y)
(x, y) # f
f b: y a: x

default values for parameters:
f = {a :Num = 1 , b :Num = 2 | ...}
f b: 22
f 11
f()

{f x y} can be written as: || f x y

conditional expression:
condition.then {} else: {}
condition.then {} {}
condition.then {} || c.then {} || c.then {} {}

and or:
a && b
a \\ b
which are equivalent to:
a.and {b}
a.or {b}
not: -a

matching:
a # {
b :B | ...
() | ...
}

there is no loop; just iterators:
iter.each {x | ...}
any type that implements "Iter" interface, must define an "iter" method,
whose returned value stores the mutable state
iterators store all states in their internal memory, so no memory allocation is done in the loop

actor:
a = Actor B(x, y)
a.do {b !B | ...}

modules are files containing definitions
any definition whose name is the module's name, with the first letter capitalized,
and possibly an additional number or extension (separated with an apostrophe),
will be exported (ie is accessible outside of the module)
to hide a definition, append an apostrophe at the end of its name: a' = ...
to access the definitions in a module which is inside a directory: dir.Definition
to hide a module so it can't be accessed from modules in the parent directory,
append an apostrophe at the end of its file name

a package is a collection of modules
in a project directory, we can have multiple package directories
the name of the package directory is the package name, plus a ".jin" extension
packages are of two kinds:
, application packages that contain a file named "0.jina" (which must contain the init function)
, library packages

to use a library package in a another package, create a file named "your_chosen_name.p" with this format:
, first line is the name of the package
, second line is the URL of the project containing the package
, the format of the URL (where protocol can be gnunet, git):
protocol://address'of'the'project'containing'package'directory
, the third line can contain a public key, which will be used to check the signature provided by the project
if there is no URL, it refers to the current project

to use the definitions of a package:
dot'p'file'name.Definition
definitions in "std" package, are directly accessible
there is no need for a "std.p" file, and prefixing with "std."

structs:
S := a: A, b: B
defining methods (using a namespace):
;ns S
{a :A -> Self |
b = ...
a, b
}
m1 = {self, x :X -> Y |
...
}
m2 = {self! |
...
}
to create an instance of a struct:
s :S = a: x, b: y
s = S. a: x, b: y
s = S a: x
accessing a member:
s.a
calling a method (note that there is no record field named "m", otherwise this will not work):
s.m x
which is equivalent to:
S.m s x

singletons:
B0 := () ;; false
B0 :B0 = ()
B1 := () ;; true
B1 :B1 = ()
unions:
B := B0 \ B1
type "A?" is a shortcut for "A\()"

traits:
;tr T
m1 :{self, x :X -> Y}
m2 = {self! |
;; default implementation
}
trait inheritance:
;tr T : T1, T2
...
defining the methods of a struct that implements some traits:
;ns S
m1 = ...
m2 = ...
;impl T1
m3 = ...
m4 = ...
;impl T2
...

generics:
S[X] := a: X, b: X
bounded generics:
S[X :I] := a: X, b: X

to directly enter C code:
;c ...
...
beware! with great power comes great responsibility