mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2025-04-26 15:13:56 +00:00
commit
5f344b47fa
346
niva.md
Normal file
346
niva.md
Normal file
@ -0,0 +1,346 @@
|
||||
---
|
||||
name: niva
|
||||
filename: main.niva
|
||||
contributors:
|
||||
- ["gavr", "https://github.com/gavr123456789"]
|
||||
---
|
||||
|
||||
## Intro
|
||||
Niva is a simple language that takes a lot of inspiration from Smalltalk.
|
||||
But leaning towards the functional side and static typed.
|
||||
Everything is still an object, but instead of classes, interfaces, inheritance, and abstract classes,
|
||||
we have tagged unions, which is the only way to achieve polymorphism.
|
||||
|
||||
|
||||
For example, everything except the declaration is sending messages to objects.
|
||||
`1 + 2` is not a + operator, but a `... + Int` message for `Int` object.
|
||||
(there are no extra costs for that)
|
||||
|
||||
C-like: `1.inc()`
|
||||
Niva: `1 inc`
|
||||
|
||||
In essence, niva is highly minimalistic, like its ancestor Smalltalk.
|
||||
It introduces types, unions, and associated methods.
|
||||
There are no functions.
|
||||
|
||||
Install:
|
||||
```bash
|
||||
git clone https://github.com/gavr123456789/Niva.git
|
||||
cd Niva
|
||||
./gradlew buildJvmNiva
|
||||
# LSP here https://github.com/gavr123456789/niva-vscode-bundle
|
||||
```
|
||||
|
||||
## The Basics
|
||||
|
||||
#### Variable
|
||||
Variables are immutable by default.
|
||||
There is no special keyword for declaring a variable.
|
||||
|
||||
```Scala
|
||||
// this is a comment
|
||||
int = 1
|
||||
str = "qwf"
|
||||
boolean = true
|
||||
char = 'a'
|
||||
float = 3.14f
|
||||
double = 3.14
|
||||
mutltiLineStr = """
|
||||
qwf ars zxc \n \t "qwf"
|
||||
"""
|
||||
explicit_type::Int = 5
|
||||
|
||||
|
||||
list = {1 2 3}
|
||||
set = #(1 2 3)
|
||||
map = #{1 'a' 2 'b'}
|
||||
|
||||
// declare a mutable variable
|
||||
mut x = 5
|
||||
x <- 6 // mutate
|
||||
```
|
||||
|
||||
#### Messages
|
||||
```Scala
|
||||
// hello world is a one liner
|
||||
"Hello world" echo // echo is a message for String obj
|
||||
|
||||
|
||||
// There are 3 types of messages
|
||||
1 inc // 2 unary
|
||||
1 + 2 // 3 binary
|
||||
"abc" at: 0 // 'a' keyword
|
||||
|
||||
|
||||
// they can be chained
|
||||
1 inc inc inc dec // 3
|
||||
1 + 1 + 2 - 3 // 1
|
||||
1 to: 3 do: [it echo] // 1 2 3
|
||||
// the last one here to:do: is a single message
|
||||
// to chain 2 keyword messages you need to wrap it in parentheses
|
||||
("123456" drop: 1) dropLast: 2 // "234"
|
||||
the comma `,` is syntactic sugar for the same effect
|
||||
"123456" drop: 1, dropLast: 2 // "234"
|
||||
|
||||
// you can mix them
|
||||
1 inc + 3 dec - "abc" count // 2 + 2 - 3 -> 1
|
||||
"123" + "456" drop: 1 + 1 // "123456" drop: 2 -> "3456"
|
||||
|
||||
// everything except type and message declarations are message sends in niva
|
||||
// for example `if` is a message for Boolean object that takes a lambda
|
||||
1 > 2 ifTrue: ["wow" echo]
|
||||
// expression
|
||||
base = 1 > 2 ifTrue: ["heh"] ifFalse: ["qwf"]
|
||||
// same for while
|
||||
mut q = 0
|
||||
[q > 10] whileTrue: [q <- q inc]
|
||||
// all of this is zero cost because of inlining at compile time
|
||||
```
|
||||
|
||||
#### Type
|
||||
New lines are not significant in niva
|
||||
Type declarations look like keyword messages consisting of fields and types
|
||||
```Scala
|
||||
type Square side: Int
|
||||
|
||||
type Person
|
||||
name: String
|
||||
age: Int
|
||||
```
|
||||
|
||||
#### Create instance
|
||||
Creating an object is the same keyword message as when declaring it, but with values in place of types.
|
||||
```Scala
|
||||
square = Square side: 42
|
||||
alice = Person name: "Alice" age: 24
|
||||
|
||||
// destruct fields by names
|
||||
{age name} = alice
|
||||
```
|
||||
|
||||
#### Access fields
|
||||
Getting fields is the same as sending a unary message with its name to the object
|
||||
```Scala
|
||||
// get age, add 1 and print it
|
||||
alice age inc echo // 25
|
||||
```
|
||||
|
||||
#### Method for type:
|
||||
Everything is an object, just like in Smalltalk, so everything can have a method declared.
|
||||
Here, we add a `double` method to `Int` and then use it inside the `perimeter` method of `Square`.
|
||||
|
||||
```Scala
|
||||
Int double = this + this
|
||||
Square perimeter = side double
|
||||
|
||||
square = Square side: 42
|
||||
square perimeter // call
|
||||
|
||||
|
||||
// explicit return type
|
||||
Int double2 -> Int = this * 2
|
||||
|
||||
// with body
|
||||
Int double3 -> Int = [
|
||||
result = this * 2
|
||||
^ result // ^ is return
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
#### Keyword message declaration
|
||||
```Scala
|
||||
type Range from: Int to: Int
|
||||
// keyword message with one arg `to`
|
||||
Int to::Int = Range from: this to: to
|
||||
|
||||
1 to: 2 // Range
|
||||
```
|
||||
#### Type constructor
|
||||
A type constructor functions as a message for the type itself rather than to a specific instance.
|
||||
```Scala
|
||||
constructor Float pi = 3.14
|
||||
x = Float pi // 3.14
|
||||
```
|
||||
|
||||
It can be useful for initializing fields with default values.
|
||||
```Scala
|
||||
type Point x: Int y: Int
|
||||
constructor Point atStart = Point x: 0 y: 0
|
||||
|
||||
p1 = Point x: 0 y: 0
|
||||
p2 = Point atStart
|
||||
// constructor is just a usual message, so it can have params
|
||||
constructor Point y::Int = Point x: 0 y: y
|
||||
p3 = Point y: 20 // x: 0 y: 20
|
||||
```
|
||||
|
||||
|
||||
#### Conditions
|
||||
If, like everything else, is the usual sending of a message to a Boolean object.
|
||||
It takes one or two lambda arguments.
|
||||
```Scala
|
||||
false ifTrue: ["yay" echo]
|
||||
1 < 2 ifTrue: ["yay" echo]
|
||||
1 > 2 ifTrue: ["yay" echo] ifFalse: ["oh no" echo]
|
||||
|
||||
// `ifTrue:ifFalse:` message can be used as expression
|
||||
str = 42 % 2 == 0
|
||||
ifTrue: ["even"]
|
||||
ifFalse: ["odd"]
|
||||
// str == "even"
|
||||
```
|
||||
|
||||
#### Cycles
|
||||
There is no special syntax for cycles.
|
||||
It's just keyword messages that take codeblocks as parameters.
|
||||
(it's zero cost thanks for inlining)
|
||||
```Scala
|
||||
{1 2 3} forEach: [ it echo ] // 1 2 3
|
||||
1..10 forEach: [ it echo ]
|
||||
|
||||
mut c = 10
|
||||
[c > 0] whileTrue: [ c <- c dec ]
|
||||
|
||||
c <- 3 // reset c
|
||||
[c > 0] whileTrue: [
|
||||
c <- c dec
|
||||
c echo // 3 2 1
|
||||
]
|
||||
```
|
||||
`whileTrue:` is a message for lambda object of the type:
|
||||
`[ -> Boolean] whileTrue::[ -> Unit]`
|
||||
|
||||
#### Matching
|
||||
there is special syntax for matching, since niva heavily utilize tagged unions
|
||||
```Scala
|
||||
x = "Alice"
|
||||
// matching on x
|
||||
| x
|
||||
| "Bob" => "Hi Bob!"
|
||||
| "Alice" => "Hi Alice!"
|
||||
|=> "Hi guest"
|
||||
|
||||
|
||||
// It can be used as expression as well
|
||||
y = | "b"
|
||||
| "a" => 1
|
||||
| "b" => 2
|
||||
|=> 0
|
||||
y echo // 2
|
||||
```
|
||||
|
||||
#### Tagged unions
|
||||
|
||||
```Scala
|
||||
union Color = Red | Blue | Green
|
||||
|
||||
// branches can have fields
|
||||
union Shape =
|
||||
| Rectangle width: Int height: Int
|
||||
| Circle radius: Double
|
||||
|
||||
constructor Double pi = 3.14
|
||||
Double square = this * this
|
||||
|
||||
// match on this(Shape)
|
||||
Shape getArea -> Double =
|
||||
| this
|
||||
| Rectangle => width * height, toDouble
|
||||
| Circle => Double pi * radius square
|
||||
|
||||
// There is exhaustiveness checking, so when you add a new branch
|
||||
// all the matches will become errors until all cases processed
|
||||
|
||||
Shape getArea -> Double = | this
|
||||
| Rectangle => width * height, toDouble
|
||||
// ERROR: Not all possible variants have been checked (Circle)
|
||||
```
|
||||
|
||||
#### Collections
|
||||
```Scala
|
||||
// commas are optional
|
||||
list = {1 2 3}
|
||||
map = #{'a' 1 'b' 2}
|
||||
map2 = #{'a' 1, 'b' 2, 'c' 3}
|
||||
set = #(1 2 3)
|
||||
|
||||
// common collection operations
|
||||
{1 2 3 4 5}
|
||||
map: [it inc],
|
||||
filter: [it % 2 == 0],
|
||||
forEach: [it echo] // 2 4 6
|
||||
|
||||
// iteration on map
|
||||
map forEach: [key, value ->
|
||||
key echo
|
||||
value echo
|
||||
]
|
||||
```
|
||||
|
||||
#### Nullability
|
||||
By default, variables cannot be assigned a null value.
|
||||
To allow this, nullable types are used, indicated by a question mark at the end of the type.
|
||||
You may already be familiar with this concept from TypeScript, Kotlin, or Swift.
|
||||
```Scala
|
||||
x::Int? = null
|
||||
q = x unpackOrPANIC
|
||||
|
||||
// do something if it's not null
|
||||
x unpack: [it echo]
|
||||
|
||||
// same but provide a backup value
|
||||
w = x unpack: [it inc] or: -1
|
||||
|
||||
// just unpack or backup value
|
||||
e = x unpackOrValue: -1
|
||||
```
|
||||
|
||||
#### Handling the error
|
||||
```Scala
|
||||
// exit the program with stack trace
|
||||
x = file read orPANIC
|
||||
x = file read orValue: "no file"
|
||||
```
|
||||
Errors work like effects, look for more in [Error handling](https://gavr123456789.github.io/niva-site/error-handling.html)
|
||||
|
||||
## Misc
|
||||
|
||||
#### Local arg names
|
||||
```Scala
|
||||
Int from: x::Int to: y::Int = this + x + y
|
||||
```
|
||||
|
||||
#### Syntax sugar for this
|
||||
```Scala
|
||||
Person foo = [
|
||||
.bar
|
||||
this bar // same thing
|
||||
]
|
||||
```
|
||||
|
||||
#### Compile time reflection
|
||||
You can get string representation of any argument from a call site.
|
||||
```Scala
|
||||
Int bar::Int baz::String = [
|
||||
// getting string representation from call side
|
||||
a = Compiler getName: 0
|
||||
b = Compiler getName: 1
|
||||
c = Compiler getName: 2
|
||||
a echo // 1 + 1
|
||||
b echo // variable
|
||||
c echo // "str"
|
||||
]
|
||||
|
||||
variable = 42
|
||||
// call side
|
||||
1 + 1
|
||||
bar: variable
|
||||
baz: "str"
|
||||
```
|
||||
|
||||
Links:
|
||||
- [Site](https://gavr123456789.github.io/niva-site/reference.html)
|
||||
- [GitHub](https://github.com/gavr123456789/Niva)
|
||||
- [LSP](https://github.com/gavr123456789/vaLSe)
|
||||
- [VSC plugin](https://github.com/gavr123456789/niva-vscode-bundle)
|
Loading…
Reference in New Issue
Block a user