Я имею в виду определение экземпляра класса типов, который применяется в локальном (let
или где
) область видимости в функции. Что еще более важно, я хочу, чтобы функции в этом экземпляре были замыканиями, т.е. имели возможность закрывать переменные в лексической области видимости, где определен экземпляр (что означает, что экземпляр может работать по-другому в следующий раз, когда функция, в которой он находится, вызывается). ).
Я могу предложить вам упрощенный вариант использования. Предположим, у меня есть функция, которая работает с типом на основе класса типов. В этом примере я использую возведение в квадрат, которое работает с любым типом экземпляров Num
(да, возведение в квадрат очень просто и может быть легко реализовано повторно, но оно заменяет нечто, что может быть более сложным). Мне нужно иметь возможность использовать существующую функцию как есть (без ее изменения или повторной реализации).
square :: Num a => a -> a
square x = x * x
Теперь предположим, что я хочу использовать эту операцию в модульной арифметике, то есть сложение, умножение и т. д. по модулю некоторого числа. Это было бы легко реализовать для любой фиксированной базы по модулю, но я хочу иметь что-то общее, что я могу повторно использовать для разных баз по модулю.Я хочу иметь возможность определить что-то вроде этого:
newtype ModN = ModN Integer deriving (Eq, Show)
-- computes (x * x) mod n
squareModN ::
squareModN x n =
let instance Num ModN where
ModN x * ModN y = ModN ((x * y) `mod` n) -- modular multiplication
_ + _ = undefined -- the rest are unimplemented for simplicity
negate _ = undefined
abs _ = undefined
signum _ = undefined
fromInteger _ = undefined
in let ModN y = square (ModN x)
in y
Суть в том, что мне нужно использовать функцию выше (square
), которая требует, чтобы ее аргумент был типом, который является экземпляром определенный класс типов. Я определяю новый тип и делаю его экземпляром Num
; однако для правильного выполнения арифметических операций по модулю это зависит от базы по модулю n
, которая из-за общего дизайна этой функции может меняться от вызова к вызову. Я хочу определить функции экземпляра как своего рода одноразовые «обратные вызовы» (если хотите) для функции square
для настройки того, как она выполняет операции в этот раз (и только в этот раз).
Одним из решений может быть интеграция «переменных закрытия» непосредственно в сам тип данных (т.е. ModN (x, n)
для представления числа и базы, к которой оно принадлежит), и операции могут просто извлечь эту информацию из аргументов. Однако у этого есть несколько проблем: 1) Для функций с несколькими аргументами (например, (*)
) нужно будет проверять во время выполнения, что эта информация совпадает, что некрасиво; и 2) экземпляр может содержать «значения» с 0 аргументами, которые я мог бы захотеть зависеть от переменных замыкания, но которые, поскольку они не содержат аргументов, не могут извлечь их из аргументов.