Renderingen-US

Errors and catching

Faber does not throw errors the way most languages do. A failure travels back through a dedicated error channel, declared in the signature and separate from the return value. That one decision shapes everything on this page.

The error channel#

→ declares what a function returns. ⇥ declares what it may fail with.

reader locale
faber convert --to en — English reader surface
fn divide(int a, int b) → int ⇥ string {
    if b ≡ 0 {
        throw "division by zero"
    }
    return a / b
}
faber convert --to la — canonical Faber
functio divide(numerus a, numerus b) → numerus ⇥ textus {
    si b ≡ 0 {
        iace "division by zero"
    }
    redde a / b
}
faber convert --to th-TH — Thai
ฟังก์ชัน divide(จำนวน a, จำนวน b) → จำนวน ⇥ ข้อความ {
    ถ้า b ≡ 0 {
        โยน "division by zero"
    }
    คืน a / b
}
faber convert --to zh-Hans — Simplified Chinese
函数 divide(整数 a, 整数 b) → 整数 ⇥ 文本 {
    如果 b ≡ 0 {
        抛错 "division by zero"
    }
    返回 a / b
}
faber convert --to zh-Hant — Traditional Chinese
函式 divide(整數 a, 整數 b) → 整數 ⇥ 文字 {
    若 b ≡ 0 {
        拋出 "division by zero"
    }
    傳回 a / b
}
faber convert --to vi — Vietnamese
hàm divide(số a, số b) → số ⇥ văn_bản {
    nếu b ≡ 0 {
        ném "division by zero"
    }
    trả a / b
}
faber convert --to ar — Arabic
دالة divide(عدد a, عدد b) → عدد ⇥ نص {
    إذا b ≡ 0 {
        ارم "division by zero"
    }
    أعد a / b
}
faber convert --to hi — Hindi
फलन divide(संख्या a, संख्या b) → संख्या ⇥ पाठ {
    यदि b ≡ 0 {
        इधरफेंको "division by zero"
    }
    लौटाओ a / b
}

Read it as: returns a number, or fails with a string. The two channels are independent — the success type is not widened, wrapped, or made optional to accommodate failure. A caller reading the signature knows both shapes before looking at the body.

throw sends a value down the error channel. It is not return: it exits by the other route.

Guards: require / reject#

A guard pairs the condition and the throw in one statement. require (English require) throws when the condition fails; reject (English reject) throws when the condition holds:

reader locale
faber convert --to en — English reader surface
fn divide(int a, int b) → int ⇥ string {
    require b ≠ 0 throw "division by zero"
    return a / b
}

fn exigePositivum(int value) → int ⇥ string {
    reject value ≺ 0 throw "negative value"
    return value
}
faber convert --to la — canonical Faber
functio divide(numerus a, numerus b) → numerus ⇥ textus {
    requirit b ≠ 0 iace "division by zero"
    redde a / b
}

functio exigePositivum(numerus value) → numerus ⇥ textus {
    reice value ≺ 0 iace "negative value"
    redde value
}
faber convert --to th-TH — Thai
ฟังก์ชัน divide(จํานวน a, จํานวน b) → จํานวน ⇥ ข้อความ {
    ต้องการ b ≠ 0 โยน "division by zero"
    คืน a / b
}

ฟังก์ชัน exigePositivum(จํานวน value) → จํานวน ⇥ ข้อความ {
    ปฏิเสธ value ≺ 0 โยน "negative value"
    คืน value
}
faber convert --to zh-Hans — Simplified Chinese
函数 divide(整数 a, 整数 b) → 整数 ⇥ 文本 {
    需求 b ≠ 0 抛错 "division by zero"
    返回 a / b
}

函数 exigePositivum(整数 value) → 整数 ⇥ 文本 {
    拒绝 value ≺ 0 抛错 "negative value"
    返回 value
}
faber convert --to zh-Hant — Traditional Chinese
函式 divide(整數 a, 整數 b) → 整數 ⇥ 文字 {
    需要 b ≠ 0 拋出 "division by zero"
    傳回 a / b
}

函式 exigePositivum(整數 value) → 整數 ⇥ 文字 {
    拒絕 value ≺ 0 拋出 "negative value"
    傳回 value
}
faber convert --to vi — Vietnamese
hàm divide(số a, số b) → số ⇥ văn_bản {
    yêu_cầu b ≠ 0 ném "division by zero"
    trả a / b
}

hàm exigePositivum(số value) → số ⇥ văn_bản {
    từ_chối value ≺ 0 ném "negative value"
    trả value
}
faber convert --to ar — Arabic
دالة divide(عدد a, عدد b) → عدد ⇥ نص {
    يتطلب b ≠ 0 ارم "division by zero"
    أعد a / b
}

دالة exigePositivum(عدد value) → عدد ⇥ نص {
    يرفض value ≺ 0 ارم "negative value"
    أعد value
}
faber convert --to hi — Hindi
फलन divide(संख्या a, संख्या b) → संख्या ⇥ पाठ {
    आवश्यक b ≠ 0 इधरफेंको "division by zero"
    लौटाओ a / b
}

फलन exigePositivum(संख्या value) → संख्या ⇥ पाठ {
    अस्वीकार value ≺ 0 इधरफेंको "negative value"
    लौटाओ value
}

Like throw itself, guards need the function to declare a ⇥ channel.

Catching#

catch binds whatever came down the error channel.

reader locale
faber convert --to en — English reader surface
fn divide(int a, int b) → int ⇥ string {
    if b ≡ 0 {
        throw "division by zero"
    }
    return a / b
}

fn tutum(int a, int b) → int {
    do {
        return divide(a, b)
    }
    catch err {
        warn err
        return 0
    }
}

main {
    print tutum(10, 2)
    print tutum(10, 0)
}
faber convert --to la — canonical Faber
functio divide(numerus a, numerus b) → numerus ⇥ textus {
    si b ≡ 0 {
        iace "division by zero"
    }
    redde a / b
}

functio tutum(numerus a, numerus b) → numerus {
    fac {
        redde divide(a, b)
    }
    cape err {
        mone err
        redde 0
    }
}

incipit {
    nota tutum(10, 2)
    nota tutum(10, 0)
}
faber convert --to th-TH — Thai
ฟังก์ชัน divide(จำนวน a, จำนวน b) → จำนวน ⇥ ข้อความ {
    ถ้า b ≡ 0 {
        โยน "division by zero"
    }
    คืน a / b
}

ฟังก์ชัน tutum(จำนวน a, จำนวน b) → จำนวน {
    ทำ {
        คืน divide(a, b)
    }
    จับ err {
        เตือน err
        คืน 0
    }
}

เริ่ม {
    บันทึก tutum(10, 2)
    บันทึก tutum(10, 0)
}
faber convert --to zh-Hans — Simplified Chinese
函数 divide(整数 a, 整数 b) → 整数 ⇥ 文本 {
    如果 b ≡ 0 {
        抛错 "division by zero"
    }
    返回 a / b
}

函数 tutum(整数 a, 整数 b) → 整数 {
    执行 {
        返回 divide(a, b)
    }
    捕获 err {
        警告 err
        返回 0
    }
}

入口 {
    显示 tutum(10, 2)
    显示 tutum(10, 0)
}
faber convert --to zh-Hant — Traditional Chinese
函式 divide(整數 a, 整數 b) → 整數 ⇥ 文字 {
    若 b ≡ 0 {
        拋出 "division by zero"
    }
    傳回 a / b
}

函式 tutum(整數 a, 整數 b) → 整數 {
    執行 {
        傳回 divide(a, b)
    }
    捕捉 err {
        警告 err
        傳回 0
    }
}

入口 {
    註記 tutum(10, 2)
    註記 tutum(10, 0)
}
faber convert --to vi — Vietnamese
hàm divide(số a, số b) → số ⇥ văn_bản {
    nếu b ≡ 0 {
        ném "division by zero"
    }
    trả a / b
}

hàm tutum(số a, số b) → số {
    làm {
        trả divide(a, b)
    }
    bắt err {
        cảnh_báo err
        trả 0
    }
}

bắt_đầu {
    ghi_chú tutum(10, 2)
    ghi_chú tutum(10, 0)
}
faber convert --to ar — Arabic
دالة divide(عدد a, عدد b) → عدد ⇥ نص {
    إذا b ≡ 0 {
        ارم "division by zero"
    }
    أعد a / b
}

دالة tutum(عدد a, عدد b) → عدد {
    افعل {
        أعد divide(a, b)
    }
    التقط err {
        نبه err
        أعد 0
    }
}

بداية {
    اعرض tutum(10, 2)
    اعرض tutum(10, 0)
}
faber convert --to hi — Hindi
फलन divide(संख्या a, संख्या b) → संख्या ⇥ पाठ {
    यदि b ≡ 0 {
        इधरफेंको "division by zero"
    }
    लौटाओ a / b
}

फलन tutum(संख्या a, संख्या b) → संख्या {
    करो {
        लौटाओ divide(a, b)
    }
    पकड़ो err {
        चेताओ err
        लौटाओ 0
    }
}

आरंभ {
    दिखाओ tutum(10, 2)
    दिखाओ tutum(10, 0)
}

tutum has no ⇥ in its signature, and that is the point: it handles the failure, so it cannot itself fail. The channel stops here.

catch attaches to more than do#

This is the part that surprises people arriving from other languages. catch is not the tail of a try statement — it attaches to structured statements generally. A loop can carry its own handler:

reader locale
faber convert --to en — English reader surface
main {
    const list<string> raw ← ["1", "two", "3"]
    for from raw const item {
        const int n ← item ↦ int
        print n
    } catch err {
        warn err
    }
}
faber convert --to la — canonical Faber
incipit {
    fixum lista<textus> raw ← ["1", "two", "3"]
    itera ex raw fixum item {
        fixum numerus n ← item ↦ numerus
        nota n
    } cape err {
        mone err
    }
}
faber convert --to th-TH — Thai
เริ่ม {
    คงที่ รายการ<ข้อความ> raw ← ["1", "two", "3"]
    วน ออก raw คงที่ item {
        คงที่ จํานวน n ← item ↦ จํานวน
        บันทึก n
    } จับ err {
        เตือน err
    }
}
faber convert --to zh-Hans — Simplified Chinese
入口 {
    常量 列表<文本> raw ← ["1", "two", "3"]
    遍历 取自 raw 常量 item {
        常量 整数 n ← item ↦ 整数
        显示 n
    } 捕获 err {
        警告 err
    }
}
faber convert --to zh-Hant — Traditional Chinese
入口 {
    定值 列表<文字> raw ← ["1", "two", "3"]
    遍歷 取自 raw 定值 item {
        定值 整數 n ← item ↦ 整數
        註記 n
    } 捕捉 err {
        警告 err
    }
}
faber convert --to vi — Vietnamese
bắt_đầu {
    hằng danh_sách<văn_bản> raw ← ["1", "two", "3"]
    lặp từ raw hằng item {
        hằng số n ← item ↦ số
        ghi_chú n
    } bắt err {
        cảnh_báo err
    }
}
faber convert --to ar — Arabic
بداية {
    ثابت قائمة<نص> raw ← ["1", "two", "3"]
    كرر من raw ثابت item {
        ثابت عدد n ← item ↦ عدد
        اعرض n
    } التقط err {
        نبه err
    }
}
faber convert --to hi — Hindi
आरंभ {
    स्थिर सूची<पाठ> raw ← ["1", "two", "3"]
    दोहराओ सेवन raw स्थिर item {
        स्थिर संख्या n ← item ↦ संख्या
        दिखाओ n
    } पकड़ो err {
        चेताओ err
    }
}

And so can a while loop:

reader locale
faber convert --to en — English reader surface
main {
    var int i ← 0
    while i ≺ 3 {
        i ← i + 1
        if i ≡ 2 {
            throw "halfway"
        }
    } catch err {
        warn err
    }
}
faber convert --to la — canonical Faber
incipit {
    varia numerus i ← 0
    dum i ≺ 3 {
        i ← i + 1
        si i ≡ 2 {
            iace "halfway"
        }
    } cape err {
        mone err
    }
}
faber convert --to th-TH — Thai
เริ่ม {
    แปร จํานวน i ← 0
    ขณะ i ≺ 3 {
        i ← i + 1
        ถ้า i ≡ 2 {
            โยน "halfway"
        }
    } จับ err {
        เตือน err
    }
}
faber convert --to zh-Hans — Simplified Chinese
入口 {
    变量 整数 i ← 0
    当 i ≺ 3 {
        i ← i + 1
        如果 i ≡ 2 {
            抛错 "halfway"
        }
    } 捕获 err {
        警告 err
    }
}
faber convert --to zh-Hant — Traditional Chinese
入口 {
    變值 整數 i ← 0
    當 i ≺ 3 {
        i ← i + 1
        若 i ≡ 2 {
            拋出 "halfway"
        }
    } 捕捉 err {
        警告 err
    }
}
faber convert --to vi — Vietnamese
bắt_đầu {
    biến số i ← 0
    trong_khi i ≺ 3 {
        i ← i + 1
        nếu i ≡ 2 {
            ném "halfway"
        }
    } bắt err {
        cảnh_báo err
    }
}
faber convert --to ar — Arabic
بداية {
    متغير عدد i ← 0
    طالما i ≺ 3 {
        i ← i + 1
        إذا i ≡ 2 {
            ارم "halfway"
        }
    } التقط err {
        نبه err
    }
}
faber convert --to hi — Hindi
आरंभ {
    चर संख्या i ← 0
    जबतक i ≺ 3 {
        i ← i + 1
        यदि i ≡ 2 {
            इधरफेंको "halfway"
        }
    } पकड़ो err {
        चेताओ err
    }
}

The handler belongs to the construct it follows. There is no separate try keyword to wrap things in — do is simply the bare block form for when you want a handler and have no other statement to hang it on.

Failing without a message#

A function that declares ⇥ and does not catch propagates automatically — callers must deal with it or declare their own channel.

reader locale
faber convert --to en — English reader surface
fn parse(string raw) → int ⇥ string {
    if raw ≡ "" {
        throw "empty input"
    }
    return raw ↦ int ⊥ 0
}

fn duplum(string raw) → int ⇥ string {
    return parse(raw) * 2
}

main {
    do {
        print duplum("21")
    }
    catch err {
        warn err
    }
}
faber convert --to la — canonical Faber
functio parse(textus raw) → numerus ⇥ textus {
    si raw ≡ "" {
        iace "empty input"
    }
    redde raw ↦ numerus ⊥ 0
}

functio duplum(textus raw) → numerus ⇥ textus {
    redde parse(raw) * 2
}

incipit {
    fac {
        nota duplum("21")
    }
    cape err {
        mone err
    }
}
faber convert --to th-TH — Thai
ฟังก์ชัน parse(ข้อความ raw) → จํานวน ⇥ ข้อความ {
    ถ้า raw ≡ "" {
        โยน "empty input"
    }
    คืน raw ↦ จํานวน ⊥ 0
}

ฟังก์ชัน duplum(ข้อความ raw) → จํานวน ⇥ ข้อความ {
    คืน parse(raw) * 2
}

เริ่ม {
    ทำ {
        บันทึก duplum("21")
    }
    จับ err {
        เตือน err
    }
}
faber convert --to zh-Hans — Simplified Chinese
函数 parse(文本 raw) → 整数 ⇥ 文本 {
    如果 raw ≡ "" {
        抛错 "empty input"
    }
    返回 raw ↦ 整数 ⊥ 0
}

函数 duplum(文本 raw) → 整数 ⇥ 文本 {
    返回 parse(raw) * 2
}

入口 {
    执行 {
        显示 duplum("21")
    }
    捕获 err {
        警告 err
    }
}
faber convert --to zh-Hant — Traditional Chinese
函式 parse(文字 raw) → 整數 ⇥ 文字 {
    若 raw ≡ "" {
        拋出 "empty input"
    }
    傳回 raw ↦ 整數 ⊥ 0
}

函式 duplum(文字 raw) → 整數 ⇥ 文字 {
    傳回 parse(raw) * 2
}

入口 {
    執行 {
        註記 duplum("21")
    }
    捕捉 err {
        警告 err
    }
}
faber convert --to vi — Vietnamese
hàm parse(văn_bản raw) → số ⇥ văn_bản {
    nếu raw ≡ "" {
        ném "empty input"
    }
    trả raw ↦ số ⊥ 0
}

hàm duplum(văn_bản raw) → số ⇥ văn_bản {
    trả parse(raw) * 2
}

bắt_đầu {
    làm {
        ghi_chú duplum("21")
    }
    bắt err {
        cảnh_báo err
    }
}
faber convert --to ar — Arabic
دالة parse(نص raw) → عدد ⇥ نص {
    إذا raw ≡ "" {
        ارم "empty input"
    }
    أعد raw ↦ عدد ⊥ 0
}

دالة duplum(نص raw) → عدد ⇥ نص {
    أعد parse(raw) * 2
}

بداية {
    افعل {
        اعرض duplum("21")
    }
    التقط err {
        نبه err
    }
}
faber convert --to hi — Hindi
फलन parse(पाठ raw) → संख्या ⇥ पाठ {
    यदि raw ≡ "" {
        इधरफेंको "empty input"
    }
    लौटाओ raw ↦ संख्या ⊥ 0
}

फलन duplum(पाठ raw) → संख्या ⇥ पाठ {
    लौटाओ parse(raw) * 2
}

आरंभ {
    करो {
        दिखाओ duplum("21")
    }
    पकड़ो err {
        चेताओ err
    }
}

duplum never mentions failure in its body. It declares the channel and lets parse's failure travel through.

Errors as a parameter#

A caught error is an ordinary value, so a function can receive one like any other parameter. Declare the parameter with the error's type and pass the catch binding straight in.

reader locale
faber convert --to en — English reader surface
fn divide(int a, int b) → int ⇥ string {
    if b ≡ 0 {
        throw "division by zero"
    }
    return a / b
}

fn logga(string context, string err) → void {
    warn context
    warn err
}

main {
    do {
        print divide(1, 0)
    }
    catch err {
        logga("divide", err)
    }
}
faber convert --to la — canonical Faber
functio divide(numerus a, numerus b) → numerus ⇥ textus {
    si b ≡ 0 {
        iace "division by zero"
    }
    redde a / b
}

functio logga(textus context, textus err) → vacuum {
    mone context
    mone err
}

incipit {
    fac {
        nota divide(1, 0)
    }
    cape err {
        logga("divide", err)
    }
}
faber convert --to th-TH — Thai
ฟังก์ชัน divide(จํานวน a, จํานวน b) → จํานวน ⇥ ข้อความ {
    ถ้า b ≡ 0 {
        โยน "division by zero"
    }
    คืน a / b
}

ฟังก์ชัน logga(ข้อความ context, ข้อความ err) → เปล่า {
    เตือน context
    เตือน err
}

เริ่ม {
    ทำ {
        บันทึก divide(1, 0)
    }
    จับ err {
        logga("divide", err)
    }
}
faber convert --to zh-Hans — Simplified Chinese
函数 divide(整数 a, 整数 b) → 整数 ⇥ 文本 {
    如果 b ≡ 0 {
        抛错 "division by zero"
    }
    返回 a / b
}

函数 logga(文本 context, 文本 err) → 无值 {
    警告 context
    警告 err
}

入口 {
    执行 {
        显示 divide(1, 0)
    }
    捕获 err {
        logga("divide", err)
    }
}
faber convert --to zh-Hant — Traditional Chinese
函式 divide(整數 a, 整數 b) → 整數 ⇥ 文字 {
    若 b ≡ 0 {
        拋出 "division by zero"
    }
    傳回 a / b
}

函式 logga(文字 context, 文字 err) → 空值 {
    警告 context
    警告 err
}

入口 {
    執行 {
        註記 divide(1, 0)
    }
    捕捉 err {
        logga("divide", err)
    }
}
faber convert --to vi — Vietnamese
hàm divide(số a, số b) → số ⇥ văn_bản {
    nếu b ≡ 0 {
        ném "division by zero"
    }
    trả a / b
}

hàm logga(văn_bản context, văn_bản err) → trống {
    cảnh_báo context
    cảnh_báo err
}

bắt_đầu {
    làm {
        ghi_chú divide(1, 0)
    }
    bắt err {
        logga("divide", err)
    }
}
faber convert --to ar — Arabic
دالة divide(عدد a, عدد b) → عدد ⇥ نص {
    إذا b ≡ 0 {
        ارم "division by zero"
    }
    أعد a / b
}

دالة logga(نص context, نص err) → فراغ {
    نبه context
    نبه err
}

بداية {
    افعل {
        اعرض divide(1, 0)
    }
    التقط err {
        logga("divide", err)
    }
}
faber convert --to hi — Hindi
फलन divide(संख्या a, संख्या b) → संख्या ⇥ पाठ {
    यदि b ≡ 0 {
        इधरफेंको "division by zero"
    }
    लौटाओ a / b
}

फलन logga(पाठ context, पाठ err) → रिक्त {
    चेताओ context
    चेताओ err
}

आरंभ {
    करो {
        दिखाओ divide(1, 0)
    }
    पकड़ो err {
        logga("divide", err)
    }
}

There is no special parameter keyword for this. The legacy errors function modifier is not how a function receives an error; use an ordinary parameter.

Related: Testing · Conversions for ⊥, the conversion default · Errors and testing for the full treatment.