การจัดการข้อผิดพลาด

ผลลัพธ์ของการประเมินนิพจน์ M จะทําให้เกิดหนึ่งในผลลัพธ์ต่อไปนี้:

  • มีการผลิตค่าเดียว

  • มีข้อผิดพลาด เกิดขึ้น ที่ระบุถึงกระบวนการประเมินนิพจน์ จะไม่สามารถผลิตค่าได้ ข้อผิดพลาดประกอบด้วยค่าเรกคอร์ดเดียวที่สามารถใช้เพื่อระบุข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่เป็นสาเหตุให้เกิดการประเมินที่ไม่สมบูรณ์

ข้อผิดพลาดสามารถเกิดขึ้นได้จากภายในนิพจน์ และสามารถจัดการได้จากภายในนิพจน์

การเกิดข้อผิดพลาด

ไวยากรณ์สําหรับการสร้างข้อผิดพลาดมีดังนี้:

error-raising-expression:
      errorนิพจน์

ค่าข้อความสามารถใช้เป็นแบบย่อสําหรับค่าข้อผิดพลาดได้ ตัวอย่างเช่น

error "Hello, world" // error with message "Hello, world"

ค่าข้อผิดพลาดแบบเต็มคือเรกคอร์ด และสามารถสร้างโดยใช้ฟังก์ชัน ได้ Error.Record :

error Error.Record("FileNotFound", "File my.txt not found",
     "my.txt")

นิพจน์ข้างต้นเทียบเท่ากับ:

error [ 
    Reason = "FileNotFound", 
    Message = "File my.txt not found", 
    Detail = "my.txt" 
]

การเกิดข้อผิดพลาดจะทําให้การประเมินนิพจน์ปัจจุบันหยุดและสแตกการประเมินนิพจน์จะคลายออกจนกว่าหนึ่งในรายการต่อไปนี้จะเกิดขึ้น:

  • เขตข้อมูลเรกคอร์ด สมาชิกส่วน หรือตัวแปรให้ — รวมกัน: เข้าถึงรายการ รายการถูกทําเครื่องหมายว่ามีข้อผิดพลาด ค่าข้อผิดพลาดจะถูกบันทึกพร้อมกับรายการดังกล่าว จากนั้น จะถูกเผยแพร่ การเข้าถึงรายการดังกล่าวในภายหลังจะทําให้เกิดข้อผิดพลาดแบบเดียวกัน รายการอื่นๆ ของเรกคอร์ด ส่วน หรือนิพจน์ให้ ดังกล่าวจะไม่ได้รับผลกระทบเสมอไป (ยกเว้นกรณีที่รายการดังกล่าวเข้าถึงรายการที่ถูกทําเครื่องหมายว่ามีข้อผิดพลาดอยู่ก่อนหน้า)

  • เข้าถึงนิพจน์ระดับบนสุดแล้ว ในกรณีนี้ ผลลัพธ์ของการประเมินนิพจน์ระดับสูงสุดจะเป็นข้อผิดพลาดแทนที่จะเป็นค่า

  • tryเข้าถึงนิพจน์ ในกรณีนี้ ข้อผิดพลาดจะถูกบันทึกไว้และแสดงเป็นค่า

การจัดการข้อผิดพลาด

error-handling-expression (รู้จักกันอย่างไม่เป็นทางการว่า "try expression") ถูกใช้เพื่อจัดการข้อผิดพลาด:

error-handling-expression:
      tryprotected-expression error-handleropt
protected-expression:
      นิพจน์
ตัวจัดการข้อผิดพลาด:
      otherwise-clause
      catch-clause
otherwise-clause:

      otherwisedefault-expression
default-expression:
      นิพจน์
catch-clause:
      catchcatch-function
catch-function:
      (parameter-nameopt)=>function-body

รายการต่อไปนี้จะถูกระงับไว้ ขณะที่ประเมิน error-handling-expression โดยไม่มี ตัวจัดการข้อผิดพลาด:

  • หากการประเมินของ protected-expression ไม่ทําให้เกิดข้อผิดพลาดและทําให้เกิดค่า x ค่าที่เกิดจาก error-handling-expression เป็นเรกคอร์ดของฟอร์มต่อไปนี้:
    [ HasErrors = false, Value = x ]
  • หากการประเมินของ protected-expression ทําให้เกิดค่าข้อผิดพลาด e ผลลัพธ์ของ error-handling-expression จะเป็นเรกคอร์ดของฟอร์มต่อไปนี้:
    [ HasErrors = true, Error = e ]

รายการต่อไปนี้จะถูกระงับไว้ ขณะที่ประเมิน error-handling-expression ที่มี ตัวจัดการข้อผิดพลาด:

  • จําเป็นต้องประเมิน protected-expression ก่อนตัวจัดการข้อผิดพลาด

  • ต้องประเมินตัวจัดการข้อผิดพลาด ก็ต่อเมื่อการประเมินของ protected-expression ทําให้เกิดข้อผิดพลาด

  • หากการประเมินของ protected-expression ทําให้เกิดข้อผิดพลาดค่าที่เกิดจาก error-handling-expression จะเป็นผลลัพธ์ของการประเมิน error-handler

  • มีข้อผิดพลาดเกิดขึ้นในระหว่างการประเมินตัวจัดการ ข้อผิดพลาด จะถูกเผยแพร่

  • เมื่อตัวจัดการข้อผิดพลาดที่กําลังประเมินเป็นส่วนคําสั่ง catch-function จะถูกเรียกใช้ ถ้าฟังก์ชันนั้นยอมรับพารามิเตอร์ ค่าข้อผิดพลาดจะถูกส่งผ่านเป็นค่าของฟังก์ชันดังกล่าว

ตัวอย่างต่อไปนี้แสดงให้เห็นถึง error-handling-expression ในกรณีที่ไม่เกิดข้อผิดพลาด:

let
    x = try "A"
in
    if x[HasError] then x[Error] else x[Value] 
// "A"

ตัวอย่างต่อไปนี้แสดงถึงการเกิดข้อผิดพลาด และจัดการข้อผิดพลาดดังกล่าว:

let
    x = try error "A" 
in
    if x[HasError] then x[Error] else x[Value] 
// [ Reason = "Expression.Error", Message = "A", Detail = null ]

ตัวอย่างก่อนหน้านี้สามารถเขียนใหม่ด้วยไวยากรณ์น้อยลงโดยใช้ส่วน คําสั่ง catch-กับ ฟังก์ชัน catch-function ที่ยอมรับพารามิเตอร์:

let
    x = try error "A" catch (e) => e
in
    x
// [ Reason = "Expression.Error", Message = "A", Detail = null ]

otherwise-clause สามารถใช้เพื่อแทนที่ข้อผิดพลาดที่จัดการโดยนิพจน์ ลอง ด้วยค่าอื่น:

try error "A" otherwise 1 
// 1

ส่วนคําสั่ง catch-with a zero-parameter catch-function เป็นไวยากรณ์ทางเลือกที่ยาวกว่าสําหรับ otherwise-clause:

try error "A" catch () => 1 
// 1

ถ้าตัวจัดการข้อผิดพลาดทําให้เกิดข้อผิดพลาดด้วย นิพจน์ ลอง ทั้งหมดก็จะทําให้เกิดข้อผิดพลาดเช่นกัน:

try error "A" otherwise error "B" 
// error with message "B"
try error "A" catch () => error "B" 
// error with message "B"
try error "A" catch (e) => error "B" 
// error with message "B"

ข้อผิดพลาดในเรกคอร์ดและตัวกําหนดค่าเริ่มต้นให้

ตัวอย่างต่อไปนี้แสดงตัวกําหนดค่าเริ่มต้นของเรกคอร์ดด้วยเขตข้อมูล A ที่เกิดข้อผิดพลาดและเข้าถึงได้โดยเขตข้อมูลอื่นๆ สองเขต B และC เขตข้อมูล B จะไม่จัดการข้อผิดพลาดที่เกิดขึ้นจาก Aแต่ C จะจัดการ เขตข้อมูลDสุดท้ายไม่สามารถเข้าถึง A ดังนั้นจึงไม่ได้รับผลกระทบจากข้อผิดพลาดในA

[ 
    A = error "A", 
    B = A + 1,
    C = let x =
            try A in
                if not x[HasError] then x[Value]
                else x[Error], 
    D = 1 + 1 
]

ผลลัพธ์ของการประเมินนิพจน์ข้างต้นคือ:

[ 
    A = // error with message "A" 
    B = // error with message "A" 
    C = "A", 
    D = 2 
]

ควรดําเนินการจัดการข้อผิดพลาดใน M ใกล้กับสาเหตุของข้อผิดพลาดเพื่อจัดการกับผลกระทบของการเริ่มต้นเขตข้อมูลขี้เกียจและการประเมินการปิดการทํางานแบบเลื่อนออกไป ตัวอย่างต่อไปนี้แสดงความพยายามที่ไม่สําเร็จขณะจัดการข้อผิดพลาดโดยใช้ try นิพจน์:

let
    f = (x) => [ a = error "bad", b = x ],
    g = try f(42) otherwise 123
in 
    g[a]  // error "bad"

ในตัวอย่างนี้ ข้อกําหนดgมีขึ้นเพื่อจัดการข้อผิดพลาดที่เกิดขึ้นเมื่อเรียกf อย่างไรก็ตาม ข้อผิดพลาดที่เกิดจากตัวกําหนดค่าเริ่มต้นของ try เขตข้อมูลที่จะทํางานเมื่อจําเป็นเท่านั้น และหลังจากแสดงเรกคอร์ดจาก f และส่งผ่านนิพจน์

ข้อผิดพลาดที่ไม่ได้นํามาใช้

ในขณะที่มีการพัฒนานิพจน์ผู้เขียนอาจต้องการออกจากการนํานิพจน์บางส่วนไปใช้ แต่อาจยังคงต้องการสามารถดําเนินการนิพจน์ได้ วิธีหนึ่งในการจัดการกรณีนี้คือ การทําให้เกิดข้อผิดพลาดสําหรับส่วนที่ไม่ได้ใช้งาน ตัวอย่างเช่น

(x, y) =>
     if x > y then
         x - y
     else
         error Error.Record("Expression.Error", 
            "Not Implemented")

สัญลักษณ์จุดไข่ปลา (...) สามารถใช้เป็นทางลัดสําหรับerror

not-implemented-expression:
      ...

ตัวอย่างเช่น รายการต่อไปนี้จะเทียบเท่ากับตัวอย่างก่อนหน้านี้:

(x, y) => if x > y then x - y else ...