Skip to content

Commit 5f344b4

Browse files
authored
Merge pull request #5275 from gavr123456789/master
[niva/en]
2 parents 43882f8 + ec44661 commit 5f344b4

File tree

1 file changed

+346
-0
lines changed

1 file changed

+346
-0
lines changed

niva.md

+346
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
---
2+
name: niva
3+
filename: main.niva
4+
contributors:
5+
- ["gavr", "https://github.com/gavr123456789"]
6+
---
7+
8+
## Intro
9+
Niva is a simple language that takes a lot of inspiration from Smalltalk.
10+
But leaning towards the functional side and static typed.
11+
Everything is still an object, but instead of classes, interfaces, inheritance, and abstract classes,
12+
we have tagged unions, which is the only way to achieve polymorphism.
13+
14+
15+
For example, everything except the declaration is sending messages to objects.
16+
`1 + 2` is not a + operator, but a `... + Int` message for `Int` object.
17+
(there are no extra costs for that)
18+
19+
C-like: `1.inc()`
20+
Niva: `1 inc`
21+
22+
In essence, niva is highly minimalistic, like its ancestor Smalltalk.
23+
It introduces types, unions, and associated methods.
24+
There are no functions.
25+
26+
Install:
27+
```bash
28+
git clone https://github.com/gavr123456789/Niva.git
29+
cd Niva
30+
./gradlew buildJvmNiva
31+
# LSP here https://github.com/gavr123456789/niva-vscode-bundle
32+
```
33+
34+
## The Basics
35+
36+
#### Variable
37+
Variables are immutable by default.
38+
There is no special keyword for declaring a variable.
39+
40+
```Scala
41+
// this is a comment
42+
int = 1
43+
str = "qwf"
44+
boolean = true
45+
char = 'a'
46+
float = 3.14f
47+
double = 3.14
48+
mutltiLineStr = """
49+
qwf ars zxc \n \t "qwf"
50+
"""
51+
explicit_type::Int = 5
52+
53+
54+
list = {1 2 3}
55+
set = #(1 2 3)
56+
map = #{1 'a' 2 'b'}
57+
58+
// declare a mutable variable
59+
mut x = 5
60+
x <- 6 // mutate
61+
```
62+
63+
#### Messages
64+
```Scala
65+
// hello world is a one liner
66+
"Hello world" echo // echo is a message for String obj
67+
68+
69+
// There are 3 types of messages
70+
1 inc // 2 unary
71+
1 + 2 // 3 binary
72+
"abc" at: 0 // 'a' keyword
73+
74+
75+
// they can be chained
76+
1 inc inc inc dec // 3
77+
1 + 1 + 2 - 3 // 1
78+
1 to: 3 do: [it echo] // 1 2 3
79+
// the last one here to:do: is a single message
80+
// to chain 2 keyword messages you need to wrap it in parentheses
81+
("123456" drop: 1) dropLast: 2 // "234"
82+
the comma `,` is syntactic sugar for the same effect
83+
"123456" drop: 1, dropLast: 2 // "234"
84+
85+
// you can mix them
86+
1 inc + 3 dec - "abc" count // 2 + 2 - 3 -> 1
87+
"123" + "456" drop: 1 + 1 // "123456" drop: 2 -> "3456"
88+
89+
// everything except type and message declarations are message sends in niva
90+
// for example `if` is a message for Boolean object that takes a lambda
91+
1 > 2 ifTrue: ["wow" echo]
92+
// expression
93+
base = 1 > 2 ifTrue: ["heh"] ifFalse: ["qwf"]
94+
// same for while
95+
mut q = 0
96+
[q > 10] whileTrue: [q <- q inc]
97+
// all of this is zero cost because of inlining at compile time
98+
```
99+
100+
#### Type
101+
New lines are not significant in niva
102+
Type declarations look like keyword messages consisting of fields and types
103+
```Scala
104+
type Square side: Int
105+
106+
type Person
107+
name: String
108+
age: Int
109+
```
110+
111+
#### Create instance
112+
Creating an object is the same keyword message as when declaring it, but with values in place of types.
113+
```Scala
114+
square = Square side: 42
115+
alice = Person name: "Alice" age: 24
116+
117+
// destruct fields by names
118+
{age name} = alice
119+
```
120+
121+
#### Access fields
122+
Getting fields is the same as sending a unary message with its name to the object
123+
```Scala
124+
// get age, add 1 and print it
125+
alice age inc echo // 25
126+
```
127+
128+
#### Method for type:
129+
Everything is an object, just like in Smalltalk, so everything can have a method declared.
130+
Here, we add a `double` method to `Int` and then use it inside the `perimeter` method of `Square`.
131+
132+
```Scala
133+
Int double = this + this
134+
Square perimeter = side double
135+
136+
square = Square side: 42
137+
square perimeter // call
138+
139+
140+
// explicit return type
141+
Int double2 -> Int = this * 2
142+
143+
// with body
144+
Int double3 -> Int = [
145+
result = this * 2
146+
^ result // ^ is return
147+
]
148+
```
149+
150+
151+
#### Keyword message declaration
152+
```Scala
153+
type Range from: Int to: Int
154+
// keyword message with one arg `to`
155+
Int to::Int = Range from: this to: to
156+
157+
1 to: 2 // Range
158+
```
159+
#### Type constructor
160+
A type constructor functions as a message for the type itself rather than to a specific instance.
161+
```Scala
162+
constructor Float pi = 3.14
163+
x = Float pi // 3.14
164+
```
165+
166+
It can be useful for initializing fields with default values.
167+
```Scala
168+
type Point x: Int y: Int
169+
constructor Point atStart = Point x: 0 y: 0
170+
171+
p1 = Point x: 0 y: 0
172+
p2 = Point atStart
173+
// constructor is just a usual message, so it can have params
174+
constructor Point y::Int = Point x: 0 y: y
175+
p3 = Point y: 20 // x: 0 y: 20
176+
```
177+
178+
179+
#### Conditions
180+
If, like everything else, is the usual sending of a message to a Boolean object.
181+
It takes one or two lambda arguments.
182+
```Scala
183+
false ifTrue: ["yay" echo]
184+
1 < 2 ifTrue: ["yay" echo]
185+
1 > 2 ifTrue: ["yay" echo] ifFalse: ["oh no" echo]
186+
187+
// `ifTrue:ifFalse:` message can be used as expression
188+
str = 42 % 2 == 0
189+
ifTrue: ["even"]
190+
ifFalse: ["odd"]
191+
// str == "even"
192+
```
193+
194+
#### Cycles
195+
There is no special syntax for cycles.
196+
It's just keyword messages that take codeblocks as parameters.
197+
(it's zero cost thanks for inlining)
198+
```Scala
199+
{1 2 3} forEach: [ it echo ] // 1 2 3
200+
1..10 forEach: [ it echo ]
201+
202+
mut c = 10
203+
[c > 0] whileTrue: [ c <- c dec ]
204+
205+
c <- 3 // reset c
206+
[c > 0] whileTrue: [
207+
c <- c dec
208+
c echo // 3 2 1
209+
]
210+
```
211+
`whileTrue:` is a message for lambda object of the type:
212+
`[ -> Boolean] whileTrue::[ -> Unit]`
213+
214+
#### Matching
215+
there is special syntax for matching, since niva heavily utilize tagged unions
216+
```Scala
217+
x = "Alice"
218+
// matching on x
219+
| x
220+
| "Bob" => "Hi Bob!"
221+
| "Alice" => "Hi Alice!"
222+
|=> "Hi guest"
223+
224+
225+
// It can be used as expression as well
226+
y = | "b"
227+
| "a" => 1
228+
| "b" => 2
229+
|=> 0
230+
y echo // 2
231+
```
232+
233+
#### Tagged unions
234+
235+
```Scala
236+
union Color = Red | Blue | Green
237+
238+
// branches can have fields
239+
union Shape =
240+
| Rectangle width: Int height: Int
241+
| Circle radius: Double
242+
243+
constructor Double pi = 3.14
244+
Double square = this * this
245+
246+
// match on this(Shape)
247+
Shape getArea -> Double =
248+
| this
249+
| Rectangle => width * height, toDouble
250+
| Circle => Double pi * radius square
251+
252+
// There is exhaustiveness checking, so when you add a new branch
253+
// all the matches will become errors until all cases processed
254+
255+
Shape getArea -> Double = | this
256+
| Rectangle => width * height, toDouble
257+
// ERROR: Not all possible variants have been checked (Circle)
258+
```
259+
260+
#### Collections
261+
```Scala
262+
// commas are optional
263+
list = {1 2 3}
264+
map = #{'a' 1 'b' 2}
265+
map2 = #{'a' 1, 'b' 2, 'c' 3}
266+
set = #(1 2 3)
267+
268+
// common collection operations
269+
{1 2 3 4 5}
270+
map: [it inc],
271+
filter: [it % 2 == 0],
272+
forEach: [it echo] // 2 4 6
273+
274+
// iteration on map
275+
map forEach: [key, value ->
276+
key echo
277+
value echo
278+
]
279+
```
280+
281+
#### Nullability
282+
By default, variables cannot be assigned a null value.
283+
To allow this, nullable types are used, indicated by a question mark at the end of the type.
284+
You may already be familiar with this concept from TypeScript, Kotlin, or Swift.
285+
```Scala
286+
x::Int? = null
287+
q = x unpackOrPANIC
288+
289+
// do something if it's not null
290+
x unpack: [it echo]
291+
292+
// same but provide a backup value
293+
w = x unpack: [it inc] or: -1
294+
295+
// just unpack or backup value
296+
e = x unpackOrValue: -1
297+
```
298+
299+
#### Handling the error
300+
```Scala
301+
// exit the program with stack trace
302+
x = file read orPANIC
303+
x = file read orValue: "no file"
304+
```
305+
Errors work like effects, look for more in [Error handling](https://gavr123456789.github.io/niva-site/error-handling.html)
306+
307+
## Misc
308+
309+
#### Local arg names
310+
```Scala
311+
Int from: x::Int to: y::Int = this + x + y
312+
```
313+
314+
#### Syntax sugar for this
315+
```Scala
316+
Person foo = [
317+
.bar
318+
this bar // same thing
319+
]
320+
```
321+
322+
#### Compile time reflection
323+
You can get string representation of any argument from a call site.
324+
```Scala
325+
Int bar::Int baz::String = [
326+
// getting string representation from call side
327+
a = Compiler getName: 0
328+
b = Compiler getName: 1
329+
c = Compiler getName: 2
330+
a echo // 1 + 1
331+
b echo // variable
332+
c echo // "str"
333+
]
334+
335+
variable = 42
336+
// call side
337+
1 + 1
338+
bar: variable
339+
baz: "str"
340+
```
341+
342+
Links:
343+
- [Site](https://gavr123456789.github.io/niva-site/reference.html)
344+
- [GitHub](https://github.com/gavr123456789/Niva)
345+
- [LSP](https://github.com/gavr123456789/vaLSe)
346+
- [VSC plugin](https://github.com/gavr123456789/niva-vscode-bundle)

0 commit comments

Comments
 (0)