نوع مات یک تکنیک کپسولهسازی در Elm است.
برخی این کار را پنهانسازی اطلاعات یا پنهانکردن جزئیات پیادهسازی هم مینامند.
این نوع معمولاً برای اعمال اعتبارسنجی و با استفاده از الگوی تجزیه کنید، اعتبارسنجی نکنید بهکار میرود.
برای مثال، میتوانید یک نوع مات EmailAddress داشته باشید که تنها میتواند نشانی ایمیل معتبر را در خود نگه دارد (برخلاف String که میتواند هر چیزی را شامل شود).
انواع سفارشی یک یا چند گونه دارند و کامپایلر بهطور خودکار تابعی برای ساخت هر گونه تولید میکند که از اسم آن گونه استفاده میکند. این تنها راه ساخت انواع سفارشی است. کامپایلر همچنین شکستن ساختار را برای این گونهها ممکن میکند.
type Maybe a
= Nothing --> Variant implies a 'Nothing' function, with type 'Maybe a', and destructuring of 'Nothing'
| Just a --> Variant implies a 'Just' function, with type 'a -> Maybe a', and destructuring of 'Just _'
بهطور پیشفرض، همهی اینها درونیِ فایل / ماژول هستند و ما با «کلیدواژه»ی exposing انتخاب میکنیم چه چیزی صادر شود.
برای ساخت یک نوع مات، تنها کاری که میکنید این است که انتخاب کنید گونهها صادر نشوند.
وقتی این کار را میکنید، تقریباً همیشه لازم است راه دیگری برای ساخت و گرفتن گونهها فراهم کنید، با صادر کردن توابع سفارشی. این توابع سفارشی به شما امکان میدهند هنگام ساخت و گرفتن گونهها منطقی را اعمال کنید. این کار جزئیات پیادهسازی درونی ماژول را پنهان میکند و به این معناست که تا زمانی که توابع صادرشده یکسان بمانند، میتوانید جزئیات پیادهسازی را هرقدر که بخواهید تغییر دهید.
احتمالاً رایجترین کاربرد نوع مات، اعمال اعتبارسنجی با الگوی «تجزیه کنید، اعتبارسنجی نکنید» است.
در این الگو، تابعی را صادر میکنید که ورودیای با ساختار / ایمنی نوعی کمتر از آنچه میخواهید میگیرد، مانند String، و سپس آن را به نوعی ساختارمندتر، مانند EmailAddress، تجزیه میکنید؛ و یا آن نوع را برمیگردانید یا یک خطا (معمولاً Maybe یا Result) را برمیگردانید.
برای مثال، در کد زیر تنها ماژولهای بیرونی میتوانند با استفاده از تابع parseEmailAddress یک نوع EmailAddress بسازند؛ پس وقتی با نوع EmailAddress کار میکنید، مطمئن هستید که معتبر است.
module EmailAddress exposing ( EmailAddress, parseEmailAddress, getEmailAddress )
-- The `EmailAddress` custom type has one variant, also called `EmailAddress`
-- When a custom type has one variant, it is idiomatic to use the same name for both.
type EmailAddress =
EmailAddress String
-- create an EmailAddress variant
parseEmailAddress: String -> Maybe EmailAddress
parseEmailAddress candidateEmailAddress =
if isValidEmailAddress candidateEmailAddress then
Just (EmailAddress candidateEmailAddress)
else
Nothing
-- get an EmailAddress variant
getEmailAddress: EmailAddress -> String
getEmailAddress ( EmailAddress emailAdress ) =
emailAdress
isValidEmailAddress: String -> bool
-- ...
در این تمرین میخواهید یک SecureTreasureChest عمومی (و جادویی!) بنویسید تا گنجی را در آن نگه دارید.
اگر تیزبین باشید، متوجه شدهاید که TreasureChest قبلی چندان امن نبود!
اگر از تابع getTreasure استفاده میکردید، باید رمز عبور را وارد میکردید، اما همچنان میتوانستید نوع TreasureChest را بهسادگی ساختارشکنی کنید و بدون دانستن رمز عبور به گنج برسید.
بدتر از آن، میتوانستید رمز عبور را هم به همین شکل به دست آورید!
بیایید این مشکل را با استفاده از یک «Opaque Type» حل کنیم.
یک نوع سفارشی Password با یک واریانت تعریف کنید.
این واریانت باید یک مقدار String متناظر برای رمز عبور داشته باشد.
باید نوع Password را آشکار کنید، اما خود واریانت را نه.
این کار Password را به یک Opaque type تبدیل میکند و یعنی دیگر نمیتوانید با ساختارشکنی به رمز عبور دست پیدا کنید.
نمیتوان آزمود که یک نوع Opaque است یا نه؛ در کد تولیدی، از elm-review (یک ابزار تحلیل ایستا) برای اعمال آن استفاده میکنید.
یک تابع createPassword همراه با یک تعریف نوع تعریف و آشکار کنید که یک String میگیرد و یک Maybe Password برمیگرداند.
Owasp توصیه میکند که رمزهای عبور باید حداقل ۸ کاراکتر باشند؛ پس بیایید از الگوی Parse, don't validate برای اعمال این نامتغیر استفاده کنیم.
اگر رمز عبور کمتر از ۸ کاراکتر باشد، createPassword باید Nothing را برگرداند، در غیر این صورت باید Just Password را برگرداند.
باید نوع SecureTreasureChest را آشکار کنید، اما واریانت آن را نه.
این کار SecureTreasureChest را به یک Opaque type تبدیل میکند و یعنی دیگر نمیتوانید با ساختارشکنی به رمز عبور یا گنج دست پیدا کنید.
این تابع باید دو پارامتر بگیرد و یک تعریف نوع داشته باشد.
Password (برای رمز عبور)تابع باید یک SecureTreasureChest برگرداند.
این تابع را آشکار کنید تا ماژولهای دیگر بتوانند نمونههایی از SecureTreasureChest بسازند.
این تابع باید دو پارامتر بگیرد و یک تعریف نوع داشته باشد.
String (برای رمز عبور)SecureTreasureChest
این تابع باید رمز عبور واردشده را با Password موجود در SecureTreasureChest بررسی کند.
تابع باید یک Maybe برگرداند.
اگر رمزها مطابقت داشته باشند، Just را همراه با مقدار عمومی موجود در SecureTreasureChest برگردانید (همان گنج!)
اگر رمزها مطابقت نداشته باشند، Nothing را برگردانید.
این تابع را آشکار کنید تا ماژولهای دیگر بتوانند در صورتی که رمز عبور درست را وارد کنند، گنج را از یک SecureTreasureChest به دست آورند.