همهی خطاها در Elixir «رفتار استثنا» را پیادهسازی میکنند. «رفتار استثنا» درست مانند «رفتار دسترسی»، توابع کالبکی را تعریف میکند که یک ماژول برای ایفای قرارداد نرمافزاری این رفتار باید پیادهسازی کند. وقتی یک خطا تعریف میشود، ویژگیهای زیر را دارد:
:message خواهد داشت.raise/1 و raise/2 برای raise کردن خطای مورد نظر استفاده کرد.«رفتار استثنا» همچنین دو تابع کالبک را مشخص میکند: message/1 و exception/1. اگر پیادهسازی نشوند، از پیادهسازیهای پیشفرض استفاده میشود. message/1 هنگام فراخوانی با raise، ساختار خطا را به یک پیام خوانا تبدیل میکند. exception/1 امکان میدهد هنگام فراخوانی با raise/2، زمینهی بیشتری به پیام اضافه شود.
برای تعریف یک استثنا از یک ماژول خطا، از ماکروی defexception استفاده میکنیم:
# Defines a minimal error, with the name `MyError`
defmodule MyError do
defexception message: "error"
end
# Defines an error with a customized exception/1 function
defmodule MyCustomizedError do
defexception message: "custom error"
@impl true
def exception(value) do
case value do
[] ->
%MyCustomizedError{}
_ ->
%MyCustomizedError{message: "Alert: " <> value}
end
end
end
از خطاهای تعریفشده میتوان مانند یک خطای داخلی و با raise/1 یا raise/2 استفاده کرد.
raise/1 یک خطای مشخص را با اسم ماژولش raise میکند، یا اگر آرگومان یک رشته باشد، یک RuntimeError را با آن رشته به عنوان پیام raise میکند.raise/2 یک خطای مشخص را با اسم ماژولش raise میکند و یک آرگومان ویژگیها میپذیرد که برای به دست آوردن خطا با پیام مناسب به کار میرود.در حالی که کار خود را در Instruments of Texas ادامه میدهید، پیشرفتی در پیادهسازی ماشینحساب RPN با Elixir در حال انجام است. تیم شما میخواهد بتواند خطاهایی اختصاصیتر از خطاهای عمومی کتابخانهی استاندارد ایجاد کند. شما در حال تحقیق هستید، اما تصمیم گرفتهاید دو خطای جدید بنویسید که Exception Behaviour را پیادهسازی میکنند.
تقسیم یک عدد بر صفر نتیجهی تعریفنشدهای تولید میکند که به نظر تیم بهتر است با یک خطا نشان داده شود.
ماژول DivisionByZeroError را طوری پیادهسازی کنید که پیام خطایش "division by zero occurred" باشد.
raise DivisionByZeroError
# => ** (DivisionByZeroError) division by zero occurred
ماشینحسابهای RPN از یک «پشته» استفاده میکنند تا اعداد را پیش از انجام محاسبات روی آنها نگه دارند. تیم این «پشته» را با فهرستی از اعداد (صحیح و اعشاری) نمایش میدهد، مثلاً: [3, 4.0]. هر عملیات برای انجام محاسبهاش به تعداد مشخصی عدد روی پشته نیاز دارد. وقتی اعداد کافی روی پشته نباشد، به این حالت خطای «کمبود پشته» میگویند. استثنای StackUnderflowError را پیادهسازی کنید که یک پیام پیشفرض و زمینهی اضافی اختیاری فراهم میکند.
raise StackUnderflowError
# => ** (StackUnderflowError) stack underflow occurred
raise StackUnderflowError, "when dividing"
# => ** (StackUnderflowError) stack underflow occurred, context: when dividing
تابع divide/1 را پیادهسازی کنید که یک پشته (فهرستی از دو عدد) میگیرد و:
RPNCalculator.Exception.divide([])
# => ** (StackUnderflowError) stack underflow occurred, context: when dividing
RPNCalculator.Exception.divide([0, 100])
# => ** (DivisionByZeroError) division by zero occurred
RPNCalculator.Exception.divide([4, 16])
# => 4
توجه کنید که ترتیب فهرست معکوس است!