This commit is contained in:
gavr123456789 2025-03-17 14:53:59 +02:00
parent d95333b179
commit f1771a1435

96
niva.md
View File

@ -14,21 +14,14 @@ we have tagged unions, which is the only way to achieve polymorphism.
For example, everything except the declaration is sending messages to objects. For example, everything except the declaration is sending messages to objects.
`1 + 2` is not a + operator, but a `... + Int` message for `Int` object. `1 + 2` is not a + operator, but a `... + Int` message for `Int` object.
(ofc there are no extra costs for that) (there are no extra costs for that)
C-like: `1.inc()` C-like: `1.inc()`
Niva: `1 inc` Niva: `1 inc`
So basically niva is types, unions, and methods for them. In essence, niva is highly minimalistic, like its ancestor Smalltalk.
There are no functions. It introduces types, unions, and associated methods.
On an imaginary graph of complexity, I would put it here: There are no functions.
Go < Niva < Java < Kotlin < Scala
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)
Install: Install:
```bash ```bash
@ -68,7 +61,7 @@ x <- 6 // mutate
#### Messages #### Messages
```Scala ```Scala
// hello world is one liner // hello world is a one liner
"Hello world" echo // echo is a message for String obj "Hello world" echo // echo is a message for String obj
@ -83,15 +76,17 @@ x <- 6 // mutate
1 + 1 + 2 - 3 // 1 1 + 1 + 2 - 3 // 1
1 to: 3 do: [it echo] // 1 2 3 1 to: 3 do: [it echo] // 1 2 3
// the last one here to:do: is a single message // the last one here to:do: is a single message
// to chain 2 keyword message you need comma `,` // 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" "123456" drop: 1, dropLast: 2 // "234"
// or mixed // you can mix them
1 inc + 3 dec - "abc" count // 2 + 2 - 3 -> 1 1 inc + 3 dec - "abc" count // 2 + 2 - 3 -> 1
"123" + "456" drop: 1 + 1 // "123456" drop: 2 -> "3456" "123" + "456" drop: 1 + 1 // "123456" drop: 2 -> "3456"
// everything except type and msg declarations are message sends in niva // everything except type and message declarations are message sends in niva
// for example `if` is a message for Boolean object that takes lambda // for example `if` is a message for Boolean object that takes a lambda
1 > 2 ifTrue: ["wow" echo] 1 > 2 ifTrue: ["wow" echo]
// expression // expression
base = 1 > 2 ifTrue: ["heh"] ifFalse: ["qwf"] base = 1 > 2 ifTrue: ["heh"] ifFalse: ["qwf"]
@ -103,7 +98,7 @@ mut q = 0
#### Type #### Type
New lines are not significant in niva New lines are not significant in niva
Type declaration looks like a keyword message for type Type declarations look like keyword messages consisting of fields and types
```Scala ```Scala
type Square side: Int type Square side: Int
@ -112,10 +107,8 @@ type Person
age: Int age: Int
``` ```
#### Create instance #### Create instance
Object creation is just a keyword message with all fields Creating an object is the same keyword message as when declaring it, but with values in place of types.
```Scala ```Scala
square = Square side: 42 square = Square side: 42
alice = Person name: "Alice" age: 24 alice = Person name: "Alice" age: 24
@ -163,7 +156,7 @@ Int to::Int = Range from: this to: to
1 to: 2 // Range 1 to: 2 // Range
``` ```
#### Type constructor #### Type constructor
It's like a message for type itself instead of instance A type constructor functions as a message for the type itself rather than to a specific instance.
```Scala ```Scala
constructor Float pi = 3.14 constructor Float pi = 3.14
x = Float pi // 3.14 x = Float pi // 3.14
@ -183,25 +176,38 @@ p3 = Point y: 20 // x: 0 y: 20
#### Conditions #### Conditions
=> is syntax sugar for ifTrue message, since conditions are pretty common If, like everything else, is the usual sending of a message to a Boolean object.
It takes one or two lambda arguments.
```Scala ```Scala
false ifTrue: ["yay" echo]
1 < 2 ifTrue: ["yay" echo] 1 < 2 ifTrue: ["yay" echo]
1 > 2 ifTrue: ["yay" echo] ifFalse: ["oh no" 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 #### Cycles
There is no special syntax for cycles. There is no special syntax for cycles.
It's just keyword messages that take codeblocks as parameters. It's just keyword messages that take codeblocks as parameters.
(it's zero cost thanks for inlining) (it's zero cost thanks for inlining)
```Scala ```Scala
{1 2 3} forEach: [ it echo ] {1 2 3} forEach: [ it echo ] // 1 2 3
1..10 forEach: [ it echo ] 1..10 forEach: [ it echo ]
mut c = 10 mut c = 10
[c > 0] whileTrue: [ c <- c dec ] [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 codeblock of type: `whileTrue:` is a message for codeblock of the type:
`[ -> Boolean] whileTrue::[ -> Unit]` `[ -> Boolean] whileTrue::[ -> Unit]`
#### Matching #### Matching
@ -230,18 +236,24 @@ union Color = Red | Blue | Green
// branches can have fields // branches can have fields
union Shape = union Shape =
| Rectangle width: Int height: Int | Rectangle width: Int height: Int
| Circle radius: Int | Circle radius: Double
constructor Float pi = 3.14 constructor Double pi = 3.14
Double square = this * this
// match on this(Shape) // match on this(Shape)
Shape getArea -> Float = | this Shape getArea -> Double =
| Rectangle => width * height |> toFloat | this
| Circle => Float pi * radius * radius | Rectangle => width * height, toDouble
| Circle => Double pi * radius square
// its exhaustive, so when u add new branch // There is exhaustiveness checking, so when you add a new branch
// all the matches will become errors until all cases processed // 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 #### Collections
@ -252,7 +264,7 @@ map = #{'a' 1 'b' 2}
map2 = #{'a' 1, 'b' 2, 'c' 3} map2 = #{'a' 1, 'b' 2, 'c' 3}
set = #(1 2 3) set = #(1 2 3)
// default actions // common collection operations
{1 2 3 4 5} {1 2 3 4 5}
map: [it inc], map: [it inc],
filter: [it % 2 == 0], filter: [it % 2 == 0],
@ -266,15 +278,17 @@ map forEach: [key, value ->
``` ```
#### Nullability #### Nullability
Same as in Kotlin or Swift 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 ```Scala
x::Int? = null x::Int? = null
q = x unpackOrPANIC q = x unpackOrPANIC
// do something if its not null // do something if it's not null
x unpack: [it echo] x unpack: [it echo]
// same but expression with backup value // same but provide a backup value
w = x unpack: [it inc] or: -1 w = x unpack: [it inc] or: -1
// just unpack or backup value // just unpack or backup value
@ -316,4 +330,10 @@ Foo bar::Int baz::String = [
b echo b echo
c echo c echo
] ]
``` ```
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)