// 제대로 반납하는 버전. // // Atomic Updater와 비교하는 것이 목적이다. 자원이 두 층이고, 반납이 // 파괴가 아니며, 획득이 실패할 수 있다 — 파일에는 셋 다 없었다. import "../std-draft/db" as Db // 한 요청. 성공하든 실패하든 lease를 반납한다. pub fn load_user(db: Db.Db, pool: Db.Pool, sql: String) effects {Db.acquire, Db.query, Db.release} -> Result[List[String], Db.DbError] { match db.acquire(pool) { Err(e) => Err(e), // l을 소비하는 인자와 l을 쓰는 인자를 한 호출에 섞을 수 없다. // move 검사는 인자를 왼쪽부터 걷기 때문이다 — 실제 평가 순서와 // 무관하다. 먼저 질의하고 결과를 넘긴다. Ok(l) => { let r = db.query(l, sql) release_then(db, pool, l, r) }, } } // 반납하고 원래 결과를 그대로 돌려준다. // 반납 실패는 일부러 버린다 — 원래 결과가 더 중요하다. // // 파일 때와 다른 점: 정리가 실패 경로마다 다르지 않다. 성공이든 실패든 // 같은 일(반납)을 하므로 결과를 통째로 실어나르는 도우미 하나로 접힌다. pub fn release_then( db: Db.Db, pool: Db.Pool, own l: Db.Lease, r: Result[List[String], Db.DbError], ) effects {Db.release} -> Result[List[String], Db.DbError] { let _ = db.release(pool, l) r } // 프로그램 하나. 풀을 열고 쓰고 닫는다. pub fn run(db: Db.Db, url: String, sql: String) effects {Db.pool, Db.acquire, Db.query, Db.release, Db.close_pool} -> Result[List[String], Db.DbError] { match db.pool(url, 8) { Err(e) => Err(e), Ok(pool) => { let rows = load_user(db, pool, sql) close_then(db, pool, rows) }, } } pub fn close_then( db: Db.Db, own pool: Db.Pool, r: Result[List[String], Db.DbError], ) effects {Db.close_pool} -> Result[List[String], Db.DbError] { let _ = db.close_pool(pool) r }