Мне кажется, что вы всегда можете передавать аргументы функции, а не использовать класс типов. Например, вместо определения класса типов равенства:
class Eq a where
(==) :: a -> a -> Bool
И использование его в других функциях для указания аргумента типа должно быть экземпляром Eq
:
elem :: (Eq a) => a -> [a] -> Bool
Разве мы не можем просто определить нашу elem
функцию без использования класса типов и вместо этого передать аргумент функции, который выполняет эту работу?
Monad m
Ограничение говорит для меня больше , чем передачи дополнительных аргументов функции типовa -> m a
иm a -> (a -> m b) -> m b
.TypeApplications
Расширение позволяет сделать неявный аргумент явным.(==) @Int 3 5
сравнивает3
и5
конкретно какInt
ценности. Вы можете думать о нем@Int
как о ключе в словаре функций равенства для конкретных типов, а не самойInt
функции сравнения.Ответы:
Да. Это называется "стиль передачи словаря". Иногда, когда я делаю какие-то особенно сложные вещи, мне нужно отбросить класс типов и превратить его в словарь, потому что передача словаря является более мощной 1 , но часто довольно громоздкой, что делает концептуально простой код выглядящим довольно сложным. Иногда я использую стиль передачи словаря в языках, которые не являются Haskell, для имитации классов типов (но я узнал, что это обычно не такая хорошая идея, как кажется).
Конечно, всякий раз, когда есть разница в выразительной силе, есть компромисс. Хотя вы можете использовать данный API-интерфейс несколькими способами, если он написан с использованием DPS, API-интерфейс получает больше информации, если вы не можете. На практике это проявляется в
Data.Set
том, что дляOrd
каждого типа существует только один словарь. ВSet
магазинах его элементы сортируются в соответствии сOrd
, и если вы строите набор с одного словаря, а затем вставить элемент , используя другую, как это было бы возможно с DPS, вы можете разбитьSet
«s инвариантно и привести к аварии. Эта проблема уникальности может быть смягчена с помощью фантомного экзистенциальноготип, чтобы отметить словарь, но, опять же, ценой довольно немного раздражающей сложности в API. Это также проявляется почти таким же образом вTypeable
API.Бит уникальности появляется не очень часто. Классы типов хороши в написании кода для вас. Например,
который берет два «процессора», которые принимают входные данные и могут выдавать выходные данные, и объединяют их, сглаживая их
Nothing
, должны быть записаны в DPS примерно так:По сути, нам снова пришлось прописать тип, в котором мы его используем, даже если мы уже прописали его в сигнатуре типа, и даже это было избыточно, потому что компилятор уже знает все типы. Поскольку существует только один способ создать данное значение
Semigroup
для типа, компилятор может сделать это за вас. Это имеет эффект типа «сложный интерес», когда вы начинаете определять множество параметрических экземпляров и использовать структуру ваших типов для вычислений для вас, как вData.Functor.*
комбинаторах, и это используется дляderiving via
достижения большого эффекта, когда вы можете получить практически все «стандартная» алгебраическая структура вашего типа написана для вас.И даже не заводите меня на MPTC и fundeps, которые возвращают информацию обратно к проверке типов и выводам. Я никогда не пытался преобразовать такую вещь в DPS - я подозреваю, что это потребовало бы передачи большого количества доказательств равенства типов - но в любом случае я уверен, что это будет намного больше работы для моего мозга, чем мне было бы удобно с.
-
1 U nless использовать
reflection
в этом случае они становятся эквивалентными в силе - ноreflection
также может быть обременительным для использования.источник
Да. Это (называется передачей словаря) - это то, что компилятор делает с классами типов в любом случае. Для этой функции, выполненной буквально, это будет выглядеть примерно так:
Вызов
elemBy (==) x xs
теперь эквивалентенelem x xs
. И в этом конкретном случае вы можете пойти еще дальше:eq
каждый раз иметь один и тот же первый аргумент, так что вы можете взять на себя ответственность вызывающего абонента за применение этого, и в итоге получите следующее:Вызов
elemBy2 (x ==) xs
теперь эквивалентенelem x xs
.... Ой, подожди. Это просто
any
. (А на самом деле в стандартной библиотекеelem = any . (==)
.)источник
implicit
и компилятор вставит их для вас из области видимости.