rokdocs
v0.1

2.3. 산술 / 비트 / 인덱싱 트레잇

rok::core::ops 모듈은 rok 의 연산자 토큰 (refs-lang 1.10) 이 dispatch 되는 트레잇 패밀리를 정의합니다. + - * / % << >> & | ^ ~ && || 같은 일반 연산자뿐 아니라 arr[i] 인덱싱과 s[1:3] 슬라이싱도 모두 이 장에서 정의된 트레잇 메서드를 호출합니다.

refs-lang 3.1 연산자 디스패치 의 메커니즘은 본 장의 내용 을 lookup 하며, 이 문서인 refs-standard-libraries 2.3 이 그 권위 자리를 차지합니다.

참고

이 장의 연산자 트레잇 메서드는 모두 throws 없이 선언됩니다. 따라서 연산자는 에러를 던지지 않으며, impl 도 throws 를 붙일 수 없습니다 (refs-lang 4.14.11). 실패할 수 있는 연산은 이름 있는 메서드 (checked_div 등) 로 두거나 Result 를 반환합니다.

2.3.1. 산술 트레잇

Add - 산술 덧셈 (+)

가장 먼저 살펴볼 것은 덧셈 트레잇입니다.

#[lang("add_trait")]
trait Add<Rhs = Self> {
    type Output;

    #[infix("+")]
    fn add(self, rhs: Rhs) -> Output;
}

의미: 두 값을 더한 결과를 반환합니다.

let a: i32 = 5;
let b: i32 = 3;
let c = a + b;        //→ 8 (i32)

let x: f64 = 1.5;
let y: f64 = 2.5;
let z = x + y;        //→ 4.0 (f64)

Output 규칙

Output associated type 은 보통 Self 와 같은 타입이 됩니다. 다만 다음과 같은 예외가 존재합니다. 첫째, int + int = int 의 경우 auto-promote 가 적용되어 i32 + i32 의 결과가 i64 범위라면 자동으로 promote 됩니다. 둘째, Vec3<f64> + f64 = Vec3<f64> 와 같은 사용자 정의 산술은 impl Add<f64> for Vec3<f64> 형식으로 정의됩니다.

오버플로 동작

오버플로 동작은 타입에 따라 다음과 같이 갈립니다. i32 + i32 의 경우 overflow 검사가 켜져 있으면 panic 이 발생하고, 꺼져 있으면 wrap 으로 처리됩니다. 검사는 rokc -C overflow-checks[=yes|no] (refs-lang 9.1.2) 로 정하며 기본은 -O0 에서만 켬입니다. f32 + f32 와 f64 + f64 는 IEEE 754 덧셈을 따르기 때문에 overflow 가 발생하면 ±∞ 또는 NaN 으로 귀결됩니다. int + int 는 무한 정밀도이므로 overflow 자체가 존재하지 않습니다. saturate, checked, wrapping 변형은 별도 메서드 (saturating_add, checked_add, wrapping_add) 로 제공되며 본 트레잇 외부에 정의됩니다.

자세한 정수 오버플로 정책은 2.11 변환 의 fixed-width 산술 절을 참고합니다.

Sub - 산술 뺄셈 (-)

뺄셈 트레잇은 다음과 같이 정의됩니다.

#[lang("sub_trait")]
trait Sub<Rhs = Self> {
    type Output;

    #[infix("-")]
    fn sub(self, rhs: Rhs) -> Output;
}

Add 와 동일한 형식을 가지며, a - b 는 Sub::sub(a, b) 로 dispatch 됩니다.

let r = 10i32 - 3i32;     //→ 7
let s = 1.0f64 - 0.5f64;  //→ 0.5

Mul - 산술 곱셈 (*)

곱셈 트레잇의 정의는 다음과 같습니다.

#[lang("mul_trait")]
trait Mul<Rhs = Self> {
    type Output;

    #[infix("*")]
    fn mul(self, rhs: Rhs) -> Output;
}
let r = 6i32 * 7i32;      //→ 42
let s = 2.5f64 * 4.0f64;  //→ 10.0

사용자 정의 의미

Mul 은 산술 곱 외에도 도메인 특화된 의미를 가질 수 있으며, 대표적으로 matrix multiplication 등이 이에 해당합니다.

struct Matrix2x2 { a: f64; b: f64; c: f64; d: f64; }

impl Mul<Matrix2x2> for Matrix2x2 {
    type Output = Matrix2x2;

    fn mul(self, rhs: Matrix2x2) -> Matrix2x2 {
        Matrix2x2 {
            a: self.a * rhs.a + self.b * rhs.c,
            b: self.a * rhs.b + self.b * rhs.d,
            c: self.c * rhs.a + self.d * rhs.c,
            d: self.c * rhs.b + self.d * rhs.d,
        }
    }
}

element-wise 곱은 2.6 broadcast 의 DotMul (m .* n) 에서 다룹니다.

Div - 산술 나눗셈 (/)

나눗셈 트레잇은 다음과 같이 정의됩니다.

#[lang("div_trait")]
trait Div<Rhs = Self> {
    type Output;

    #[infix("/")]
    #[infix("÷")]
    fn div(self, rhs: Rhs) -> Output;
}
let r = 20i32 / 7i32;      //→ 2 (정수 나눗셈, 절삭)
let s = 20.0f64 / 7.0f64;  //→ 2.857142857142857

0 으로 나누기

0 으로 나누는 동작은 타입별로 처리됩니다. 정수 i32 / 0 은 panic 이며, 상수로 접히는 식 (1 / 0) 이면 컴파일 에러입니다. 실수 f64 / 0.0 은 IEEE 754 규약에 따라 ±∞ 또는 NaN 을 산출합니다.

panic 대신 none 을 반환받고 싶다면 checked_div 를 사용하면 됩니다.

let r = 20i32.checked_div(7i32);  //→ 2
let s = 20i32.checked_div(0i32);  //→ none

Rem - 산술 나머지 (%)

나머지 트레잇의 정의는 다음과 같습니다.

#[lang("rem_trait")]
trait Rem<Rhs = Self> {
    type Output;

    #[infix("%")]
    fn rem(self, rhs: Rhs) -> Output;
}
let r = 20i32 % 7i32;     //→ 6
let s = 5.5f64 % 2.0f64;  //→ 1.5 (IEEE 754 fmod)

부호 규칙은 다음과 같이 적용됩니다. 결과의 부호는 피제수 (좌측 피연산자) 의 부호를 따르므로 (-5) % 3 = -2 이고 5 % (-3) = 2 가 됩니다 (C / Rust 와 동일한 truncated division 정의).

Modulo - floored modulo (%%)

floored (몫을 내림한) 나머지 트레잇의 정의는 다음과 같습니다.

trait Modulo<Rhs = Self> {
    type Output;

    #[infix("%%")]
    fn modulo(self, rhs: Rhs) -> Output;
}
let r = (-5i32) %% 3i32;     //→ 1   ( = -5 - (-2)*3 )
let s = 5i32 %% (-3i32);     //→ -1  ( = 5 - (-2)*(-3) )
let t = (-7.5f64) %% 2.0f64; //→ 0.5

Rem 과 다른 점은 결과의 부호 규칙입니다. a %% b = a - floor(a / b) * b 로, 결과의 부호는 제수 b 를 따릅니다 (Python 의 % 와 같음). 그래서 양수 제수에 대해 결과가 항상 [0, b) 범위에 들어가 음수 피제수에서도 양의 잉여를 얻고, 음수 제수에 대해서는 (b, 0] 범위입니다 (7 %% -3 = -2). Euclidean modulo (늘 0 이상) 와는 음수 제수에서 다릅니다. 인덱스 wrap-around 나 모듈러 산술에서 자주 쓰입니다. 부동소수 %% 의 0 결과는 제수의 부호를 가집니다 (44.0 %% -4.0 = -0.0).

// 자주 쓰이는 패턴 - 음수 인덱스 wrap-around
let i: i32 = -1;
let len: i32 = 8;
let wrapped = i %% len;      //→ 7    (i % len 이라면 -1)

Rem 과 Modulo 의 부호 차이는 다음 표로 정리할 수 있습니다.

aba % b (Rem)a %% b (Modulo)
7311
-73-12
7-31-2
-7-3-1-1

두 토큰은 별도 트레잇으로 매핑되어 있으므로, 어느 한쪽만 구현하거나 두 의미를 다르게 줄 수 있습니다. 다만 표준 라이브러리의 primitive 정수 / 부동소수 impl 은 위 표의 정의를 그대로 따릅니다.

%% 토큰은 refs-lang 1.10 구두점과 연산자 토큰 에서 정의되어 있고, 우선순위는 refs-lang 3.2 표에서 % 와 같은 8 단계, 좌결합입니다.

2.3.2. 단항 트레잇

Neg - 단항 부정 (- 전위)

단항 부정 트레잇은 다음과 같이 정의됩니다.

#[lang("neg_trait")]
trait Neg {
    type Output;

    #[prefix("-")]
    fn neg(self) -> Output;
}
let r = -5i32;       //→ -5
let s = -3.14f64;    //→ -3.14
let t = -(a + b);    //→ -(a+b)

이 트레잇은 부호 있는 정수 및 실수에서만 의미를 가지며, u32 에는 Neg impl 이 존재하지 않습니다.

특히 i32::MIN (= -2147483648) 의 부정은 표현이 불가능하므로 debug 빌드에서는 panic 이 발생하고 release 빌드에서는 wrap 됩니다 (= i32::MIN 그대로). 명시적인 wrap 이 필요하다면 wrapping_neg 를, T | none 결과가 필요하다면 checked_neg 를 사용하면 됩니다.

Not - 비트 반전 / 논리 부정 (!)

Not 트레잇의 정의는 다음과 같습니다.

#[lang("not_trait")]
trait Not {
    type Output;

    #[prefix("!")]
    fn not(self) -> Output;
}
let r = !true;       //→ false
let s = !0u8;        //→ 255 (모든 비트 1)
let t = !0xFFi32;    //→ 0xFFFFFF00 (전 32비트 반전)

타입별 동작은 다음과 같습니다. bool::not(true) = false 이고 bool::not(false) = true 가 되어 논리 부정으로 동작합니다. 반면 u8::not(0) = 0xFF 와 i32::not(0) = -1 의 경우는 비트 반전으로 작용합니다.

BitNot - 비트 반전 (~)

~ 토큰 (refs-lang 1.10.3) 은 정수 전용 비트 반전입니다.

#[lang("bitnot_trait")]
trait BitNot {
    type Output;

    #[prefix("~")]
    fn bitnot(self) -> Output;
}
let s = ~0u8;        //→ 255
let t = ~0i32;       //→ -1

정수 타입에 대해 ~x 는 !x 와 같은 결과를 냅니다. 다만 BitNot 은 i* / u* 에만 구현되며 bool 에는 구현되지 않으므로, ~true 는 컴파일 에러입니다. 논리 부정에는 ! 를, 비트 반전임을 드러내고 싶을 때에는 ~ 를 쓰는 관용을 권장합니다.

2.3.3. 비트 트레잇

BitAnd - 비트 AND (&)

비트 AND 트레잇은 다음과 같이 정의됩니다.

#[lang("bitand_trait")]
trait BitAnd<Rhs = Self> {
    type Output;

    #[infix("&")]
    fn bitand(self, rhs: Rhs) -> Output;
}
let r = 0xF0i32 & 0x0Fi32;   //→ 0x00
let s = 0xFFi32 & 0x0Fi32;   //→ 0x0F
let b = true & false;         //→ false (bool 도 BitAnd impl 가짐)

참고

&& 와 & 는 다릅니다. && 는 short-circuit 논리 AND, & 는 비트 AND. bool 에 대해 두 결과가 같지만 평가 시점이 다름.

BitOr - 비트 OR (|)

비트 OR 트레잇의 정의는 다음과 같습니다.

#[lang("bitor_trait")]
trait BitOr<Rhs = Self> {
    type Output;

    #[infix("|")]
    fn bitor(self, rhs: Rhs) -> Output;
}
let r = 0xF0i32 | 0x0Fi32;   //→ 0xFF

BitXor - 비트 XOR (^)

비트 XOR 트레잇은 다음과 같이 정의됩니다.

#[lang("bitxor_trait")]
trait BitXor<Rhs = Self> {
    type Output;

    #[infix("^")]
    fn bitxor(self, rhs: Rhs) -> Output;
}
let r = 0xFFi32 ^ 0x0Fi32;   //→ 0xF0
let s = 0b1010 ^ 0b1100;     //→ 0b0110

Shl - 좌 비트 시프트 (<<)

좌 시프트 트레잇의 정의는 다음과 같습니다.

#[lang("shl_trait")]
trait Shl<Rhs = Self> {
    type Output;

    #[infix("<<")]
    fn shl(self, rhs: Rhs) -> Output;
}
let r = 1i32 << 4;     //→ 16
let s = 0xFFi32 << 24; //→ 0xFF000000

shift count 가 비트폭 이상일 때의 동작은 다음과 같이 처리됩니다. debug 빌드에서는 panic 이 발생하며, release 빌드에서는 wrap 으로 처리됩니다 (실제로는 LLVM 정의상 undefined 에 가까우므로 사용자에게 명시적 wrap 사용을 권장합니다).

let r = 1i32.wrapping_shl(40);  // 명시 - 40 % 32 = 8 만큼 shift

Shr - 우 비트 시프트 (>>)

우 시프트 트레잇은 다음과 같이 정의됩니다.

#[lang("shr_trait")]
trait Shr<Rhs = Self> {
    type Output;

    #[infix(">>")]
    fn shr(self, rhs: Rhs) -> Output;
}
let r = 256i32 >> 4;  //→ 16
let s = -1i32 >> 1;   //→ -1 (signed = arithmetic shift, sign extension)
let t = -1u32 >> 1;   //→ 0x7FFFFFFF (unsigned = logical shift)

부호 있는 정수의 경우 arithmetic shift (sign-extend) 가 적용되고, 부호 없는 정수의 경우 logical shift 가 적용됩니다.

2.3.4. 인덱싱 트레잇

인덱싱과 슬라이싱은 언어 syntax (refs-lang 3.6) 에 해당하지만, 실제 dispatch 는 본 절의 트레잇을 통해 이루어집니다.

Index - 단일 원소 읽기 (arr[i])

Index 트레잇의 정의는 다음과 같습니다.

#[lang("index_trait")]
trait Index<Idx> {
    type Output;

    fn index(self, idx: Idx) -> Output;
}
let xs: List<i32> = [10, 20, 30];
let v = xs[1];        //→ 20
let s: str = "hello";
let c = s.chars().next();   //→ 'h' (char | none) - str 에는 Index 가 없음 (2.9.8)

Bounds 검사

경계 처리는 다음과 같이 이루어집니다. 범위 밖 인덱스 (xs[100]) 는 panic 합니다 (연산자는 에러를 던지지 않습니다). 부호 있는 정수 인덱스가 음수이면 끝에서부터 셉니다 (xs[-1] 은 마지막 원소, refs-lang 3.6.1). len + i < 0 이면 panic 입니다.

panic 대신 T | none 결과를 받고 싶다면 xs.get(i) 를 사용하면 됩니다.

let v = xs.get(100);  //→ none

IndexSet - 단일 원소 쓰기 (arr[i] = v)

IndexSet 트레잇은 다음과 같이 정의됩니다.

#[lang("index_set_trait")]
trait IndexSet<Idx> {
    type Value;

    fn index_set(self, idx: Idx, value: Value);     // 컬렉션은 참조 핸들 - self
}
let xs: List<i32> = [10, 20, 30];
xs[1] = 99;           // IndexSet::index_set(xs, 1, 99)
xs[1];                //→ 99

Index 와 분리한 이유는, 읽기만 가능한 컨테이너 (예: 튜플의 immutable view) 도 Index 는 구현할 수 있어야 하기 때문입니다. 따라서 IndexSet 은 별도의 opt-in 으로 분리됩니다.

IndexSet 도 범위를 벗어나면 panic 이 발생하며, xs.set(i, v) 의 경우 IndexError 를 던집니다 (throws (IndexError)).

Sliceable - 부분 추출 (s[a:b], s[a:b:step])

슬라이스 트레잇의 정의는 다음과 같습니다. 트레잇 이름은 Sliceable 이고, 결과 view 타입이 Slice<T> (2.9.7) 입니다. 묵시 glob 아래에서 한 이름이 둘을 가리킬 수 없으므로 이름을 나눴습니다.

#[lang("slice_trait")]
trait Sliceable<R> {
    type Output;

    fn slice(self, range: R) -> Output;
}

xs[a:b] 는 xs.slice(a..b), xs[a:b:k] 는 Python 의 slice.indices 로 구한 범위의 xs.slice(..).step(k) 입니다 (refs-lang 3.6.2). 의미는 Python 슬라이스와 같아서 경계를 길이에 맞춰 잘라 맞추고 범위 때문에 panic 하지 않으며, xs 는 한 번만 평가됩니다. xs[a..b] 와 xs[..] 도 같습니다. 모든 Sliceable<Range<usize>> 구현은 rok::core::index::clamp_range 로 범위를 잘라 맞춥니다.

let xs: List<i32> = [1, 2, 3, 4, 5];
let s = xs[1:3];       //→ Slice<i32> view of [2, 3]
let r = xs[::2];       //→ [1, 3, 5] (step=2)
let v = xs[::-1];      //→ [5, 4, 3, 2, 1] (역순)

let str_part = "hello"[1:4];  //→ "ell" (새 str)

표준 라이브러리의 구현은 Sliceable<Range<usize>> 이며 대상은 List<T>, Slice<T>, [T; N], str, StrBuf 입니다. List / Slice / 배열은 Slice<T> view 를, str / StrBuf 는 UTF-8 경계를 검사한 새 str 사본을 돌려줍니다. 음수 경계는 끝에서부터 센 뒤 잘라 맞추며, 슬라이스 대입은 지원하지 않습니다. 소유한 사본이 필요하면 xs[1:3].to_list() 를 씁니다.

2.3.5. 트레잇 구현 매트릭스

primitive 타입에 대한 ops 트레잇 impl 은 모두 #[intrinsic("rok.OP.TYPE")] 부착으로 컴파일러 백엔드 PrimOp 에 직결됩니다 (0.3). 그 의미는 다음과 같습니다.

  • primitive 피연산자의 연산자 (a + b) 는 메서드 호출을 거치지 않고 바로 기계 연산으로 lower 됩니다.
  • 이름으로 부른 호출 (a.add(b), Add::add(a, b)) 도 같은 기계 연산으로 lower 됩니다.

산술 트레잇

타입모듈동작
i8..i64, isizerok::core::numrok.add.i32 / rok.sub.i32 / rok.rem.i32 / rok.mod.i32 ...
u8..u64, usizerok::core::numrok.add.u32 / ... (unsigned 계열, Modulo ≡ Rem)
f32, f64rok::core::numrok.add.f32 / rok.add.f64 / rok.mod.f64 (floored)
introk::core::numauto-promote 로직
floatrok::core::num(위와 동일 - auto-promote)

비트 트레잇

타입모듈intrinsic
i*, u*, boolrok::core::numrok.and.OP, rok.or.OP, rok.xor.OP, rok.shl.OP, rok.shr.OP

f32 와 f64 의 경우에는 의미가 없으므로 BitAnd 등이 구현되어 있지 않습니다.

인덱싱 트레잇

타입정의 위치
List<T>표준 라이브러리
Dict<K, V>표준 라이브러리 (Idx = K)
[T; N]컴파일러 합성 (primitive array)
str표준 라이브러리 (Slice 만 - Index<usize> 미정의, byte 직접 접근 거부)
Slice<T>표준 라이브러리
Tuple컴파일러 합성 (.0, .1 형태) - Index 미정의 (compile-time tuple 접근만)

2.3.6. 사용자 정의 ops

표준 라이브러리 외부의 사용자도 impl Add for MyType 등을 통해 자유롭게 트레잇을 구현할 수 있습니다. 이 경우 trait dispatch 가 MyType::add 호출로 이어지게 됩니다.

struct Vec3 { x: f64; y: f64; z: f64; }

impl Add for Vec3 {
    type Output = Vec3;

    fn add(self, rhs: Vec3) -> Vec3 {
        Vec3 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
        }
    }
}

let a = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
let b = Vec3 { x: 0.5, y: 0.5, z: 0.5 };
let c = a + b;        //→ Vec3 { x: 1.5, y: 2.5, z: 3.5 }

다만 사용자 ops 에 #[intrinsic] 을 부착하는 것은 금지 됩니다 (refs-standard-libraries 0.3.5). 본문이 권위를 가지기 때문입니다.

2.3.7. 트레잇 사이의 관계 - Output 일관성

여러 트레잇이 결합되는 식 (a + b * c) 에서는 Output 타입이 일관되어야 합니다.

let r = a + b * c;
//   = Add::add(a, Mul::mul(b, c))
//   요구: <typeof(b) as Mul<typeof(c)>>::Output 가 Add 의 Rhs 와 호환

표준 라이브러리의 모든 primitive 산술 impl 은 Output = Self 를 따릅니다. 사용자 타입은 자유롭게 정의할 수 있지만, 컴파일러 에러 메시지 등 도구의 친화성을 확보하기 위해서는 Output = Self 를 권장합니다.

2.3.8. broadcast 트레잇과의 관계

a + b 는 Add::add 로 dispatch 되고, a .+ b 는 DotAdd::dot_add 로 dispatch 되므로 두 연산은 별개의 트레잇으로 처리됩니다. 그러나 일반적인 산술 타입 (Vec3 등) 에서는 두 트레잇을 모두 구현하는 것이 보통입니다. 자세한 형태는 2.6 broadcast 에서 다룹니다.

2.3.9. 논리 트레잇 - LogicalAnd (&&) / LogicalOr (||)

&& 와 || 도 다른 연산자와 같은 방식으로 트레잇 메서드에 연결됩니다. 우선순위와 결합성은 refs-lang 3.2 표가 고정합니다.

#[lang("logical_and_trait")]
trait LogicalAnd<Rhs = Self> {
    #[infix("&&")]
    fn logical_and(self, rhs: Rhs) -> bool;
}

#[lang("logical_or_trait")]
trait LogicalOr<Rhs = Self> {
    #[infix("||")]
    fn logical_or(self, rhs: Rhs) -> bool;
}

표준 라이브러리는 이 두 트레잇을 bool 에 대해서만 구현합니다. 두 피연산자가 모두 bool 일 때 컴파일러는 logical_and_trait / logical_or_trait 슬롯을 통해 이 구현을 식별하고, 메서드 호출 대신 short-circuit 분기로 lower 합니다 - a && b 는 a 가 false 이면 b 를 평가하지 않습니다.

경고

사용자 타입이 LogicalAnd / LogicalOr 를 구현하면 a && b 는 일반 트레잇 메서드 호출이 되므로, 두 피연산자가 모두 평가 됩니다 (short-circuit 없음). 이는 broadcast 형태 .&& / .|| (2.6) 와 같은 규칙입니다. short-circuit 은 bool 피연산자에 한해 언어가 보장합니다.

2.3.10. Unicode 수학 연산자 트레잇

refs-lang 1.10.3 의 Unicode 연산자는 다음 트레잇에 연결됩니다. 모든 메서드는 ASCII 이름을 가지므로 연산자 대신 메서드로도 부를 수 있습니다. 우선순위는 refs-lang 3.2 표가 고정합니다. ÷ 는 위 Div 의 동의어이고, ≡ ≠ ≤ ≥ 는 2.5 cmp 의 동의어입니다.

trait Cross<Rhs = Self> {               // a × b - 8 단계
    type Output;
    #[infix("×")]
    fn cross(self, rhs: Rhs) -> Output;
}

trait Dot<Rhs = Self> {                 // a ⋅ b - 8 단계
    type Output;
    #[infix("⋅")]
    fn dot(self, rhs: Rhs) -> Output;
}

trait Compose<Rhs> {                    // f ∘ g - 9 단계, (f ∘ g)(x) = f(g(x))
    type Output;
    #[infix("∘")]
    fn compose(self, rhs: Rhs) -> Output;
}

trait Sqrt {                            // √x - 전위 단항
    type Output;
    #[prefix("√")]
    fn sqrt(self) -> Output;
}

trait Contains<Item> {                  // a ∈ s, a ∉ s - 4 단계
    #[infix("∈", swap)]
    fn contains(self, item: Item) -> bool;
    #[infix("∉", swap)]
    fn not_contains(self, item: Item) -> bool { !self.contains(item) }
}

trait Subset<Rhs = Self> {              // a ⊂ b, a ⊆ b - 4 단계
    #[infix("⊂")]
    fn is_proper_subset(self, rhs: Rhs) -> bool;
    #[infix("⊆")]
    fn is_subset(self, rhs: Rhs) -> bool;
}

trait Union<Rhs = Self> {               // a ∪ b - 7 단계
    type Output;
    #[infix("∪")]
    fn union(self, rhs: Rhs) -> Output;
}

trait Intersect<Rhs = Self> {           // a ∩ b - 8 단계
    type Output;
    #[infix("∩")]
    fn intersection(self, rhs: Rhs) -> Output;
}

∈ ∉ 는 컨테이너가 오른쪽에 오므로 swap 으로 연결합니다 (refs-lang 6.7.2). a ∈ xs 는 xs.contains(a) 입니다.

트레잇표준 라이브러리 구현
Sqrtf32, f64 (#[intrinsic("rok.sqrt.TYPE")])
ContainsList<T>, Set<T>, Dict<K, V> (키), str (부분 문자열), Range<T>
Subset, Union, IntersectSet<T>
Compose함수 타입 fn(B) -> C 와 fn(A) -> B
Cross, Dot표준 라이브러리 구현 없음 - 벡터 타입이 구현

GitHub에서 이 페이지 고치기