Возможно ли иметь «локальный» экземпляр класса типов?

Я имею в виду определение экземпляра класса типов, который применяется в локальном (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 аргументами, которые я мог бы захотеть зависеть от переменных замыкания, но которые, поскольку они не содержат аргументов, не могут извлечь их из аргументов.

18
задан newacct 3 March 2012 в 11:44
поделиться