Подтвердить что ты не робот

Возможно ли иметь "локальный" экземпляр typeclass?

Я имею в виду определение экземпляра класса типа, который применяется в локальной (let или where) области действия. Что еще более важно, я хочу, чтобы функции в этом экземпляре были закрытыми, т.е. Иметь возможность закрывать переменные в лексической области, где указан экземпляр (что означает, что экземпляр может работать по-другому в следующий раз, когда функция, в которой она находится, называется).

Я могу дать вам упрощенный пример использования. Предположим, что у меня есть функция, которая работает с типом, основанным на классе типа. В этом примере я использую возведение в квадрат, который работает на любом типе, в экземплярах Num (да, возведение в квадрат очень простое и его можно легко переделать, но оно стоит для чего-то более сложного). Мне нужно иметь возможность использовать существующую функцию as-is (без ее изменения или повторной реализации).

square :: Num a => a -> a
square x = x * x

Теперь предположим, что я хочу использовать эту операцию в модульной арифметике, т.е. сложение, умножение и т.д. mod some number. Это было бы легко реализовать для любой фиксированной модульной базы, но я хочу иметь что-то общее, что я могу повторно использовать для разных модулей по модулю. Я хочу иметь возможность определить что-то вроде этого:

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

4b9b3361

Ответ 1

Предлагаемое расширение имеет ту же проблему, что и в предыдущем ответе; вы можете использовать локальные экземпляры для создания двух Map с тем же типом ключа, но с разными экземплярами Ord, в результате чего все инварианты будут рушиться.

Однако reflection пакет позволяет определить такой тип ModN: вы определяете один экземпляр с Reifies и активировать экземпляр для определенного n с помощью reify. (Я полагаю, что неявные параметры также сделают это возможным, но это расширение редко используется.)