"코드가 시가 되는 언어" 토파즈의 문법은 최소한의 코드로 최대한의 표현력을 추구합니다. 이 가이드에서 토파즈만의 아름다운 문법을 마스터해보세요. 🌟
🎯 핵심 철학
"Write Less, Express More"
복잡한 로직도 간결하고 읽기 쉽게 표현할 수 있습니다.
글로벌 문법, 로컬 표현
- 키워드: 영어로 통일 (
function,let,match,case) - 식별자: 한글, 영어, 이모지 자유롭게 사용 가능
모든 것은 표현식
if, match, for, try 등 모든 구문이 값을 반환합니다.
📝 기본 문법
변수 선언
// 불변 변수 (기본)
let 이름 = "김토파즈"
let 나이 = 25
let 활성화 = true
// 가변 변수
mut let 점수 = 85
점수 = 90 // 변경 가능
// 타입 명시 (선택적)
let 거리: float = 3.14
let 사용자들: Array<string> = ["김철수", "박영희"]
null 병합 할당 (??=)
대상 값이 None/null일 때만 초기화하는 문장 전용 연산자입니다.
let mut 이름: Option<string> = None
이름 ??= Some("guest")
함수 정의
// 기본 함수
function 인사하기(이름: string) -> string {
"안녕하세요, {이름}님!"
}
// 기본값 매개변수
function 계산하기(x: int, y: int = 10) -> int {
x + y
}
// 다중 반환값
function 좌표계산() -> (int, int) {
(100, 200)
}
// 고차 함수
function 변환(데이터: Array<int>, 함수: (int) -> int) -> Array<int> {
데이터.map(함수)
}
조건문
// if 표현식 (값을 반환)
let 메시지 = if 나이 >= 20 {
"성인입니다"
} else if 나이 >= 13 {
"청소년입니다"
} else {
"어린이입니다"
}
// 삼항 연산자 스타일
let 상태 = if 온라인 { "접속중" } else { "오프라인" }
패턴 매칭
// 기본 매칭
let 결과 = match 값 {
case 1 => "하나"
case 2 => "둘"
case 3 => "셋"
case _ => "기타"
}
// 범위 매칭
let 등급 = match 점수 {
case 90..100 => "A"
case 80..<90 => "B"
case 70..<80 => "C"
case _ => "F"
}
// 구조 매칭
let 할인율 = match 고객 {
case { 등급: "VIP", 구매액 } if 구매액 > 1000000 => 0.3
case { 등급: "VIP" } => 0.2
case { 가입일 } if 오늘 - 가입일 > 365일 => 0.1
case _ => 0.0
}
#### 리스트 패턴
```rust
match xs {
case [head, ..tail] => 사용(head, tail)
case [..init, last] => 마지막사용(init, last)
case [a, .., z] => 양끝사용(a, z)
case [x, y, ..rest] if rest.length > 0 => 처리(x, y, rest)
}
엔드투엔드 예시(가드 + 중첩 리스트 패턴):
let 설명 = match 숫자들 {
case [] => "빈 리스트"
case [단일] => "하나"
case [첫, .., 끝] if 첫 < 끝 => "오름차순 경향"
case [첫, ..중간, 끝] => "양끝 {첫}..{끝}, 중간={중간.length}"
}
## 🌊 파이프라인 연산
### 기본 파이프라인
```rust
// 데이터가 흐르는 듯한 표현
let 결과 = 원본데이터
|> 정규화()
|> 필터링(x => x > 0)
|> 맵핑(x => x * 2)
|> 정렬()
메서드 체이닝과 혼용
let 처리된문자열 = "hello world"
.split(" ")
|> 각각(단어 => 단어.첫글자대문자())
.join(" ")
|> 추가("!", _)
// 결과: "Hello World!"
파이프 슈거
v4파이프 오른쪽에서 간결한 표기법을 사용할 수 있습니다.
값
|> .length // 속성 슈거: (x => x.length)
|> replace("foo", _, "bar") // 메서드 슈거: (x => replace("foo", x, "bar"))
// 여러 개의 _ 는 가장 가까운 호출에 왼쪽부터 순서대로 바인딩
값 |> format("id=", _, ":", _)
🧠 타입 시스템
타입 추론
// 타입이 자동으로 추론됩니다
let 숫자 = 42 // int
let 문자열 = "안녕" // string
let 배열 = [1, 2, 3, 4] // Array<int>
let 구조체 = { // { 이름: string, 나이: int }
이름: "김토파즈",
나이: 25
}
리터럴 타입
// 정확한 값으로 타입 제한
type 신호등 = "빨강" | "노랑" | "초록"
type 주사위 = 1 | 2 | 3 | 4 | 5 | 6
type 상태코드 = 200 | 404 | 500
function 신호처리(색: 신호등) {
match 색 {
case "빨강" => 정지()
case "노랑" => 주의()
case "초록" => 출발()
// 모든 경우를 처리했는지 컴파일러가 확인!
}
}
유니온 타입
type 사용자입력 = string | int | null
function 처리(입력: 사용자입력) -> string {
match 입력 {
case s: string => "문자열: {s}"
case n: int => "숫자: {n}"
case null => "입력 없음"
}
}
🔁 반복문
for 루프 (표현식)
// 범위 반복
for i in 1..10 {
print("숫자: {i}")
}
// 배열 반복
for 항목 in ["사과", "바나나", "체리"] {
print("과일: {항목}")
}
// 인덱스와 함께
for (인덱스, 값) in 데이터.enumerate() {
print("{인덱스}: {값}")
}
// for도 값을 반환!
let 제곱수들 = for x in 1..5 { x * x } // [1, 4, 9, 16]
by를 사용한 범위 스텝
v4
// 증가 스텝
for i in 0..10 by 2 { print(i) } // 0,2,4,6,8,10
// 감소 스텝 (음수 stride)
for i in 10..0 by -3 { print(i) } // 10,7,4,1
// 날짜/시간 스텝 (라이브러리 기간 단위)
for day in 시작일..종료일 by 1.day { 일정.추가(day) }
while 루프
mut let 카운터 = 0
while 카운터 < 10 {
print("카운터: {카운터}")
카운터 = 카운터 + 1
}
📚 컬렉션
배열
// 기본 배열
let 숫자들 = [1, 2, 3, 4, 5]
let 과일들 = ["사과", "바나나", "체리"]
// 배열 메서드
let 큰수들 = 숫자들.filter(x => x > 2) // [3, 4, 5]
let 제곱들 = 숫자들.map(x => x * x) // [1, 4, 9, 16, 25]
let 합계 = 숫자들.reduce(0, +) // 15
멤버십 (in)
// 배열/리스트/집합
let ok1 = 3 in [1, 2, 3]
let ok2 = "a" in Set.of("a", "b")
// 맵 키
let hasId = "id" in 사용자맵.keys
// 범위
let inside = 5 in 1..10
let outside = 10 in 1..<10
객체/구조체
// 객체 리터럴
let 사용자 = {
이름: "김토파즈",
나이: 25,
이메일: "topaz@example.com"
}
// 구조체 정의
struct 사용자 {
이름: string,
나이: int,
이메일: string
}
// 구조체 인스턴스
let 새사용자 = 사용자 {
이름: "박루비",
나이: 30,
이메일: "ruby@example.com"
}
레코드 업데이트 리터럴
v4불변으로 얕은 복사를 한 뒤 선택한 필드만 갱신합니다.
let 사용자 = { 이름: "Alice", 나이: 20, 도시: "Seoul" }
let 갱신 = 사용자{ 나이: 사용자.나이 + 1, 도시: "Busan" }
// { 이름: "Alice", 나이: 21, 도시: "Busan" }
🎭 문자열 템플릿
기본 보간
let 이름 = "토파즈"
let 나이 = 25
let 인사 = "안녕하세요, {이름}님! 나이가 {나이}세이시군요."
태그드 템플릿
v4표준 태그와 안전/메타 보존:
let 경로 = p"/home/{사용자}/docs/{파일명}" // 경로 정규화
let 패턴 = r"^[a-z0-9_]+$" // 정규식 이스케이프 간소화
let 명령 = sh"grep {패턴} {파일}" // 안전 셸 템플릿(실행 정책 적용)
// SQL: 보간은 항상 파라미터 바인딩으로 처리 (직접 문자열 삽입 금지)
let 쿼리 = sql"SELECT * FROM users WHERE age > {나이} AND city = {도시}"
⚡ 비동기 처리
자동 비동기
// I/O는 자동으로 비동기 처리되지만 동기처럼 작성!
function 사용자정보가져오기(id: int) -> 사용자 {
let 기본정보 = API.사용자조회(id) // 자동 비동기 처리
let 프로필 = API.프로필조회(id) // 자동 비동기 처리
사용자 {
기본정보: 기본정보,
프로필: 프로필
}
}
병렬 실행
// 여러 작업을 병렬로 실행
let 결과 = concurrent {
날씨: 날씨API.현재날씨("서울"),
환율: 환율API.달러환율(),
뉴스: 뉴스API.헤드라인(5개)
}
🛡️ 에러 처리
Result 타입
function 안전한나누기(a: int, b: int) -> Result<int, string> {
if b == 0 {
Err("0으로 나눌 수 없습니다")
} else {
Ok(a / b)
}
}
// ? 연산자로 간단하게
function 복잡한계산(x: int) -> Result<int, string> {
let 결과1 = 안전한나누기(x, 2)?
let 결과2 = 안전한나누기(결과1, 3)?
Ok(결과2 * 10)
}
try 표현식
let 설정 = try {
파일읽기("config.json")?
} else {
// 기본값 반환
{ 테마: "다크", 언어: "한국어" }
}
🎨 고급 기능
부분 적용
let 더하기10 = 더하기(10, _)
let 결과 = [1, 2, 3].map(더하기10) // [11, 12, 13]
옵션-안전 옵셔널 체이닝 (?.)
v4좌변이 Option<T> 또는 T | null 같은 null 포함 유니온일 때만 사용하는 연산자입니다. 좌→우로 단락 평가하며 내부적으로 Option.map/flatMap으로 환원됩니다.
let 사용자: Option<{ 이름: string, 프로필: Option<{ 도시: string }> }> =
Some({ 이름: "Ann", 프로필: Some({ 도시: "Seoul" }) })
let 이름옵션 = 사용자?.이름
let 도시옵션 = 사용자?.프로필?.도시
// ??와 조합해 기본값 제공
let 도시 = 사용자?.프로필?.도시 ?? "Unknown"
// 일반 객체 체이닝이 아닙니다 — Option 이 아닌 값에는 `.` 사용
let 일반 = { 이름: "Jo" }
let ok = 일반.이름
동등한 map/flatMap 표현:
// 사용자?.프로필?.도시
let 함수형 = 사용자
|> Option.flatMap(_, u => u.프로필)
|> Option.map(_, p => p.도시)
매크로
// 반복 실행 매크로
macro 반복실행(횟수: int) {
for i in 1..횟수 {
print("실행 {i}/{횟수}")
yield // 사용자 코드 실행
}
}
반복실행(3) {
중요한작업()
}
🔧 실용적 예제
웹 API 호출
let 사용자데이터 = fetch("https://api.example.com/users/1")
|> json()
|> (data => {
이름: data.name,
이메일: data.email,
가입일: Date.parse(data.created_at)
})
데이터 처리 파이프라인
let 분석결과 = CSV.읽기("sales.csv")
|> 필터(행 => 행.매출 > 1000000)
|> 그룹화(행 => 행.지역)
|> 집계(그룹 => {
지역: 그룹.키,
총매출: 그룹.값.합계(행 => 행.매출),
평균매출: 그룹.값.평균(행 => 행.매출)
})
|> 정렬(내림차순: 행 => 행.총매출)
🎯 다음 단계
이제 토파즈의 기본 문법을 마스터했습니다! 다음으로 학습할 내용:
토파즈와 함께 코딩이 시가 되는 경험을 계속해보세요! ✨