การจัดการข้อผิดพลาด
ผลลัพธ์ของการประเมินนิพจน์ 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:
try
protected-expression error-handleropt
protected-expression:
สีหน้า
ตัวจัดการข้อผิดพลาด:
otherwise-clause
catch-clause
otherwise-clause:
otherwise
default-expression
default-expression:
สีหน้า
catch-clause:
catch
catch-function
catch-function:
(
parameter-nameopt)
=>
function-body
รายการต่อไปนี้จะถูกระงับไว้ ขณะที่ประเมิน error-handling-expression โดยไม่มี ตัวจัดการข้อผิดพลาด:
- หากการประเมินของ protected-expression ไม่ทําให้เกิดข้อผิดพลาดและทําให้เกิดค่า x ค่าที่เกิดจาก error-handling-expression เป็นเรกคอร์ดของฟอร์มต่อไปนี้:
[ HasErrors = false, Value = x ]
- หากการประเมินของ protected-expression ทําให้เกิดค่าข้อผิดพลาด ผลลัพธ์ของ 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 ...