چرا از پیشوند Maybe_ در توابع اکسیر اجتناب کنید؟

در زبان های کاربردی، مفاهیمی مانند مونادها نقش مهمی در مدیریت عوارض جانبی و زنجیره ایمن عملیات ایفا می کنند. نمونه معروف آن نوع است Maybe
در Haskell، که محاسبات اختیاری را انتزاعی می کند و از رسیدگی صریح مقادیر از دست رفته اجتناب می کند. با این حال، Elixir، یک زبان کاربردی مدرن که بر روی BEAM (ماشین مجازی Erlang) ساخته شده است، مسیر متفاوتی را در طراحی زبان طی می کند.
این انتخاب پیامدهای مستقیمی برای نحوه برخورد ما با سناریوهای مشابه دارد و توضیح می دهد که چرا استفاده از پیشوند maybe_
در توابع عملی توصیه نمی شود. بیایید دلایل فنی و طراحی پشت این تصمیم را بررسی کنیم و اینکه Elixir چگونه جایگزینهای همسو با اهداف شما را ارائه میدهد.
نقش مونادها و نوع Maybe
مونادها، در زبانهایی مانند Haskell، ساختارهای انتزاعی هستند که به شما امکان میدهند عملیاتها را زنجیرهای کنید، در حالی که به طور شفاف با عوارض جانبی یا مقادیر از دست رفته برخورد میکنید. نوع Maybe
به عنوان مثال، مقادیر اختیاری را نشان می دهد و دو مورد را محصور می کند: Just
(یک ارزش فعلی) یا Nothing
(عدم ارزش).
این بسیار قدرتمند است زیرا توسعه دهندگان را از نوشتن شرط های صریح برای هر موردی که ممکن است یک مقدار وجود داشته باشد یا نباشد، باز می دارد. این مثال را در Haskell ببینید:
safeDivide :: Int -> Int -> Maybe Int
safeDivide _ 0 = Nothing
safeDivide x y = Just (x `div` y)
result = Just 10 >>= (`safeDivide` 2) >>= (`safeDivide` 5)
-- Resultado: Just 1
اینجا، اپراتور >>=
به شما امکان می دهد تا تماس هایی را زنجیره ای کنید که به قوانین نوع احترام می گذارند Maybe
. اگر هر مرحله ای برگردد Nothing
، موارد بعدی به طور خودکار نادیده گرفته می شوند.
چرا اکسیر مونادها را قبول نمی کند؟
اکسیر با وجود کاربردی بودن، مونادها را مستقیما پیاده سازی نمی کند و این یک انتخاب آگاهانه است. طراحی زبان به سادگی و عملکرد در BEAM کمک می کند. چند دلیل اصلی برای این تصمیم وجود دارد:
-
معناشناسی ساده و صریح: مونادها، اگرچه ظریف هستند، اما می توانند پیچیدگی مفهومی را برای کسانی که با پارادایم عملکردی آشنا نیستند، معرفی کنند. در Elixir، هدف این است که زبان را برای مخاطبان گسترده، از جمله توسعه دهندگان با پیشینه زبان های ضروری، در دسترس نگه دارد.
-
BEAM در حال حاضر بسیاری از مشکلات را حل می کند: BEAM مکانیسم های قوی برای مقابله با خرابی ها و همزمانی، مانند پیوندها، سرپرستان و فرآیندهای مجزا ارائه می دهد. این نیاز به انتزاع هایی مانند مونادها برای کنترل جریان یا انتشار خطا را کاهش می دهد.
-
جایگزین های اصطلاحی در اکسیر: ساختارهایی مانند تطبیق الگو،
with
و استفاده از تاپل ها ({:ok, value}
یا{:error, reason}
) راههای اصطلاحی و صریح را برای رسیدگی به جریانهای داده یا خرابیهای اختیاری ارائه میکند. این ابزارها در حال حاضر نیازهای رایج را بدون معرفی یک لایه اضافی از انتزاع برآورده می کنند.
مشکل پیشوند maybe_
وقتی از پیشوند استفاده می کنیم maybe_
برای نامگذاری توابع در اکسیر، به طور ضمنی در حال آوردن تداعیهایی از نوع آن هستیم Maybe
از زبان هایی مانند Haskell. این یک انتظار اشتباه ایجاد می کند که این توابع مانند یک موناد رفتار می کنند، یعنی می توانند به طور خودکار زنجیره شوند و به طور ضمنی با مقادیر اختیاری سروکار دارند. با این حال، در اکسیر، هر کنترل جریان نیاز به ساختارهای واضح دارد.
def maybe_divide(_, 0), do: nil
def maybe_divide(x, y), do: x / y
اسمی شبیه maybe_divide
نشان می دهد که ما با انتزاعی مانند Maybe
از Haskell، اما نتیجه تابع فقط صفر در شکست است. برای زنجیره ای کردن این تابع، باید منطق صریح ایجاد کنید:
result =
case maybe_divide(10, 2) do
nil -> nil
value -> maybe_divide(value, 5)
end
این نشان دهنده ظرافت مرتبط با مونادها نیست و با روح طراحی صریح و ساده اکسیر مغایرت دارد.
راه حل اصطلاحی در اکسیر: with
اکسیر مشکل کنترل جریان و انتشار خطا را با ماکرو حل می کند with
. این ساختار به شما امکان می دهد عملیات هایی را که ممکن است شکست بخورند زنجیره بزنید، اما وضوح و سادگی را حفظ می کند.
بازنویسی مثال قبلی با with
:
def safe_divide(_, 0), do: {:error, :division_by_zero}
def safe_divide(x, y), do: {:ok, x / y}
with {:ok, value1} <- safe_divide(10, 2),
{:ok, value2} <- safe_divide(value1, 5) do
{:ok, value2}
end
O with
نیاز به منطق صریح مورد را از بین می برد، کد را ساده می کند و کنترل جریان ظریفی را بدون تکیه بر مفاهیم پیچیده مانند مونادها ارائه می دهد.
نتیجه گیری: نقش طراحی اکسیر در نامگذاری و جابجایی جریان
پیشوند maybe_
در توابع اکسیر به خوبی مورد توجه قرار نمی گیرد زیرا با مفهوم مونادها که بخشی از طراحی زبان نیستند، ارتباط های گمراه کننده ای دارد. این می تواند با بیان این که یک انتزاع ضمنی برای برخورد با مقادیر اختیاری وجود دارد، به سردرگمی و انتظارات نادرست منجر شود، در حالی که در واقع اکسیر بر ساختارهای صریح و سرراست مانند تاپل ها متکی است.{:ok, valor}
یا {:error, motivo}
تطبیق الگو و با ماکرو. این ابزارها برای حل مشکلات مشابه به روشی واضح و اصطلاحی بیش از اندازه کافی هستند.
علاوه بر این، نام هایی مانند try_algo
یا maybe_algo
آنها همچنین با فلسفه زیربنای اکسیر و BEAM که اصل “بگذار خراب شود” را اتخاذ می کند، در تضاد هستند. در اکسیر، نه ما سعی می کنیم انجام دهیم چیزی؛ به سادگی ما انجام می دهیم، و هر گونه خرابی به طور صریح رسیدگی می شود یا به سیستم نظارت قوی BEAM واگذار می شود. این رویکرد شرطهای دفاعی بیش از حد را حذف میکند و کد تمیزی را که با طراحی زبان همسو است، ترویج میکند.
یک مثال ساده را در نظر بگیرید:
# Não idiomático
def try_divide(_, 0), do: {:error, :division_by_zero}
def try_divide(x, y), do: {:ok, x / y}
در این مورد، نام try_divide
واقعیت کد یا روشی که BEAM عملیات را مدیریت می کند را منعکس نمی کند. یک نام واضح تر و اصطلاحی تر به سادگی می تواند این باشد:
def safe_divide(_, 0), do: {:error, :division_by_zero}
def safe_divide(x, y), do: {:ok, x / y}
با این رویکرد، نام دقیقاً همان کاری را که تابع انجام میدهد، بیان میکند: تقسیم امن را انجام میدهد و در صورت لزوم یک خطا را برمیگرداند. این با سادگی و وضوح مورد انتظار در طراحی اکسیر مطابقت دارد.
در نهایت با پرهیز از نامگذاری هایی مانند try_
ه maybe_
، ما اطمینان می دهیم که کد ما منعکس کننده اصول اصلی اکسیر است: سادگی، کارایی، انعطاف پذیری و همسویی مستقیم با مقادیر اصلی BEAM. این زبان به گونهای طراحی شده است که واضح و روشن باشد، و انتخاب نامگذاری و طراحی ما باید این فلسفه را تقویت کند و کدهای اصطلاحی، خوانا و قدرتمندتر را ترویج کند.