Да. Это называется "стиль передачи словаря". Иногда, когда я делаю какие-то особенно сложные вещи, мне нужно отказаться от класса типов и превратить его в словарь, потому что передача словаря более мощная 1 , но часто довольно громоздкая, что делает концептуально простой код выглядящим довольно сложным. Я иногда использую стиль передачи словаря в языках, которые не Haskell для имитации классов типов (но я узнал, что это, как правило, не такая хорошая идея, как кажется).
Конечно, когда есть разница в выразительной власти есть компромисс. Несмотря на то, что вы можете использовать данный API-интерфейс несколькими способами, если он написан с использованием DPS, он получает больше информации, если вы не можете. На практике это проявляется в Data.Set
, который основан на том факте, что для каждого типа существует только один Ord
словарь. Set
хранит свои элементы, отсортированные в соответствии с Ord
, и если вы строите набор с одним словарем, а затем вставляете элемент с использованием другого, как это было бы возможно с DPS, вы могли бы сломать инвариант Set
и заставить его взломать sh. Эту проблему уникальности можно решить, используя фантомный экзистенциальный тип для обозначения словаря, но, опять-таки, ценой довольно неприятной сложности в API. Это также проявляется примерно так же в API Typeable
.
Бит уникальности появляется не очень часто. Классы типов хороши в написании кода для вас. Например,
catProcs :: (i -> Maybe String) -> (i -> Maybe String) -> (i -> Maybe String)
catProcs f g = f <> g
, который принимает два «процессора», которые принимают входные данные и могут выдавать выходные данные, и объединяет их, сглаживая Nothing
, должен быть записан в DPS примерно так:
catProcs f g = (<>) (funcSemi (maybeSemi listSemi)) f g
По сути, нам снова пришлось прописать тип, в котором мы его используем, хотя мы уже прописали его в сигнатуре типа, и даже это было избыточно, потому что компилятор уже знает все типы. Поскольку существует только один способ создания данного Semigroup
для типа, компилятор может сделать это за вас. Это имеет эффект типа «сложный интерес», когда вы начинаете определять множество экземпляров параметризации c и использовать для вычисления структуру ваших типов, как в Data.Functor.*
комбинаторах, и это привык к большому эффекту с deriving via
, где вы можете по существу получить всю «стандартную» алгебраическую c структуру вашего типа, написанную для вас.
И даже не заводите меня на MPT C и функциях, которые возвращают информацию обратно в проверку типов и вывод. Я никогда не пытался преобразовать такую вещь в DPS - я подозреваю, что это потребовало бы передачи большого количества доказательств равенства типов - но в любом случае я уверен, что это будет намного больше работы для моего мозга, чем мне было бы удобно с.
-
1 U , если вы не используете reflection
, и в этом случае они становятся эквивалентными по мощности - но reflection
также может быть громоздким в использовании.