Возможно ли иметь «локальный» экземпляр класса типов? - PullRequest
18 голосов
/ 03 марта 2012

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

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

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

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

* * 1010

Суть в том, что мне нужно использовать функцию сверху (square), которая требует, чтобы ее аргумент был типом, который является экземпляром определенного класса типов. Я определяю новый тип и делаю его экземпляром Num; однако для правильного выполнения арифметики по модулю это зависит от базы по модулю n, которая из-за общего дизайна этой функции может меняться от вызова к вызову. Я хочу определить функции экземпляра как своего рода одноразовые «обратные вызовы» (если хотите) для функции square, чтобы настроить, как она выполняет операции в этот раз (и только в этот раз).

Одним из решений может быть интеграция «переменных замыкания» непосредственно в сам тип данных (т. Е. ModN (x, n) для представления числа и базы, к которой он принадлежит), и операции могут просто извлечь эту информацию из аргументов. Однако у этого есть несколько проблем: 1) Для функций с несколькими аргументами (например, (*)) во время выполнения необходимо проверить соответствие этой информации, что некрасиво; и 2) экземпляр может содержать «значения» с 0 аргументами, от которых я мог бы зависеть от переменных замыкания, но которые, поскольку они не содержат аргументов, не могут извлечь их из аргументов.

1 Ответ

12 голосов
/ 03 марта 2012

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

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

...