Что вы использовали для более ранних типов в Haskell?
Более высокие классы рангов выглядят очень весело. Из Haskell wikibook приведен этот пример:
foo :: (forall a. a -> a) -> (Char,Bool)
foo f = (f 'c', f True)
Теперь мы можем оценить foo id
без взрыва компилятора. Этот пример быстро следует в книге примером реального мира, который я видел в нескольких других местах: ST monad и runST
. Это довольно круто.
Но мне еще предстоит встретить ситуацию, когда я решаю проблему, написав свою собственную функцию с аргументом более высокого ранга. У вас есть? Какие примеры у вас в полиморфизме ранга 2 или ранга-n в дикой природе?
Ответы
Ответ 1
Посмотрите на функции типа withRepoLock
в источнике Darcs.
Darcs поддерживает несколько форматов репозитория, и эта поддержка выражается через класс типов. Таким образом, вы можете написать код, который является общим для форматов репозитория. Когда вы действительно читаете репозиторий на диске, который хотите отправить в этот код через какой-то общий код, который определяет формат, в котором находится репозиторий, и выбирает правильную копию экземпляра типа.
Ответ 2
Вейрих и Уошберн "Коробки идут бананы"! (paper, слайды)
Вот очень грубое и, вероятно, немного неточное объяснение того, что все это значит: учитывая индуктивный тип, BGB позволяет вам представлять пространство функций из этого типа, которые являются "положительными" - они никогда не различаются по своим аргументам, В большинстве случаев они включают их аргументы как часть других значений (обычно одного типа).
Weirich + Washburn использовать это, чтобы получить возможное адекватное представление HOAS лямбда-исчисления в -XRankNTypes
Haskell (кто-нибудь еще доказал его адекватность?).
Я использую его здесь (предупреждение: messy code), чтобы включить
(forall g . GArrow g => g () x -> g () y)
в
(forall g . GArrow g => g x y)
Это работает, потому что полиморфный тип ранга 2 не может "проверять" структуру своего аргумента - все, что он может сделать, это "вставить" этот аргумент в более крупные структуры. Некоторые обманщики позволяют мне выяснить, где происходит вставка, а затем я вставляю точки вставки (если есть) обратно на вкладку GArrow
.
Вы не можете сделать это с помощью класса Control.Arrow
, потому что все функциональное пространство Haskell "просачивается" в него через arr
.
Ответ 3
Недавно кто-то задал вопрос здесь о переполнении стека, который можно было решить с помощью более ранних типов.
Другое приложение находится в документе Scrap Your Boilerplate.
Ответ 4
Возможно, вы столкнулись с проблемами, когда более ранжированные типы были бы полезны, но не смогли это реализовать. Например, в примере Darcs они могли легко реализовать его без более ранжированных типов. Вместо этого были бы предпосылки для некоторых функций, которые вызывающий должен был бы удостовериться в том, что они соблюдают такие правила, как выбор правильного экземпляра функции для формата репозитория.
Преимущество более ранжированного типа заключается в том, что он переносит ответственность за получение этого права от программиста до компилятора. При использовании обычного подхода, если разработчик Darcs допустил ошибку с типом репозитория, результатом будет либо ошибка времени выполнения, либо поврежденные данные. С более высоким ранжированным типом разработчик получает ошибку типа во время компиляции.