jakt - Jaktプログラミング言語

(The Jakt Programming Language)

Created at: 2022-05-05 15:35:16
Language: Rust
License: BSD-2-Clause

Jaktプログラミング言語

Jaktは、メモリセーフなシステムプログラミング言語です。

現在、C++に移行しています。

注:この言語は開発が進んでいます。

使用法

C++への変換には。が必要

clang
です。それがインストールされていることを確認してください。

jakt file.jakt
./build/file

目標

  1. メモリの安全性
  2. コードの可読性
  3. 開発者の生産性
  4. 実行可能なパフォーマンス
  5. 楽しい!

メモリの安全性

メモリの安全性を実現するために、次の戦略が採用されています。

  • 自動参照カウント
  • 強いタイピング
  • 境界チェック
  • セーフモードでは生のポインタはありません

Jaktには、次の3つのポインタタイプがあります。

  • [x] T(参照カウントクラスへの強力なポインタ
    T
    。)
  • [x]弱いT?(参照カウントクラスへのポインタが弱い
    T
    。ポインタが破壊されると空になります。)
  • [x] raw T(任意の型への生のポインター。ブロック
    T
    でのみ使用可能。)
    unsafe

セーフモードではnullポインターは使用できませんが、ポインターは

Optional
、つまり
Optional<T>
、または
T?
略してラップすることができます。

弱いポインタは常にでラップする必要があることに注意してください

Optional
。弱いTはなく、弱いTだけですか?

数学の安全性

  • [x]整数オーバーフロー(符号付きと符号なしの両方)は実行時エラーです。
  • [x]数値は自動的にに強制変換されません
    int
    。すべてのキャストは明示的である必要があります。

サイレント整数オーバーフローが必要な場合は、この機能を提供する明示的な関数があります。

コードの可読性

コードを書くよりも読むことにはるかに多くの時間が費やされます。そのため、Jaktは読みやすさを重視しています。

より読みやすいプログラムを促進する機能のいくつか:

  • [x]デフォルトでは不変です。
  • [x]呼び出し式の引数ラベル(
    object.function(width: 10, height: 5)
  • []推定
    enum
    スコープ。
    Foo
    (の代わりに言うことができます
    MyEnum::Foo
    )。
  • [x]。とのパターンマッチング
    match
  • []オプションの連鎖(
    foo?.bar?.baz
    (間違いのない)および
    foo!.bar!.baz
    (間違いのない))
  • [x]オプションの合体なし(値がある場合は
    foo ?? bar
    生成され、そうでない場合は生成されます)
    foo
    foo
    bar
  • [x]
    defer
    ステートメント。
  • [x]ポインタは常に
    .
    (決して
    ->
    )で逆参照されます
  • []末尾のクロージャパラメータは、呼び出し括弧の外に渡すことができます。
  • ErrorOr<T>
    []リターンタイプと専用
    try
    /
    must
    キーワードによるエラー伝播。

関数呼び出し

関数を呼び出すときは、渡すときに各引数の名前を指定する必要があります。

rect.set_size(width: 640, height: 480)

これには2つの例外があります。

  • [x]関数宣言のパラメーターがとして宣言されている場合
    anon
    、引数ラベルを省略できます。
  • [x]パラメーターと同じ名前の変数を渡す場合。

構造とクラス

Jaktで構造を宣言する主な方法は2つあります:

struct
class

struct

基本構文:

struct Point {
    x: i64
    y: i64
}

Jaktの構造体には値のセマンティクスがあります:

  • 構造体を含む変数には、常に構造体の一意のインスタンスがあります。
  • インスタンスをコピーすると
    struct
    、常にディープコピーが作成されます。
let a = Point(x: 10, y: 5)
let b = a
// "b" is a deep copy of "a", they do not refer to the same Point

Jaktは、構造体のデフォルトコンストラクターを生成します。すべてのフィールドを名前で取得します。上記の

Point
構造体の場合、次のようになります。

Point(x: i64, y: i64)

構造体メンバーはデフォルトで公開されています。

class

  • [x]基本的なクラスのサポート
  • [x]デフォルトのプライベートメンバー
  • [ ] 継承
  • []クラスベースのポリモーフィズム(親タイプを必要とするものに子インスタンスを割り当てる)
  • []
    Super
    タイプ
  • []
    Self
    タイプ

と同じ基本構文

struct

class Size {
    width: i64
    height: i64

    public function area(this) => .width * .height
}

Jaktのクラスには参照セマンティクスがあります:

  • class
    インスタンス(別名「オブジェクト」)をコピーすると、オブジェクトへの参照がコピーされます。
  • デフォルトでは、すべてのオブジェクトが参照カウントされます。これにより、削除後にオブジェクトにアクセスできないようになります。

クラスのメンバーはデフォルトでプライベートです。

メンバー機能

構造体とクラスの両方がメンバー関数を持つことができます。

メンバー関数には次の3種類があります。

静的メンバー関数は、呼び出すオブジェクトを必要としません。

this
パラメータはありません。

class Foo {
    function func() => println("Hello!")
}

// Foo::func() can be called without an object.
Foo::func()

非変更メンバー関数では、オブジェクトを呼び出す必要がありますが、オブジェクトを変更することはできません。最初のパラメータは

this
です。

class Foo {
    function func(this) => println("Hello!")
}

// Foo::func() can only be called on an instance of Foo.
let x = Foo()
x.func()

メンバー関数を変更するには、オブジェクトを呼び出す必要があり、オブジェクトを変更する場合があります。最初のパラメータは

mut this
です。

class Foo {
    x: i64

    function set(mut this, anon x: i64) {
        this.x = x
    }
}

// Foo::set() can only be called on a mut Foo:
mut foo = Foo(x: 3)
foo.set(9)

メンバー変数にアクセスするための省略形

メソッドで繰り返さ

this.
れるスパムを減らすために、省略形
.foo
はに展開され
this.foo
ます。

配列

動的配列は、組み込み型を介して提供され

Array<T>
ます。それらは実行時に拡大および縮小できます。

Array
メモリセーフですか:

  • 範囲外では、実行時エラーでプログラムがパニックになります。
  • スライスは、
    Array
    自動参照カウントを介して基になるデータを存続させます。

配列の宣言

// Function that takes an Array<i64> and returns an Array<String>
function foo(numbers: [i64]) -> [String] {
    ...
}

配列を作成するための省略形

// Array<i64> with 256 elements, all initialized to 0.
let values = [0; 256]

// Array<String> with 3 elements: "foo", "bar" and "baz".
let values = ["foo", "bar", "baz"]

辞書

  • [x]辞書の作成
  • [x]辞書の索引付け
  • [x]インデックスへの割り当て(別名左辺値)
function main() {
    let dict = ["a": 1, "b": 2]

    println("{}", dict["a"])
}

辞書の宣言

// Function that takes a Dictionary<i64, String> and returns an Dictionary<String, bool>
function foo(numbers: [i64:String]) -> [String:bool] {
    ...
}

辞書を作成するための省略形

// Dictionary<String, i64> with 3 entries.
let values = ["foo": 500, "bar": 600, "baz": 700]

セット

  • [x]セットの作成
  • [x]参照セマンティクス
function main() {
    let set = {1, 2, 3}

    println("{}", set.contains(1))
    println("{}", set.contains(5))
}

タプル

  • [x]タプルの作成
  • [x]インデックスタプル
  • []タプルタイプ
function main() {
    let x = ("a", 2, true)

    println("{}", x.1)
}

列挙型とパターンマッチング

  • [x]合計タイプとしての列挙型
  • [x]一般的な列挙型
  • [x]基になる型の値の名前としての列挙型
  • [x]
    match
  • [x]
    match
    アーム内の列挙型スコープ推論
  • [x]一致ブロックからの値の生成
  • []ネストされた
    match
    パターン
  • match
    []パターンとしての特性
  • ?
    [] 、
    ??
    および
    !
    演算子との相互運用のサポート
enum MyOptional<T> {
    Some(T)
    None
}

function value_or_default<T>(anon x: MyOptional<T>, default: T) -> T {
    return match x {
        Some(value) => {
            let stuff = maybe_do_stuff_with(value)
            let more_stuff = stuff.do_some_more_processing()
            yield more_stuff
        }
        None => default
    }
}

enum Foo {
    StructLikeThingy (
        field_a: i32
        field_b: i32
    )
}

function look_at_foo(anon x: Foo) -> i32 {
    match x {
        StructLikeThingy(field_a: a, field_b) => {
            return a + field_b
        }
    }
}

enum AlertDescription: i8 {
    CloseNotify = 0
    UnexpectedMessage = 10
    BadRecordMAC = 20
    // etc
}

// Use in match:
function do_nothing_in_particular() => match AlertDescription::CloseNotify {
    CloseNotify => { ... }
    UnexpectedMessage => { ... }
    BadRecordMAC => { ... }
}

ジェネリック

  • [x]ジェネリック型
  • [x]ジェネリック型推論
  • []特性

Jaktは、一般的な構造と一般的な関数の両方をサポートしています。

function id<T>(anon x: T) -> T {
    return x
}

function main() {
    let y = id(3)

    println("{}", y + 1000)
}
struct Foo<T> {
    x: T
}

function main() {
    let f = Foo(x: 100)

    println("{}", f.x)
}

名前空間

  • [x]関数と構造体/クラス/列挙型の名前空間のサポート
  • []深い名前空間のサポート
namespace Greeters {
    function greet() {
        println("Well, hello friends")
    }
}

function main() {
    Greeters::greet()
}

型キャスト

Jaktには2つの組み込みのキャスト演算子があります。

  • as? T
    Optional<T>
    ソース値がに変換できない場合は、空のを返します
    T
  • as! T
    :を返し
    T
    、ソース値がに変換できない場合はプログラムを中止します
    T

キャストはこれらの

as
ことを行うことができます(実装はまだ同意していない可能性があることに注意してください):

  • 同じタイプへのキャストは間違いなく無意味なので、将来は禁止される可能性があります。
  • 両方のタイプがプリミティブの場合、安全な変換が行われます。
    • 値が範囲外の場合、整数キャストは失敗します。これは、i32->i64のようなプロモーションキャストは間違いないことを意味します。
    • 浮動小数点->整数キャストは小数点を切り捨てます(?)
    • 整数->浮動小数点キャストは、浮動小数点型(?)で表現できる整数に最も近い値に解決されます。整数値が大きすぎると、無限大(?)に解決されます。
    • Any primitive -> bool will create
      true
      for any value except 0, which is
      false
      .
    • bool -> any primitive will do
      false -> 0
      and
      true -> 1
      , even for floats.
  • If the types are two different pointer types (see above), the cast is essentially a no-op. A cast to
    T
    will increment the reference count as expected; that's the preferred way of creating a strong reference from a weak reference. A cast from and to
    raw T
    is unsafe.
  • If the types are part of the same type hierarchy (i.e. one is a child type of another):
    • A child can be cast to its parent infallibly.
    • A parent can be cast to a child, but this will check the type at runtime and fail if the object was not of the child type or one of its subtypes.
  • If the types are incompatible, a user-defined cast is attempted to be used. The details here are not decided yet.
  • If nothing works, the cast will not even compile.

Additional casts are available in the standard library. Two important ones are

as_saturated
and
as_truncated
, which cast integral values while saturating to the boundaries or truncating bits, respectively.

Traits

(Not yet implemented)

To make generics a bit more powerful and expressive, you can add additional information to them:

trait Hashable {
    function hash(self) -> i128
}

class Foo implements Hashable {
    function hash(self) => 42
}

type i64 implements Hashable {
    function hash(self) => 100
}

The intention is that generics use traits to limit what is passed into a generic parameter, and also to grant that variable more capabilities in the body. It's not really intended to do vtable types of things (for that, just use a subclass)

Safety analysis

(Not yet implemented)

To keep things safe, there are a few kinds of analysis we'd like to do (non-exhaustive):

  • Preventing overlapping of method calls that would collide with each other. For example, creating an iterator over a container, and while that's live, resizing the container
  • Using and manipulating raw pointers
  • Calling out to C code that may have side effects

Error handling

Functions that can fail with an error instead of returning normally are marked with the

throws
keyword:

function task_that_might_fail() throws -> usize {
    if problem {
        throw Error::from_errno(EPROBLEM)
    }
    ...
    return result
}

function task_that_cannot_fail() -> usize {
    ...
    return result
}

Unlike languages like C++ and Java, errors don't unwind the call stack automatically. Instead, they bubble up to the nearest caller.

If nothing else is specified, calling a function that

throws
from within a function that
throws
will implicitly bubble errors.

Syntax for catching errors

If you want to catch errors locally instead of letting them bubble up to the caller, use a

try
/
catch
construct like this:

try {
    task_that_might_fail()
} catch error {
    println("Caught error: {}", error)
}

短い形式もあります:

try task_that_might_fail() catch error {
    println("Caught error: {}", error)
}

エラーの再スロー

(まだ実装されていません)

インラインC++

既存のC++コードとの相互運用性を向上させるため、およびブロック内のJakt

unsafe
の機能が十分に強力でない状況では、インラインC++コードをプログラムに埋め込む可能性が
cpp
ブロックの形式で存在します。

mut x = 0
unsafe {
    cpp {
        "x = (i64)&x;"
    }
}
println("{}", x)

参考文献

値とオブジェクトは、安全であることが証明されている状況では、参照によって渡すことができます。

参照は不変(デフォルト)または可変のいずれかです。

参照型の構文

  • &T
    タイプの値への不変の参照
    T
    です。
  • &mut T
    タイプの値への可変参照
    T
    です。

参照式の構文

  • &foo
    変数への不変の参照を作成します
    foo
  • &mut foo
    変数への可変参照を作成します
    foo

参照の間接参照

*
参照から「値を取得」するには、演算子を使用して逆参照する必要があります。

function sum(a: &i64, b: &i64) -> i64 {
    return *a + *b
}

構造体への可変参照の場合、フィールドアクセスを行うには、逆参照を括弧で囲む必要があります。

struct Foo {
    x: i64
}
function zero_out(foo: &mut Foo) {
    (*foo).x = 0
}

参照(最初のバージョン)機能リスト:

  • [x]参照型
  • [x]参照関数パラメーター
  • [x]参照ローカルなし
  • [x]構造体に参照がありません
  • [x]リターンタイプに参照がありません
  • [x]不変の値への不変の参照はありません
  • [x]指定されたパラメータの引数ラベルを許可する場合
    &foo
    と使用しない場合
    &mut foo
    foo

TODOへの参照:

  • [](
    unsafe
    )参照と生のポインタは双方向に変換可能
  • []永続的なクロージャでの参照によるキャプチャはありません