Прежде всего, я новичок в Haskell, поэтому будьте добры:)
Рассмотрим следующий пример:
{-# LANGUAGE RecordWildCards #-}
data Item = Item {itemPrice :: Float, itemQuantity :: Float} deriving (Show, Eq)
data Order = Order {orderItems :: [Item]} deriving (Show, Eq)
itemTotal :: Item -> Float
itemTotal Item{..} = itemPrice * itemQuantity
orderTotal :: Order -> Float
orderTotal = sum . map itemTotal . orderItems
Возможно ли запомнить функцию orderTotal
таким образом, он выполняется только один раз для «экземпляра» записи Order
, и, это сложная часть, запись в кеше, привязанная к этому экземпляру, удаляется, как только этот порядок собирается мусором? Другими словами, я не хочу иметь кеш, который постоянно растет.
Редактировать после комментариев:
Действительно, в этом простом примере накладные расходы на запоминание вероятно, не окупается. Но вы можете представить себе сценарий, в котором у нас есть сложный график значений (например, заказ, элементы заказа, продукты, клиент ...) и множество производных свойств, которые работают с этими значениями (как, например, orderTotal выше). Если мы создаем поле для суммы заказа, а не используем функцию для его вычисления, мы должны быть очень осторожны, чтобы не привести к непоследовательности заказа.
Было бы неплохо, если бы мы могли express эти взаимозависимости данных декларативно (используя функции вместо полей) и делегировать работу по оптимизации этих вычислений компилятору? Я считаю, что на чистом языке, таком как Haskell, это было бы возможно, хотя мне не хватает знаний, чтобы это сделать.
Чтобы проиллюстрировать то, что я пытаюсь сказать, посмотрите на этот код (в Python):
def memoized(function):
function_name = function.__name__
def wrapped(self):
try:
result = self._cache[function_name]
except KeyError:
result = self._cache[function_name] = function(self)
return result
return property(wrapped)
class Item:
def __init__(self, price, quantity):
self._price = price
self._quantity = quantity
self._cache = {}
@property
def price(self):
return self._price
@property
def quantity(self):
return self._quantity
@memoized
def total(self):
return self.price * self.quantity
Класс Item
является неизменным (своего рода), поэтому мы знать, что каждое производное свойство может быть вычислено только один раз за экземпляр. Это именно то, что делает функция memoized
. Кроме того, кэш находится внутри самого экземпляра (self._cache
), поэтому он будет собирать мусор вместе с ним.
Что я ищу, так это добиться аналогичного результата в Haskell.