Что это?
Это язык-обертка для Golang. Мы немного приближаемся с синтаксису Kotlin. Отсюда и название. Что-то из серии TypeScript / CoffeeScript для Javascript.
Цель: облегчить жизнь сейчас и может быть в будущем Golang займется улучшением языка. Как видно по Javascript и Java до появления альтернатив прогресса практически не было.
Соответственно, код максимально близок к Golang, но с улучшенным синтаксисом.
Сейчас это на стадии концепта: как можно улучшить язык Golang малой кровью.
Как это работает?
Транслятор Кола ищет файлы с расширением kol и генерирует из них go-файлы. Затем с ними можно делать все так же как и с обычными go-файлами.
Примеры
Примеры берем с сайта Golang. В примерах куча каких-то математических функций (в худших традициях книжек по программированию 90х и более ранних лет). И нет обработки ошибок (стыдно?)…
Поэтому берем первые 2 примера с сайта и еще один пример от себя про работу с ошибками.
Hello, World!
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
Кол:
package main
import fmt.println
fun main() {
println("Hello, 世界")
}
Conway’s Game of Life
// An implementation of Conway's Game of Life.
package main
import (
"bytes"
"fmt"
"math/rand"
"time"
)
// Field represents a two-dimensional field of cells.
type Field struct {
s [][]bool
w, h int
}
// NewField returns an empty field of the specified width and height.
func NewField(w, h int) *Field {
s := make([][]bool, h)
for i := range s {
s[i] = make([]bool, w)
}
return &Field{s: s, w: w, h: h}
}
// Set sets the state of the specified cell to the given value.
func (f *Field) Set(x, y int, b bool) {
f.s[y][x] = b
}
// Alive reports whether the specified cell is alive.
// If the x or y coordinates are outside the field boundaries they are wrapped
// toroidally. For instance, an x value of -1 is treated as width-1.
func (f *Field) Alive(x, y int) bool {
x += f.w
x %= f.w
y += f.h
y %= f.h
return f.s[y][x]
}
// Next returns the state of the specified cell at the next time step.
func (f *Field) Next(x, y int) bool {
// Count the adjacent cells that are alive.
alive := 0
for i := -1; i <= 1; i++ {
for j := -1; j <= 1; j++ {
if (j != 0 || i != 0) && f.Alive(x+i, y+j) {
alive++
}
}
}
// Return next state according to the game rules:
// exactly 3 neighbors: on,
// exactly 2 neighbors: maintain current state,
// otherwise: off.
return alive == 3 || alive == 2 && f.Alive(x, y)
}
// Life stores the state of a round of Conway's Game of Life.
type Life struct {
a, b *Field
w, h int
}
// NewLife returns a new Life game state with a random initial state.
func NewLife(w, h int) *Life {
a := NewField(w, h)
for i := 0; i < (w * h / 4); i++ {
a.Set(rand.Intn(w), rand.Intn(h), true)
}
return &Life{
a: a, b: NewField(w, h),
w: w, h: h,
}
}
// Step advances the game by one instant, recomputing and updating all cells.
func (l *Life) Step() {
// Update the state of the next field (b) from the current field (a).
for y := 0; y < l.h; y++ {
for x := 0; x < l.w; x++ {
l.b.Set(x, y, l.a.Next(x, y))
}
}
// Swap fields a and b.
l.a, l.b = l.b, l.a
}
// String returns the game board as a string.
func (l *Life) String() string {
var buf bytes.Buffer
for y := 0; y < l.h; y++ {
for x := 0; x < l.w; x++ {
b := byte(' ')
if l.a.Alive(x, y) {
b = '*'
}
buf.WriteByte(b)
}
buf.WriteByte('\n')
}
return buf.String()
}
func main() {
l := NewLife(40, 15)
for i := 0; i < 300; i++ {
l.Step()
fmt.Print("\x0c", l) // Clear screen and print field.
time.Sleep(time.Second / 30)
}
}
Кол:
// An implementation of Conway's Game of Life.
package main
import (
bytes
fmt
math/rand
time
fmt.print
time.sleep
)
// Field represents a two-dimensional field of cells.
class Field {
val field: [][]bool
val width: int
val height: int
// returns an empty field of the specified width and height
init {
field = make([][]bool, h)
field.forEach { i ->
i = make([]bool, w)
}
}
// sets the state of the specified cell to the given value
fun set(x: int, y: int, value: bool) {
field[y][x] = value
}
// reports whether the specified cell is alive
// If the x or y coordinates are outside the field boundaries they are wrapped
// toroidally. For instance, an x value of -1 is treated as width-1.
fun isAlive(x: int, y: int): bool {
x += width
x %= width
y += height
y %= height
return field[y][x]
}
// returns the state of the specified cell at the next time step
fun next(x: int, y: int): bool {
// Count the adjacent cells that are alive.
alive := 0
for i := -1; i <= 1; i++ {
for j := -1; j <= 1; j++ {
if (j != 0 || i != 0) && isAlive(x+i, y+j) {
alive++
}
}
}
// Return next state according to the game rules:
// exactly 3 neighbors: on,
// exactly 2 neighbors: maintain current state,
// otherwise: off.
return alive == 3 || alive == 2 && isAlive(x, y)
}
}
class Life(
val width: int,
val height: int,
) {
a: Field
b: Field
// creates a new Life game state with a random initial state
init {
a = Field(w, h)
b = Field(w, h)
randomizeField(a)
}
fun randomizeField(field: Field) {
for (i in 0..(width * height / 4-1)) {
field.set(rand.intn(width), rand.intn(height), true)
}
}
// advances the game by one instant, recomputing and updating all cells
fun step() {
// Update the state of the next field (b) from the current field (a).
for y in 0..height-1 {
for x in 0..width-1 {
b.set(x, y, a.next(x, y))
}
}
// Swap fields a and b.
a, b = b, a
}
// returns the game board as a string
fun string(): string {
var buf: bytes.Buffer
for y in 0..height-1 {
for x in 0..width-1 {
b: byte := if (a.isAlive(x, y)) '*' else ' '
buf.writeByte(b)
}
buf.writeByte('\n')
}
return buf.string()
}
}
fun clearScreen() {
print("\x0c")
}
fun main() {
life := Life(40, 15)
for i in 0..299 {
life.step()
clearScreen()
print(life)
sleep(time.second / 30)
}
}
Пример с ошибками
///usr/bin/true; exec /usr/bin/env go run "$0" "$@"
package main
import (
"errors"
"fmt"
)
type User struct {
ID string
Username string
Age int
}
func Find(username string) (*User, error) {
return nil, errors.New("Some error")
}
func FindUser(username string) (*User, error) {
u, err := Find(username)
if err != nil {
return nil, fmt.Errorf("FindUser: failed executing db query: %w", err)
}
return u, nil
}
func FindAndSetUserAge(username string, age int) error {
var user *User
var err error
user, err = FindUser(username)
if err != nil {
return fmt.Errorf("FindAndSetUserAge: %w", err)
}
// code that updates user
fmt.Println("user: %s", user.Username)
return nil
}
func main() {
if err := FindAndSetUserAge("user1@example.com", 1); err != nil {
fmt.Println("failed finding or updating user:", err)
return
}
fmt.Println("successfully updated user's age")
}
Кол:
package main
import (
errors
fmt
fmt.errorf
fmt.println
)
type User struct {
ID string
Username string
Age int
}
fun find(username: string): *User, error {
// throws -- special return case aka return nil, err
throws errors.new("Some error")
}
fun findUser(username: string): *User, error {
// executing db query
u, err := find(username)
// err is handled automatically including "executing db query" parse
return u
}
fun findAndSetUserAge(username: string, age: int): error {
var user: *User
var err: error
user, err = findUser(username)
// code that updates user
println("user: %s", user.Username)
}
fun main() {
// explicit checks are still possible
if err := findAndSetUserAge("user1@example.com", 1); err != nil {
println("failed finding or updating user:", err)
return
}
// try-catch syntax added
try {
findAndSetUserAge("user2@example.com", 5)
findAndSetUserAge("user3@example.com", 10)
} catch (err) {
println("failed finding or updating user:", err)
return
}
println("successfully updated user's age")
}
Основные отличия
Большие отличия:
- нормальный синтаксис классов (они все равно фактически есть)
- работа с ошибками
- return означает nil для error, throw означает nil для не error
- автоматическое обертывание, если нет try-catch и есть error в ответе
- try-catch
- все функции с малой буквы (казалось бы причем тут C#)
Поменьше:
- импорт отдельных функций
- fun вместо func
- for / forEach немного другие возможны (кстати, в последнем релизе 1.22 это немного улучшили)
- условие в присвоении (a = if (b) 1 else 0)
Пока что еще нет описания нового языка, чтобы продумать все возможные случаи.
Понятно, что это еще первое приближение. И еще возможно много вещей поменяются, но решение проблем из секции “большие отличия” в том или ином виде останутся.
Где скачать?
Пока что нигде.
Примеры выглядят хорошо. Понятно как это можно реализовать.
Как минимум нужно:
- описание языка
- транслятор в golang
- подстветка синтаксиса
- реформат кода
Еще непонятно когда дойдут руки. Пока что у меня нет планов что-то писать на Golang платформе, а там посмотрим.