То, что я имею в виду, это определение экземпляра класса типов, который применяется в локальной (let
или where
) области действия в функции. Что еще более важно, я хочу, чтобы функции в этом экземпляре были замыканиями, т. Е. Иметь возможность закрывать переменные в лексической области, в которой определен экземпляр (это означает, что экземпляр может работать иначе, когда в следующий раз вызывается функция, в которой он находится ).
Я могу дать вам упрощенный вариант использования для этого. Предположим, у меня есть функция, которая работает с типом, основанным на классе типов. В этом примере я использую возведение в квадрат, которое работает с любым типом, например Num
(да, возведение в квадрат очень просто и может быть легко реализовано повторно, но оно заменяет нечто более сложное). Мне нужно иметь возможность использовать существующую функцию как есть (без изменения или повторной реализации).
square :: Num a => a -> a
square x = x * x
Теперь предположим, что я хочу использовать эту операцию в модульной арифметике, то есть сложение, умножение и т. Д. Для некоторого числа. Это было бы легко реализовать для любой фиксированной базы по модулю, но я хочу иметь что-то общее, что я могу повторно использовать для разных баз по модулю. Я хочу иметь возможность определить что-то вроде этого:
* * 1010
Суть в том, что мне нужно использовать функцию сверху (square
), которая требует, чтобы ее аргумент был типом, который является экземпляром определенного класса типов. Я определяю новый тип и делаю его экземпляром Num
; однако для правильного выполнения арифметики по модулю это зависит от базы по модулю n
, которая из-за общего дизайна этой функции может меняться от вызова к вызову. Я хочу определить функции экземпляра как своего рода одноразовые «обратные вызовы» (если хотите) для функции square
, чтобы настроить, как она выполняет операции в этот раз (и только в этот раз).
Одним из решений может быть интеграция «переменных замыкания» непосредственно в сам тип данных (т. Е. ModN (x, n)
для представления числа и базы, к которой он принадлежит), и операции могут просто извлечь эту информацию из аргументов. Однако у этого есть несколько проблем: 1) Для функций с несколькими аргументами (например, (*)
) во время выполнения необходимо проверить соответствие этой информации, что некрасиво; и 2) экземпляр может содержать «значения» с 0 аргументами, от которых я мог бы зависеть от переменных замыкания, но которые, поскольку они не содержат аргументов, не могут извлечь их из аргументов.