Як печуть хліб програмісти на haskell, записки програміста

Отже, сидить собі такий програміст на Haskell. Тут до нього підходить менеджер і заявляє, мовляв, нам потрібно навчитися пекти хліб. Загалом, нічого толком не ясно, але раз треба, так треба:







data Bread = Bread

Пізніше стає відомо, що хліб, виявляється, має дбати в грубці:

data Oven = Oven
data Bread = Bread

createBread. Oven -> Bread
createBread _ = Bread

Як бачите, поки це мало на що впливає. Як і те, що печі бувають різних видів:

data Oven = ElectricOven | GasOven | MicrowaveOven
data Bread = Bread

createBread. Oven -> Bread
createBread _ = Bread

Перше дійсно більш-менш цікаве умова полягає в тому, що газова піч не може піч без газу:

data GasStatus = GasAvailable | GasUnavailable
data Oven = ElectricOven | GasOven | MicrowaveOven
data Bread = Bread

breadCouldBeCreated GasOven GasUnavailable = False
breadCouldBeCreated _ _ = True

createBread oven gas
| breadCouldBeCreated oven gas = Just Bread
| otherwise = Nothing

Тут вводиться тип «стан газу». Газ - він або є, або його немає. Якщо ми використовуємо газові балони, можна зберігати кількість доступного газу в літрах, суть від цього не змінюється. Функція breadCouldBeCreated перевіряє, чи можемо ми що-небудь приготувати при поточних обставинах (наявність газу і тип печі).

Пізніше стає відомо, що крім хліба в печі також можна готувати торти і пиріжки з різною начинкою:

data Stuffing = Meat | Cabbage
data Food = Cake | Bread | Pasty Stuffing
data GasStatus = GasAvailable | GasUnavailable
data Oven = ElectricOven | GasOven | MicrowaveOven

ovenCouldBeUsed GasOven GasUnavailable = False






ovenCouldBeUsed _ _ = True

create food oven gas
| ovenCouldBeUsed oven gas = Just food
| otherwise = Nothing

Вводимо типи «їжа» і «начинка». Функцію breadCouldBeCreated перейменовуємо в ovenCouldBeUsed.

Тепер менеджер хоче, щоб торти, пиріжки та хліб пеклися не просто так, а за різними рецептами. Сказано зроблено:

data Stuffing = Meat | Cabbage
data Food = Cake | Bread | Pasty Stuffing
data GasStatus = GasAvailable | GasUnavailable
data Oven = ElectricOven | GasOven | MicrowaveOven

ovenCouldBeUsed GasOven GasUnavailable = False
ovenCouldBeUsed _ _ = True

create food oven gas
| ovenCouldBeUsed oven gas = Just food
| otherwise = Nothing

breadRecipe = create Bread
cakeRecipe = create Cake
pastyRecipe stuffing = create $ Pasty stuffing

Рецепт зазвичай наказує виконання якихось дій з інгредієнтами, піччю і так далі. Очевидно, що рецепт є функцію вищого порядку.

Нарешті, в печах потрібно обпалювати цеглу. Судячи з формулювання, обпалювати цеглу слід в тих же самих печах, включаючи мікрохвильову (хоча в статті на Хабре вводиться окремий клас Furnace):

data Stuffing = Meat | Cabbage
data Food = Cake | Bread | Pasty Stuffing
data GasStatus = GasAvailable | GasUnavailable
data Oven = ElectricOven | GasOven | MicrowaveOven
data Brick = Brick

ovenCouldBeUsed GasOven GasUnavailable = False
ovenCouldBeUsed _ _ = True

create food oven gas
| ovenCouldBeUsed oven gas = Just food
| otherwise = Nothing

breadRecipe = create Bread
cakeRecipe = create Cake
pastyRecipe stuffing = create $ Pasty stuffing

makeBrick oven gas
| ovenCouldBeUsed oven gas = Just Brick
| otherwise = Nothing

Тут ми просто додали тип «цеглина» і функцію «зробити цегла».

Висновки напрошуються самі собою. Ми отримуємо простий для розуміння і легкий у супроводі код. У процесі його написання ми легко і невимушено будуємо модель предметної області, фактично просто роблячи переклад з російської на Haskell. Без усяких там наслідувань, рефакторингов і UML.

Можливо, в дійсності малося на увазі, що кожна піч виробляє хліба і торти трохи інакше, і нам знадобиться ввести додатковий клас типів (або, якщо хочете, «інтерфейс») з відповідними екземплярами класів. Заодно нам не доведеться вручну кодувати виклик ovenCouldBeUsed всюди, де використовується піч. Але не схоже, що рішення істотно ускладниться від усього цього.

А як ви печете хліб?

Сподобався пост? Поділися з іншими:

(Необхідно включити JS)







Схожі статті